55

我试图用指针来理解一些东西,所以我写了这段代码:

#include <stdio.h>

int main(void)
{
    char s[] = "asd";
    char **p = &s;

    printf("The value of s is: %p\n", s);
    printf("The direction of s is: %p\n", &s);

    printf("The value of p is: %p\n", p);
    printf("The direction of p is: %p\n", &p);

    printf("The direction of s[0] is: %p\n", &s[0]);
    printf("The direction of s[1] is: %p\n", &s[1]);
    printf("The direction of s[2] is: %p\n", &s[2]);

    return 0;
}

使用 gcc 编译时,我收到以下警告:

$ gcc main.c -o main-bin -ansi -pedantic -Wall -lm
main.c: In function ‘main’:
main.c:6: warning: initialization from incompatible pointer type
main.c:9: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char (*)[4]’
main.c:11: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char **’
main.c:12: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char ***’

(gcc 的标志是因​​为我必须是 C89)

为什么指针类型不兼容?数组的名称不是指向它的第一个元素的指针吗?因此,如果 s 是指向“a”的指针,则&s必须是 a char **,不是吗?为什么我会收到其他警告?我是否必须使用 ( void *) 转换指针才能打印它们?

运行时我得到这样的东西:

$ ./main-bin
The value of s is: 0xbfb7c860
The direction of s is: 0xbfb7c860
The value of p is: 0xbfb7c860
The direction of p is: 0xbfb7c85c
The direction of s[0] is: 0xbfb7c860
The direction of s[1] is: 0xbfb7c861
The direction of s[2] is: 0xbfb7c862

s 的值和它的方向(当然还有 的值p)怎么能一样呢?

4

8 回答 8

36

“s”不是“char*”,而是“char[4]”。因此,“&s”不是“char**”,而是“指向 4 个字符数组的指针”。您的编译器可能会将“&s”视为您编写了“&s[0]”,这大致相同,但它是一个“char*”。

当你写“char** p = &s;” 您试图说“我希望将 p 设置为当前指向“asd”的事物的地址。但目前没有任何内容指向“asd”。只有一个包含“asd”的数组;

char s[] = "asd";
char *p = &s[0];  // alternately you could use the shorthand char*p = s;
char **pp = &p;
于 2008-10-13T14:44:10.723 回答
21

是的,您的编译器期望 void *。只需将它们转换为 void *.

/* for instance... */
printf("The value of s is: %p\n", (void *) s);
printf("The direction of s is: %p\n", (void *) &s);
于 2008-10-13T14:31:38.973 回答
4

如果将数组的名称作为参数传递给函数,则将其视为已传递数组的地址。所以 &s 和 s 是相同的参数。参见 K&R 5.3。&s[0] 与 &s 相同,因为它获取数组的第一个元素的地址,这与获取数组本身的地址相同。

对于所有其他指针,尽管所有指针本质上都是内存位置,但它们仍然是类型化的,编译器会警告将一种类型的指针分配给另一种类型的指针。

  • void* p;说 p 是内存地址,但我不知道内存中有什么
  • char* s;说s是内存地址,第一个字节包含一个字符
  • char** ps;说 ps 是一个内存地址,那里的四个字节(对于 32 位系统)包含一个 char* 类型的指针。

cf http://www.oberon2005.ru/paper/kr_c.pdf(K&R的电子书版)

于 2008-10-13T14:37:57.507 回答
3

它不是指向字符char*的指针,而是指向 4 个字符数组的指针: char* [4]. 使用 g++ 它不会编译:

main.cpp:在函数'int main(int,char**)'中:main.cpp:126:错误:初始化时无法将'char(*)[4]'转换为'char**'

此外,linux手册页

p

void * 指针参数以十六进制打印(就像通过 %#x 或 %#lx 一样)。它应该是指向无效的指针。

您可以将代码更改为:

char* s = "asd";
char** p = &s;

printf("The value of s is: %p\n", s);
printf("The address of s is: %p\n", &s);

printf("The value of p is: %p\n", p);
printf("The address of p is: %p\n", &p);

printf("The address of s[0] is: %p\n", &s[0]);
printf("The address of s[1] is: %p\n", &s[1]);
printf("The address of s[2] is: %p\n", &s[2]);

结果:

s的值为:0x403f00

s的地址是:0x7fff2df9d588

p的值为:0x7fff2df9d588

p的地址是:0x7fff2df9d580

s[0]的地址是:0x403f00

s[1]的地址是:0x403f01

s[2]的地址为:0x403f02

于 2013-03-15T01:42:51.760 回答
1

换行:

char s[] = "asd";

到:

char *s = "asd";

事情会变得更清楚

于 2008-10-13T14:34:39.887 回答
1

您不能更改静态数组的值(即地址)。用技术术语来说,数组的左值是它的第一个元素的地址。因此s == &s。这只是语言的一个怪癖。

于 2008-10-13T14:38:15.980 回答
1

通常,不必要地将指针强制转换为 (void*) 被认为是一种糟糕的风格。但是,在这里,您需要在 printf 参数上强制转换为 (void*),因为 printf 是可变参数。原型没有告诉编译器在调用站点将指针转换为什么类型。

于 2008-10-13T17:20:20.160 回答
0

您使用过:

char s[] = "asd";

这里 s 实际上指向字节“asd”。s 的地址也将指向这个位置。

如果您使用:

char *s = "asd";

s 和 &s 的值会有所不同,因为 s 实际上是指向字节“asd”的指针。

你用过:

char s[] = "asd";
char **p = &s;

这里 s 指向字节“asd”。p 是一个指向字符的指针,并且已经被设置为一个字符的地址。换句话说,您在 p 中有太多间接。如果你使用 char *s = "asd",你可以使用这个额外的间接。

于 2008-10-13T14:28:28.587 回答