C语言打印输出字符串的怪事?
#include <stdio.h> int main() { char *a[]={ "Hello World" }; char b[]={ 'C','D' }; printf("%c",b); printf("\n"); printf("%s",b); }
C语言中的数组名,是不可修改的变量,只能在初始化的时候赋值,我们不能做类似b++,b=b+1这样的操作,否则会提示:“表达式必须是可修改的左值”。如果我们代码上写 7++; 也会提示一样的错误,但是数组名可以引用下标输出,如b[1],代表数组中的第二个值。
上面代码传递数组b,打印输出一个字符B,很奇怪,代码中也没有看到B啊?我们通过监视窗口获取这个b所代表的地址是0x28ff42,地址最后的42的ASCII刚好就是字符B,这边地址直接被当成字符输出,当地址发生变化,这个输出值也会改变。
最后输出一个字符串CD,为什么这边不输出地址的ASCII码了,因为%s表示输出的是字符串,所以传进来的肯定是一个地址,编译器编译的时候会通过该地址去内存中取对应的数据输出。
下面我们改下代码,把第一个指针数组的指针符号*去掉,如下代码,看看会发生什么?
#include <stdio.h> int main() { char a[]={ "Hello World" }; char b[]={ 'C','D' }; printf("%c",b); printf("\n"); printf("%s",b); }
第一行的点号因为b代表的地址已经发生了变化,所以这里也改变了
第二行把只一个字符数组a中的全部字符都输出来了,这是为什么?
因为C语言规定,定义了一个字符串常量,都是以字符数组的形式存储在内存中,而且会以\0表示字符串的结束。所以就算定义字符串没有带\0,实际内存存储的时候也存进去。当输出打印的时候如果遇到\0代表字符串结束了,\0不会输出。
我们通过内存窗口看下,首先找到 b的起始地址0x28ff2e,开始打印字符,打印C,再打印D,往后发现没有00,那就继续打印,这个时候其实已经把a数组中的字符也打印来了,最后一个64刚好是a数组中d的ASCII编码,注意这边都是16进制。再往后有一个00,这时候代表字符串结束。
如果我们希望打印b的时候,不要把a也打印出来呢,最简单的办法就是b再添加一个数组元素 ‘\0’。
a数组中Hello World是一个字符串常量,会在程序加载的时候分配到内存堆中,当运行到该代码段的时候字符串重新复制一份到栈中,这个我们可以从编译好的汇编代码中看出来。
栈中分配了12个字节的空间,用来存放从ds:0x403000 复制过来的Hello World\0,数组名a是栈中第一个字符的地址,我看下ds:0x403000内存
回过头来我们看最前面那段代码,也就是定义了一个指针数组的a, 为什么最后打印的时候只输出了CD,而没有打印Hello World
因为栈中并没有拷贝一份Hello World\0,栈中中存放了1字节的堆地址:0x403000,而这个内容的末尾刚好是0结尾,所以打印的时候遇到0代表字符串的结束,没有再打印,如果不是0结尾则继续打印。