C:为什么C需要字符的内存地址才能将其转换为int?

C:为什么C需要字符的内存地址才能将其转换为int?,c,pointers,type-conversion,pass-by-reference,memory-address,C,Pointers,Type Conversion,Pass By Reference,Memory Address,来自Python,在Python中,我只需使用类型来找出对象的类型,而C缺乏内省,这迫使我在进入更高级的主题之前更好地掌握它的数据类型、它们的关联性和指针。这是一件好事。因此,我有以下代码,我将以各种方式对其进行调整,并尝试了解结果行为: int main(int argc, char *argv[]) { int i = 0; for(i = 0; argv[1][i] != '\0'; i++) { printf("%d\n", argv[1][i]);

来自Python,在Python中,我只需使用类型来找出对象的类型,而C缺乏内省,这迫使我在进入更高级的主题之前更好地掌握它的数据类型、它们的关联性和指针。这是一件好事。因此,我有以下代码,我将以各种方式对其进行调整,并尝试了解结果行为:

int main(int argc, char *argv[])
{
    int i = 0;
    for(i = 0; argv[1][i] != '\0'; i++) {
         printf("%d\n", argv[1][i]);
         char letter = argv[1][i];

    switch(letter) {
        case 2:
            printf("%d: 2\n", i);
            break;
如果我运行它并将数字2作为单个参数传递,则不会发生任何事情。我的理解是,因为我已经将argv[I]定义为char,将其与2比较,int将返回false,因此代码不会被调用。事实上,如果我将case从2改为'2',代码确实会被调用。有道理,但这让我想到了我的第一个问题:

问题1。我在不同的地方读到,对于C来说,字符和整数本质上是一样的。那么为什么C不知道作为参数传递的2应该被解释为整数而不是字符串呢?毕竟,C允许我,例如,对字符使用%d字符串格式化程序

如果我随后将变量字母的类型从char更改为int:

int letter = argv[1][i];
。。。我仍然得到与第一个变体相同的行为,即没有发生任何事情,尽管现在我显然在比较case语句中的int和int。这让我猜测,尽管我现在将字母定义为int,但C仍然在命令行上以字符的形式读取它,仅仅将其称为int不足以从后续程序流的角度改变它的类型

问题2。上述推理正确吗

所以现在我想,如果我使用atoi将字母的类型更改为int,事情应该会顺利进行。因此:

int letter = atoi(argv[1][i]);
当我现在尝试编译时,我得到:

Switch.c:14:27: warning: incompatible integer to pointer conversion passing
      'char' to parameter of type 'const char *'; take the address with &
      [-Wint-conversion]
        int letter = atoi(argv[1][i]);
                          ^~~~~~~~~~
                          &
/usr/include/stdlib.h:132:23: note: passing argument to parameter here
int      atoi(const char *);
然后我查阅了atoi的文档,发现它只能用于将字符串转换为更精确的常量字符*,而不是字符。我想既然char*只是一个char序列,那么atoi可以同时处理这两个字符。显然,对于一个char来说,没有与atoi等价的方法,而只是一些变通方法,比如上面描述的方法

无论如何,我决定接受警告的指示,并在值之前放置一个符号,知道这意味着一个内存地址,但还不知道为什么建议这样做。因此:

int letter = atoi(&argv[1][i]);
当我这样做时,它会编译。现在,这个最终形式的程序——字母定义为int,case语句与int比较,atoi被传递地址而不是argv[1][i]的值——成功运行

但我不知道为什么,所以我把它去掉,通过打印来测试argv[1][I]和&argv[1][I]的值。我注意到,仅当我使用%s字符串格式化程序打印&argv[1][I]时,程序才会编译,因为它告诉我&argv[1][I]是字符*

第三季度。为什么&argv[1][i]是内存中的地址,是字符*

在我的打印输出中,我观察到&argv[1][I]和argv[1][I]的值是相同的,即:2。因此:

第四季度。如果argv[1][i]的值与&argv[1][i]的值相同,为什么编译器不允许我使用argv[1][i]

问题5。每当我在C程序中打印内存地址时,它总是一些长的数字,比如1246377222。为什么内存地址==在这种情况下的值

毫无疑问,会有人反对将这篇庞大的文章分成不同的文章,并提出不同的问题,但我认为尝试和错误的流程,以及你提供的答案,不仅会帮助我,也会帮助其他希望了解C的这些方面的人。也可以自由地为这篇文章建议一个更好的标题


非常感谢。

您的误解是2!='2'. 它们都有积分值,但这些值彼此不同。“2”的ascii值不等于2,而是等于50。这意味着int a='2';使a计算为50。将整型字符转换为数值的标准方法是写入int a='2'-'0';这将导致a计算为2


argv是char*的数组。这意味着argv[j][i]是一个字符,&argv[j][i]是一个字符*它是位于argv[j][i]位置的字符的地址。这意味着atoi和argv[j][i]将进行编译,但我不确定它是否达到了预期效果,因为它将尝试将从argv[j][i]开始的整个字符串转换为一个数字,而不仅仅是argv[j][i]处的特定字符。

C中的字符串是带有空终止符的字符序列。它总是由第一个字符的地址引用,因为它的长度是可变的。

Q1:char和int只有在两者都是有符号的意义上是相同的,通常是整数。有多种不同,例如char通常为1字节长,而int通常至少为4字节长

问题2:您的推理是错误的,因为您将字母“2”的ASCII代码(50)与没有ASCII视觉表示的数字2进行比较

你制造了一团雾 调试中的ake-&argv[1][i]是argv[1]字符串中第i个字符的地址。所以本质上这是一个指针。在调试器中,您可能看到了指向的字符

问题4:请参见上文-argv[1][i]是“2”,而&argv[1][i]是内存中可以找到此“2”的地址

问题5:您可能在调试过程中犯了一个错误-请参阅问题3的答案

第四季度。如果argv[1][i]的值与&argv[1][i]的值相同,为什么编译器不允许我使用argv[1][i]

他们是不同的

&argv[1][i]是指向内存位置的指针,并且

argv[1][i]是该位置的字符值

只是

printf("%s", &argv[1][i]); // Prints the c-string at memory position
printf("%c", argv[1][i]); // Prints the char

我假设当你说printf时,你指的是printf函数。

对于Q5,正如你说你来自Python,在C Python中实现的id是变量的地址。即使在Python中,数值变量的id也不是变量的值。

您要求的是C编程类的整个介绍部分。一个问题有很多答案。atoi的存在是有原因的——是的,如果只允许我回答其中一个问题,那就是Q5。我请你给我点启示。非常感谢。你一次只能问一个问题。我试图回答标题中的问题。这里有很多好问题!请注意,char可以是有符号的,也可以是无符号的。好吧,但我知道为什么它总是要被address引用,因为它的长度是可变的?你能解释一下为什么一个需要另一个吗?在我的示例中,argv[1][i]是一个字符,而不是一个字符串。int、chars和float都可以放入寄存器中,因此可以独立于内存中的某个位置存在,并且通常可以使用单个cpu指令移动。这并不意味着该语言不能有值引用的字符串类型,但C倾向于与硬件保持接近。对,argv[1][i]是一个字符,这就是为什么您不能将其传递给atoi。对,显然我还没有掌握指针的概念,您的回答可能有助于澄清问题。内存位置如何同时保存c字符串和字符?内存位置保存字符。但c字符串是指向以'/0'null限定符结尾的字符数组的指针。在这种情况下没有区别,因为字符串只有一个字符长。