在C语言中打印指针

59

我正在尝试理解指针相关的内容,因此我写了这段代码:

#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 typechar (*)[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必须是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的值)相同?

8个回答

38
"

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

数组也意味着特定类型的连续内存地址,因为s会衰减为指向char*(数组第一个元素)的指针。 - Cholthi Paul Ttiopic
@CholthiPaulTtiopic - 首先,应该说它是“指向char的指针”(而不是char *)。 但是,在某些情况下,您可以将其用作指向char的指针,但这并不意味着它就是char *。 “5.0”可能是一个整数,但它不是int,尽管您可以在某些需要int的地方使用它。 - James Curran

26

是的,你的编译器期望 void *。只需将它们强制转换为 void *。

/* for instance... */
printf("The value of s is: %p\n", (void *) s);
printf("The direction of s is: %p\n", (void *) &s);

1
那是错误的。将其转换为void*只是隐藏了问题,而不是解决它。 - James Curran
1
我的第一反应是他只是想打印数字以查看编译器分配的值,而将其转换为void会让他做到这一点,而不管声明中是否存在任何问题。但是,是的,这肯定是在回避问题。 - indiv
11
在可变参数函数中,将参数强制转换为 void * 类型并用 %p 格式进行输出,实际上是符合语言标准所要求的。 - Jens
2
这是C语言中确实需要进行类型转换的罕见情况之一。 - alk

5
如果将数组名称作为函数参数传递,它会被视为传递了数组的地址。因此,&s和s是相同的参数。请参见K&R 5.3。&s[0]与&s相同,因为它获取数组的第一个元素的地址,这与获取数组本身的地址相同。
对于所有其他指针,尽管它们都是内存位置,但它们仍然具有类型,并且编译器将警告将一种类型的指针分配给另一种类型的指针。
  • void* p;表示p是一个内存地址,但我不知道内存中有什么内容
  • char* s;表示s是一个内存地址,第一个字节包含一个字符
  • char** ps;表示ps是一个内存地址,在那里的四个字节(对于32位系统)包含char*类型的指针。
参考:http://www.oberon2005.ru/paper/kr_c.pdf(K&R的电子书版本)

3
这不是指向字符的指针char*,而是指向包含4个字符的数组的指针:char* [4]。使用g++编译时会出错:

main.cpp: In function ‘int main(int, char**)’: main.cpp:126: error: cannot convert ‘char (*)[4]’ to ‘char**’ in initialization

此外,Linux man页面说:

p

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

你可以将代码更改为:
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


嗨,感谢您回答这个老问题。幸运的是,在过去的四年中,我学到了更多关于编程的知识。但我认为您的意图是为那些来到这个问题的人提供更好的见解。考虑到这一点,我必须说使用C++编译器编译C代码并不是一个好主意,它们不是相同的语言,混淆它可能会导致错误。附注:这个评论框太糟糕了哈哈。 - alcuadrado
@alcuadrado 哦,没看到日期,这是史前时期! - 4pie0
@where_is_tftp,你能解释一下这里的“方向”是什么意思吗?“s的方向”,我不明白指针的“方向”是什么意思。 - zumzum
@zumzum 这是原帖的约定,本意是“地址”,我已在我的回答中进行了更正。谢谢。 - 4pie0

1

换行:

char s[] = "asd";

目标:

char *s = "asd";

事情将变得更加清晰


2
错误将会消失,但是问题的本质并没有更加清晰,因为你修复了问题却没有解释原始问题的原因。 - James Curran

1

你无法更改静态数组的值(即地址)。在技术术语中,数组的左值是其第一个元素的地址。因此,s == &s。这只是语言的一种怪癖。


1
通常情况下,将指针强制转换为(void*)被认为是不好的编程风格。但在这里,你需要在printf参数上进行(void*)转换,因为printf是可变参数的。原型无法告诉编译器在调用时将指针转换成什么类型。

0

你已经使用了:

char s[] = "asd";

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

如果您使用了:

char *s = "asd";

s的值和&s的值是不同的,因为s实际上是指向字节"asd"的指针。

你使用了:

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

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


@salcuadrado:但是s就是'a'。你对此的基本误解是根本问题。 - James Curran
抱歉 - 当我在打字时,它就发送了该消息。 - selwyn

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