在GCC中使用{0}初始化时出现奇怪的段错误

时间:2014-01-15 09:14:15

标签: c gcc segmentation-fault

以下是代码:

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);

7 个答案:

答案 0 :(得分:5)

让我们看看:

void **p = &s;

在那里,p指向s,也就是说,它包含s的第一个字节的地址。

它是指向指针的指针,因此*p被读作指针,它将使用s的前n个字节,n等于sizeof(void*)(4或8)。

然后你使用指针*p来读取内存字节......

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

  • 如果数组是未初始化的:垃圾,VC ++中的那个看起来像是指向who-know-where的真实指针。你读了一些随机字节。
  • 如果数组初始化为{0],它们将全为零,因此*p将是NULL指针和段错误。

答案 1 :(得分:2)

您的表达式void **p = &s;相当于void **p = &s[0];。现在,后续的*p会向您s[0]提供((char*)(*p))[i] s[0][i]*(((char*)0)+i)*(char*)NULL,初始化后将等同于{{1}},或者,在第一次迭代时,{{1}} 1}}。

答案 2 :(得分:2)

void **p = &s;将数组指针强制转换为指针指向void的指针是没有理由的。没有理由使用void**

所以代码没有任何意义,这就是它实际做的事情:

void **p = &s;

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

(char*)(*p)

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

(char*)(*p)[i]

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

由于未定义的行为意味着任何可能发生,程序可能会崩溃或程序似乎正常工作。分析调用未定义行为时获得某个程序行为的原因没有意义。只需接受您的程序包含执行时可能会或可能不会检测到的错误,具体取决于编译器和系统。

答案 3 :(得分:1)

所以,正如你所说,

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

的段错误。

你做的是

  • 声明void **指向原始char[]
  • 通过连续投射使用void **

我不知道它是否会受到伤害,但它不会太复杂,导致错误。

让我们仔细看看:

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

这是错误的。

除非您尝试某些内容,否则我建议

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

如果您想了解原始案例中发生的情况,我建议将其分解为部分:

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),在原始情况下,数据未初始化并且运气包含有效指针(在堆栈上,这是可能会发生)。不过未定义。

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

作为

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

丢失重要的类型信息,通过第一次,然后在之后取消引用来应用它,如下所示:

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

答案 6 :(得分:0)

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

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

简而言之:

如果您将数组声明为 静态 char array[N]数组&amp;数组有相同的地址;

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