【问题标题】:Strange segment fault when initialize using {0} in GCC在 GCC 中使用 {0} 初始化时出现奇怪的段错误
【发布时间】:2014-01-15 09:14:14
【问题描述】:

代码如下:

char s[8];
int i = 0;
void **p = &s;
for (; i < 8; ++i) {
    putchar( ((char*)(*p))[i] );
}

上面的代码可以工作,但给出了一些垃圾字符。所以我所做的只是初始化s[8]:

char s[8] = {0};

然后是分段错误,但是如果我使用VC++编译和运行它,它工作正常。奇怪的。有人能解释一下吗?谢谢!

更新: 很多人说上面的代码很愚蠢......这个更新是给你的。什么原创 代码如下:

static void* 
copy_and_move(void **dst, int dsz, const void **src, int ssz) {
      const int sz = ssz > dsz ? dsz : ssz;      
      memcpy(*dst, *src, sz);
      return *dst + sz;
}

然后调用代码:

char d[10], s[8];
copy_and_move(&d, sizeof(char) * 10, &s, sizeof(char) * 8);

【问题讨论】:

  • 你为什么要做((char*)(*p))[i] 而不是简单的s[i]
  • 您的* 密钥是否粘滞?为什么要将字符投射到char *?为什么要将指针声明为指向指针的指针?
  • 我一直对那些写出这样表达方式的人感到惊讶,但当他们转身咬在臀部时却不知何故感到惊讶:-)
  • 我简化了原始代码,所以,你看到了上面的愚蠢代码。你们都成功地错过了重点。
  • @user1786323 你应该阅读this。简而言之:s == &amp;s.

标签: c gcc segmentation-fault


【解决方案1】:

让我们看看:

void **p = &s;

这里,p指向s,即包含s第一个字节的地址。

而且它是一个指针指向指针,所以*p被读取为一个指针,它将使用s的前n个字节,n等于sizeof(void*)(4或8)。

然后您正在使用该指针 *p 来读取内存字节...

现在,s 的内存中的哪些字节将被读取为内存地址?

  • 如果数组未初始化:垃圾,在 VC++ 中,它恰好看起来像一个真正的指向谁知道在哪里的指针。您读取了一些随机字节。
  • 如果数组初始化为{0],它们将全为零,因此*p 将是一个空指针和段错误。

【讨论】:

    【解决方案2】:

    您的表达式void **p = &amp;s; 等价于void **p = &amp;s[0];。现在后续的*p 给你s[0],之后((char*)(*p))[i]s[0][i],在初始化之后相当于*(((char*)0)+i),或者,在第一次迭代时,*(char*)NULL

    【讨论】:

    • 您提出的第一点。为什么你不先尝试就可以自信地说出来??
    • @user1786323 printf("&amp;s=%x\n",&amp;s);printf("&amp;s[0]=%x\n",&amp;s[0]); 给了我身份地址。为什么说我不知道​​就先尝试了?
    • @user1786323 &amp;s 等价于s 等价于&amp;s[0]。这是因为数组“衰减”成指针的方式。
    • @Ruslan 在这种情况下是的,因为数组是静态的。如果s 的声明为char * s,则不等价。
    【解决方案3】:

    void **p = &amp;s; 没有合理的理由将数组指针转换为指向空指针的指针。根本没有理由使用void**

    所以代码没有任何意义,这就是它的实际作用:

    void **p = &s;
    

    你告诉程序一个数组的地址应该存储在一个指针变量中。该指针变量假定给定的地址又指向另一个有效地址。这是不正确的。

    (char*)(*p)
    

    在这里,您获取指针的内容并将其视为地址。但是指针的内容是数组的实际数据。您正在调用未定义的行为。

    (char*)(*p)[i]
    

    在这里,您正在获取任何随机垃圾地址并将其视为一个数组。这也是未定义的行为。

    而且由于未定义的行为意味着任何事情都可能发生,因此程序可能会崩溃或程序似乎可以正常工作。当你调用未定义的行为时,分析为什么会得到某种程序行为是没有意义的。只需接受您的程序包含您在执行时可能会或可能不会检测到的错误,具体取决于编译器和系统。

    【讨论】:

      【解决方案4】:

      如你所说,

      char s[8] = {0};
      int i = 0;
      void **p = &s;
      for (; i < 8; ++i) {
          putchar( ((char*)(*p))[i] );
      }
      

      段错误。

      你所做的是

      • 声明一个void ** 指向你原来的char[]
      • 通过持续施放 void ** 来使用它。

      我不知道它是否会受伤,但它过于复杂,会导致错误。

      让我们仔细看看:

      你做((char*)(*p))[i],我。 e.您取消引用 p - 指向您的数组 - 将存储在那里的值作为指针获取。再次取消引用以获取字符。

      这是错误的。

      除非你正在尝试什么,否则我建议

      for (; i < 8; ++i) {
          putchar(s[i]);
      }
      

      如果您想了解原始案例中发生的情况,我进一步建议将 ti 分解为多个部分:

      char s[8] = {0};
      int i = 0;
      void **p = &s;
      char * base = *p;
      
      printf("&s: %p\n", &s);
      printf("p: %p\n", p);
      printf("*p: %p\n", *p); // be aware that even this might be undefined...
      printf("base: %p\n", base);
      for (; i < 8; ++i) {
          putchar( base[i] ); // will lead to crash.
      }
      

      获取随机的字节串并将它们视为指针是未定义的行为。

      这里可能发生任何事情。在这种情况下,字节由所有 NUL 字节组成(s 在其长度上被初始化为 0),在您的原始情况下,数据未初始化并且幸运地包含一个有效指针(在堆栈上,这很可能发生)。然而未定义。

      【讨论】:

        【解决方案5】:

        * 太多:p 持有数组的地址。 *p 取消引用该指针,并将数组内容解释为 void *。该指针包含垃圾,包括您用于初始化的0。所以你必须降一级*

        void *p = &s;
        for (; i < 8; ++i) {
            putchar( ((char*)(p))[i] );
        }
        

        【讨论】:

        • 仍然没有解释我问了什么。
        【解决方案6】:

        作为

        void ** p = &s; /* p points to an array of 8 char. */
        

        丢失了重要的类型信息,通过转换 first 将其应用回来,然后取消引用 after,如下所示:

        putchar( (*((char(*)[8]) p))[i] );
        

        【讨论】:

          【解决方案7】:

          正如评论中所解释的,这就是您有 sigfault 的原因:

          How come an array's address is equal to its value in C?

          简而言之:

          如果将数组声明为staticchar array[N]array&array 具有相同的地址;

          如果将数组声明为指针char * arrayarray&array 的地址不同;

          【讨论】:

            猜你喜欢
            • 2021-04-27
            • 2017-11-15
            • 1970-01-01
            • 2011-04-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多