【问题标题】:How does the C program generate such an output?C 程序如何生成这样的输出?
【发布时间】:2009-09-30 12:15:40
【问题描述】:
void main()
{
    char str[2][7] = {"1234567", "abcdefg"};
    char** p = str;
    printf("%d\n", *(p+1));
    printf("%c\n", *(p+1));
}

输出是:

1631008309
5

编辑:谢谢。我看到'5'只有0x35,而不是我应该的str [0] [4]。为什么我不能离开 str[0][4] 而不是这个奇怪的 1631008309?

天哪,我太傻了才问这个问题!谢谢大家。

【问题讨论】:

  • 你期待什么?
  • 1631008309 == 0x61373635 ; 0x61 0x37 0x36 0x35 ='a''7''6''5'
  • 当然是!我从来没有这样想过。
  • 因为str+1*(str+1)是同一个值,所以犹豫了。
  • 嘿,你能解释一下这里发生了什么吗? 1631008309 == 0x61373635 怎么样? 5 == 0x35 怎么样?

标签: c pointers


【解决方案1】:

您将 char** 指向分配给二维数组的内存的开头。

当你给一个指针加 1 时,它会移动你所指向的类型的大小,在本例中是一个 char * 的大小,在你的系统中显然是 4 个字节。然后你取消引用它并将结果视为一个 int (%d),这会给你我在评论中提到的 a765 来放松。当您取消引用它并将其视为 char 时,您正确得到 5。

[我看到 unwind 删除了他们的答案,所以为了完整起见,我要补充一点,“a765”是你得到的较大数字 (0x61373635) 的 ASCII 解释。]

【讨论】:

    【解决方案2】:

    这一行:

    char** p = str;
    

    没有意义。 str 是一个由 2 个 7 个字符组成的数组。在表达式上下文中使用时,它的计算结果是指向其第一个元素的指针,相当于:

    &str[0]
    

    类型为“指向 7 个字符数组的指针”。这不可能类似于p 的类型“指向char 的指针”。任何这些都有意义:

    char *p = str[0];
    
    char *p = str[1];
    
    char (*p)[7] = str;
    

    【讨论】:

    • 是的,我想我把它读作“为什么程序生成这个输出而不是我期望的输出”。
    【解决方案3】:

    使用 %d 不会将字母数字的 ASCII 字符串转换为数字。使用 %d 意味着它期望找到的值将被解释为数字的二进制表示并转换为 ASCII 以进行打印。

    【讨论】:

      【解决方案4】:

      请注意,C 标准定义main() 程序返回int,而不是void

      给定代码:

      #include <stdio.h>
      int main()
      {
          char str[2][7] = {"1234567", "abcdefg"};
          char** p = str;
          printf("%d\n", *(p+1));
          printf("%c\n", *(p+1));
          return(0);
      }
      

      使用gcc -o x -Wall x.c 编译时,您会收到警告:

      x.c: In function ‘main’:
      x.c:5: warning: initialization from incompatible pointer type
      x.c:6: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘char *’
      x.c:7: warning: format ‘%c’ expects type ‘int’, but argument 2 has type ‘char *’
      

      (如果省略-Wall,则不会收到格式警告。)

      这告诉您您正在传递一个指向printf() 的指针。打印指针不会那么有用。但是,前面的警告还告诉您,您错误地初始化了变量p。然而,最终结果是p 指向str 中空间的开始。打印时

      一个有趣的小怪癖:通常,诸如“1234567”之类的字符串包含终止NUL '\0';在您的数组中,因为您指定数组的长度是 7,而不是 8,所以缺少终止的空值。小心你如何打印字符串!

      这是代码的另一个变体:

      #include <stdio.h>
      int main()
      {
          char str[2][7] = {"1234567", "abcdefg"};
          char** p = str;
          printf("%d\n", *(p+1));
          printf("%c\n", *(p+1));
          printf("%p %p %p -- %p %p %p\n", str, &str[0], &str[0][0], p, p+1, *(p+1));
          printf("%.4s\n", (p+1));
          return(0);
      }
      

      这给了我以下输出(来自 Mac):

      1631008309
      5
      0xbffffa5e 0xbffffa5e 0xbffffa5e -- 0xbffffa5e 0xbffffa62 0x61373635
      567a
      

      请注意,地址 str、&str[0] 和 &str[0][0] 都具有相同的值,并且p+1 是四个字节。当被视为字符串时,它会打印第一个初始化程序的最后三个字节和第二个初始化程序的第一个字节。

      另外,为了好玩,用gcc -m64 -o x64 x.c编译,输出为:

      1701077858
      b
      0x7fff5fbff9e0 0x7fff5fbff9e0 0x7fff5fbff9e0 -- 0x7fff5fbff9e0 0x7fff5fbff9e8 0x676665646362
      bcde
      

      【讨论】:

        【解决方案5】:

        我理解你的困惑,这两种说法是完全一样的。

        两个语句都将 *(p + 1) 转换为指向整数的指针,然后再取消引用它,默认情况下将所有内容都转换为 va_arg 参数中的整数。

        printf 然后将整数截断为 char,然后在第二个 printf 语句中打印它。

        这段代码 sn-p 更清楚地说明了发生了什么:

        void main()
        {
          char str[2][7] = {"1234567", "abcdefg"};
          char** p = str;
          printf("%d\n", *(p+1));
          printf("%c\n", *(p+1));
          int n = *(p+1);
          char c = *(p+1);
          printf("0x%08X\n", n);
          printf("0x%08X\n", c);
        }
        

        哪些输出:

        1631008309
        5
        0x61373635
        0x00000035
        

        这里解释了 stdarg 中的类型安全:stdarg

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-10-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-22
          相关资源
          最近更新 更多