指针除了地址之外还有什么东西? [C]

时间:2016-01-12 20:47:38

标签: c arrays pointers printf format-specifiers

我现在在大学里学习了几个月,但我错过了一个关于指针的讲座,所以我试着通过在线学习来弥补它,我以为我得到了它 - 但是我偶然发现了一些对我来说非常恼火。

我知道指针只包含他们指向的地址 - 例如,如果到目前为止我理解了所有内容,我有:

int *pointer;
int number = 30;
pointer = &number;
printf("Number at location: %d", *pointer);

这应该可以正常工作。我将变量号的地址分配给指针,然后通过解除引用指针并从地址获取实际值来最终打印它。令我恼火的是焦点指针。

我已经阅读了字符串数组/指针,所以我尝试了一些事情,当我注意到一些奇怪的东西(至少我的眼睛)发生了,也有int指针:

char* pointer;
char array[] = "Dingleberry";
pointer = array;
printf("%s\n", pointer);
return 0;

我知道我没有直接分配地址,但是如果我没记错的话,使用数组,这不一定与指针结合 - 无论如何 - 这里的代码按预期工作,打印出“Dingleberry”。我现在的问题是......为什么?如果没有解除引用,指针不应只保存值的地址吗?如果我在这里取消引用,程序崩溃,它确实显示地址,如果我使用&虽然。

编译时我没有收到任何警告。此外,如果我使用它,它不应该工作:

printf("%c", pointer); 

只能收到一个字母? (我的意思是,尝试这确实显示了一个警告 - 但我有兴趣变得更好并排除我可能最愚蠢的误解。)

4 个答案:

答案 0 :(得分:8)

它与指针类型或它存储的内容无关,它是"%s"函数的printf()说明符,它指向指向ac字符串的指针,即{{ {1}}终止字节序列。

如果要打印指针地址,请使用nul说明符

"%p"

如果你想要它指向的对象的地址,在这种情况下数组只是

printf("%p\n", (void *) &pointer);

注意:对于通用指针,使用printf("%p\n", (void *) pointer); ,因为它可以转换而不需要转换为任何指针类型。

答案 1 :(得分:4)

坚持你的袜子,这会变得有点颠簸。

首先,C中的字符串只是一个字符值序列,后跟一个零值终结符。这些字符值可以是单字节字符(用char类型表示,常用编码为ASCII和EBCDIC)或多字节字符(每个字符由序列表示一个或多个{{ 1}}类型值,用于编码,如UTF-8)。单字节和多字节字符串的终结符是单个0值字节。 C还支持广泛的"字符类型char用于编码,例如(我认为)UTF-16。

字符串存储wchar_tchar的数组。该数组必须足够大,以存储字符串 plus 零终止符中的所有字符。因此,字符串wchar_t六个字符值的数组 - "Hello"。所有字符串都是{'H', 'e', 'l', 'l', 'o', 0}(或char)的数组,但并非wchar_t(或char)的所有数组都是字符串 - 零终结符必须存在以使数组表示字符串。

字符串文字,例如wchar_t"Hello"以及"Monday"存储为"Sun"的数组,以便它们在整个主体上可见程序,它们的生命周期从程序启动延伸到程序退出。尝试修改字符串文字的内容会调用未定义的行为;您的代码可能是段错误的,或者它可能完全符合您的意图,或者它可能会执行其他操作并使您的系统处于不良状态。大多数常见平台将字符串文字存储在只读内存段中,因此尝试更新它们会导致段错误。

当你声明像

这样的指针时
char

所有char *foo = "Hello"; contains都是字符串第一个字符的地址。当您使用foo转化说明符将此指针传递给printf时,%s将从该地址开始,并且" walk"在字符串下面,打印每个字符,直到它看到0终止符。大多数处理字符串的库函数都以相同的方式工作;他们取字符串的第一个元素的地址和" walk"直到他们看到终结者。

你也可以声明一个printf的数组并将字符串存储到它:

char

这一次,char foo[] = "Hello"; 是包含字符串foo的{​​{1}}的6个元素数组。与字符串文字char不同,您可以将"Hello"数组的内容修改为您心中的内容(尽管您只能存储5个字符或更少的字符串 - 数组在添加或删除数据时不要自动增长或缩小)。

请注意,"Hello"运算符仅在初始化声明中的数组时有效;在声明之外,您不能使用foo运算符将一个数组的内容复制到另一个数组。例如

=

没有工作。在大多数情况下,数组类型的表达式(如字符串文字=)被隐式转换("衰减")到指针类型,表达式的值将是第一个的地址数组的元素。所以在行

char foo[10];
...
foo = "Hello"; // bzzzt - no good

您尝试将字符串文字"Hello"地址分配给数组 foo = "Hello"; ,这将导致编译器牦牛相反,您必须使用诸如"Hello"foostrcpy等库函数来编写或更新存储字符串的数组。

然而,

strcat

工作得很好,因为在这种情况下sprintf只是指向char *foo; ... foo = "Hello"; 的指针,而不是foo的数组。

答案 2 :(得分:3)

您在这里缺少%s格式说明符的属性。

引用C11标准,章节§7.21.6.1,fprintf()

  

s如果没有l长度修饰符,参数应该是指向初始值的指针   字符类型数组的元素 .280)数组中的字符是   写入(但不包括)终止空字符。 [...]

因此,根据定义,%s需要一个指向以null结尾的数组的指针,并打印出数组的内容,直到null终止符。因此,在%d格式说明符的情况下,您不需要取消引用指针。

答案 3 :(得分:-1)

  

另外,如果我使用它,它应该不起作用:

printf("%c", pointer); 
     

只能收到一个字母?

不,由于default argument promotion.,因为可能打印一个字母,但是可能不是字母中指针可能指向的第一个字母,因此无法打印一个字母到。

简而言之:对于可变参数函数 - 一个C函数,它接受可变数量的参数,例如printf() - 每个参数都提升到固定大小。所以被调用函数无法直接告诉每个参数究竟是什么。这就是为什么printf()在格式字符串中有格式说明符的原因 - 它们告诉被调用函数实际上是什么参数。这也是为什么对参数使用不正确的格式说明符被认为是未定义的行为 - 如果使用%s格式说明符告诉被调用函数int是指向字符串的指针,该函数将取消引用提升int的值并尝试将它指向的内存视为字符串,如果它甚至是内存,它可能不是。

因此,即使结果是未定义的行为,可能会打印的值是char,最有可能是指针本身中包含的最低位字节。这甚至可能是与字符串中第一个字符匹配的字母。