这是代码:
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);
让我们看看:
void **p = &s;
在那里,p
指向s
,也就是说,它包含s
的第一个字节的地址。
它是一个指针到指针,所以*p
被读作指针,它将使用s
的前n个字节,n等于sizeof(void*)
(4或8)。
然后你正在使用该指针*p
读取内存字节......
现在,s
的内存中有哪些字节将被读取为内存地址?
- 如果数组未初始化:垃圾,则在VC++中恰好看起来像一个指向谁知道在哪里的真实指针。您读取了一些随机字节。
- 如果数组初始化为
{0]
,它们将全部为零,因此*p
将是 NULL 指针和段错误。
表达式void **p = &s;
等效于void **p = &s[0];
。现在后续*p
给你s[0]
,之后((char*)(*p))[i]
s[0][i]
,初始化后相当于*(((char*)0)+i)
,或者在第一次迭代时,*(char*)NULL
。
void **p = &s;
没有合理的理由将数组指针转换为指针到指针到空。根本没有理由使用void**
。
所以代码没有任何意义,这就是它的实际作用:
void **p = &s;
你告诉程序数组的地址应该存储在指针变量中。该指针变量假定给定的地址依次指向另一个有效地址。这是不正确的。
(char*)(*p)
在这里,您获取指针到指针的内容并将其视为地址。但指针的内容是数组的实际数据。您正在调用未定义的行为。
(char*)(*p)[i]
在这里,您获取任何随机垃圾地址并将其视为数组。这也是未定义的行为。
由于未定义的行为意味着任何事情都可能发生,程序可能会崩溃,或者程序似乎可以正常工作。分析为什么在调用未定义的行为时会得到某种程序行为是没有意义的。只需接受您的程序包含您在执行时可能会或可能不会检测到的错误,具体取决于编译器和系统。
所以,正如你所说,
char s[8] = {0};
int i = 0;
void **p = &s;
for (; i < 8; ++i) {
putchar( ((char*)(*p))[i] );
}
段错误。
你所做的是
- 声明指向原始
char[]
的void **
- 通过连续转换来使用该
void **
。
我不知道它是否受伤,但它是不必要的复杂,导致错误。
让我们仔细看看:
你做((char*)(*p))[i]
,即你取消引用p
- 它指向你的数组 - 获取存储在那里的值作为指针。再次取消引用以获取字符。
这是错误的。
除非你正在尝试一些东西,否则我建议
for (; i < 8; ++i) {
putchar(s[i]);
}
如果你想了解在原始案例中会发生什么,我进一步建议将 ti 分解为几个部分:
char s[8] = {0};
int i = 0;
void **p = &s;
char * base = *p;
printf("&s: %pn", &s);
printf("p: %pn", p);
printf("*p: %pn", *p); // be aware that even this might be undefined...
printf("base: %pn", base);
for (; i < 8; ++i) {
putchar( base[i] ); // will lead to crash.
}
获取随机字节并将其视为指针是未定义的行为。
这里可能发生任何事情。在这种情况下,字节由所有 NUL 字节组成(s
在其整个长度上初始化为 0),在原始情况下,数据未初始化并且幸运地包含一个有效的指针(在堆栈上,这很可能会发生)。然而,未定义。
太多*
:p
保存数组的地址。*p
取消引用该指针,并将数组内容解释为void *
。 此指针包含垃圾,包括用于初始化的0
。 所以你必须放弃一个级别的*
:
void *p = &s;
for (; i < 8; ++i) {
putchar( ((char*)(p))[i] );
}
as
void ** p = &s; /* p points to an array of 8 char. */
丢失重要的类型信息,首先通过强制转换将其应用回来,然后在之后取消引用,如下所示:
putchar( (*((char(*)[8]) p))[i] );
正如评论中所解释的那样,这就是为什么你有sigfault:
为什么数组的地址等于它在 C 中的值?
总之:
如果将数组声明为静态,则char array[N]
、数组和&array具有相同的地址;
如果将数组声明为指针,则char * array
、数组和&array具有不同的地址;