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结尾则继续打印。

 

posted @ 2022-09-05 14:27  自由小菜园  阅读(327)  评论(0编辑  收藏  举报