为什么在C语言中char*和char**被视为相同类型?

5

我有以下测试应用程序:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int main(void){
    char buf[512];
    buf[0]= 0x1;
    buf[1]= 0x2;
    char *temp1 = &buf;
    char *temp2 = buf;
    char *temp3 = &buf[0];
    printf("temp1:%p, temp2:%p, temp3:%p\n",temp1,temp2,temp3);
    printf("0 = %d, %d, %d\n",temp1[0],temp2[0],temp3[0]);
    printf("1 = %d, %d, %d\n",temp1[1],temp2[1],temp3[1]);
    return;
}

编译时有一个警告:

gcc ./testptr.c -o testptr
./testptr.c: In function ‘main’:
./testptr.c:9: warning: initialization from incompatible pointer type

但当我运行它时,所有三个指针的行为都相同。

./testptr
temp1:0x7fff3a85f220, temp2:0x7fff3a85f220, temp3:0x7fff3a85f220
0 = 1, 1, 1
1 = 2, 2, 2

我知道buf == &buf [0],但是为什么&buf == &buf[0]&buf不应该是char **吗?

1
"&buf" 实际上是一个 "char(*)[512]"(指向一个包含 512 个 char 元素的数组)。它们并不完全相同。 - Jeff Mercado
你在从编译器输出内容复制粘贴代码到问题描述时更改了代码。现在问题出在第9行。 - Jonathan Leffler
抱歉,你是对的,我刚刚添加了一行新代码。 - austinmarton
一个几乎相同的问题已经在这里得到了回答:https://dev59.com/I2w15IYBdhLWcg3wqNgA - Divij
@Divij 谢谢,那里也有很好的答案,我在搜索中没有找到。 - austinmarton
4个回答

3
所有指针的行为都相同,因为您将它们全部声明为char*。C是静态类型的,因此类型绑定到变量而不是值。
现在,行为部分已经解释清楚了,我们只需要找出它们为什么实际上具有相同的值(根据%p printf)。嗯,这只是指针作为内存地址由GCC实现的一个产物(使*与**区别的偏移量和大小都由类型系统/编译器在幕后处理)。请注意,像任何最可疑的会发出警告的东西一样,这很可能是未定义的行为或至少是一种不好的做法:)

2

数组不是指针,虽然它们可以以类似的方式使用。你恰好发现了数组和指针语义不同的一个方面。


当我无意中将&buf而不是buf作为类型为void *的第二个参数传递给recv函数时,我发现了这个问题。即使上面的示例没有说明,这是否可能会导致问题? - austinmarton
@austinmarton:当涉及到C编译器警告时,我更倾向于偏执狂。 - hugomg
@missingno:说得对。在我的原始应用程序中,错误被传递为void指针,因此没有警告被生成。 - austinmarton

1

你可以从 *& 运算符的代数中解决它。

  • 我们知道 bufbuf 数组的第 0 个元素的地址

  • 我们知道 &buf[0] 也是第 0 个元素的地址

  • 根据定义,buf[0] 等同于 *(buf+0)

  • &(*(a)) 等同于 a

因此,&buf[0] 变成了 &(*(buf+0)),即 buf

更新

在这里,让我们将其作为证明陈述出来。

  1. &buf[0] 给定。
  2. &(buf[0]) 根据 C 优先级规则加上括号。
  3. &((*(buf+0))) 因为 buf[0] == *(buf+0)
  4. &(*(buf+0)) 消除多余的括号。
  5. buf 得证。

3
我同意你说的(buf == &buf[0]),但那不完全是我想问的(&buf == buf?)。 - austinmarton
@austinmarton,请按照以下步骤操作:它们证明了&buf等同于buf - Charlie Martin
1
@Charlie:不,证明表明&buf[0]等同于buf,而不是问题所问的&buf等同于buf - icktoofay

1

如果你思考编译器在处理数组时实际生成的代码,就会更加清晰。名称buf引用了数组的第一个(零号)元素的地址(用于包含字符的连续空间)。如果你查看对象中符号表条目"buf"的入口,你会找到那个第一个元素的地址。当你引用buf[0]时,编译器会生成buf的地址,再加上char类型大小的零倍。这恰好与buf本身的地址相同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接