关于char[]和char*的理解

本质区别

char str[]表示str是一个字符数组,str这个字符数组里面所有的元素都是单个的字符,因此char[]强调的是数组的概念。

char* str表示str是一个指针,str这个指针指向了一个字符的地址,因此char*强调的是指针的概念。

 

char[]字符串

由于C语言中没有“字符串”这种数据类型,而字符串本质上就是将一个个的字符串联起来,那么完全可以使用字符数组来表示一个字符串:

char str[5] = {'h', 'e', 'l', 'l', 'o'};
这样从物理空间来讲,确实是一个又5个单字符构成的hello的字符串,但是这种写法看起来不够直观,写起来要加很多的单引号很麻烦,而且比较关键的一点是,如何能够输出str这个字符串?
如果我们尝试用printf来输出这个字符串:
char str[5] = {'h', 'e', 'l', 'l', 'o'};
printf("%s", str);

执行后会发现打印出来前面是对的,但是后面会携带一些乱码(这些乱码在各个平台的表现形式不同),原因是printf中%s输出方式是检测直到遇到'\0'为止,它并不管你这个字符串(在这里就是str这个字符数组)是多长的,只认为我遇到了'\0'就认为这个字符串结束,所以连带打出了后面一些无法预知的符号,也就是乱码,直到遇到'\0',也就是我们俗称的结束符。

 

那既然字符串是以结束符作为结尾的,那就可以手动给这个str字符串添加一个'\0':

char str[6] = {'h', 'e', 'l', 'l', 'o', '\0'};

注意'\0'也是占据一个字节的,所以此时str的长度变成了6,当然也可以省略不写数组长度,但实际有效字符其实是5个。

刚提到这种写法很难一眼看出这是一个字符串,至少在我们的认知中,更容易接受像"hello"这种直观的表示方法,因此C提供了一种直观的初始化字符数组的方法:

char str[6] = {"hello"};

这种写法,不需要显示的写出结束符,但是要给结束符预留一个字符,也就是一个字节的位置,所以一样的,str长度为6,有效字符还是5个。

通常我们把{}省略,用字符数组表示字符串的最简便形态就是:

char str[6] = "hello";

 

char*字符串

按照正常的理解char*理应像int*之类的基本类型一样,表示指向的位置存放着一个char类型的变量,也就是指向的是一块1个字节的内存地址,也就是这样:

char a = 'A';
char * p = &a;
printf("%c", *p);  // A

但是为什么能够用char*表示一个字符串?

因为在C语言中像"hello"这样的字符串本质是用一个地址来表示,用的就是"hello"中的首地址也就是'h'字符的地址。

这样的话就可以用char*来表示一个字符串,更准确的说是指向这个字符串的首地址:

char* str = "hello"; 

编译器会自动为"hello"加上一个结束符,用来标志字符串的结束位置。 

 

区别

首先从本质上就是有区别的,char[]是遵循的是数组的规则,含有数组的特性;char*遵循的是指针的规则,含有指针的特性。

其次当使用char* str = "hello"时,编译器会在常量区创建一个字符串常量"hello",然后str指向这个字符串常量的首地址,编译器在编译时就知道str指向的是一个常量。

而char[]虽然是写成char str[] = "hello",但是是在运行时将"hello"中的字符逐个放入str数组中,所以此时的"hello"并不是常量,而是被拆成了栈上str数组中的各个元素。

因此,使用char* str = "hello"后,不能够通过str指针来改变常量"hello";使用char[] str = "hello"后,依然能够改变数组中的各个字符。

 

那么可能我们会好奇,既然char* str = "hello"中的"hello"是常量,为什么还允许我们通过char* str获取指向"hello",万一我们通过str企图改变"hello"不是很危险吗?

其实编译器会警告我们:

warning: ISO C++ forbids converting a string constant to 'char*'

禁止将不变字符串转为char*,如何避免这种警告呢?既然编译器不想我们改,我们就主动告诉它我没有想要改变常量字符串的企图即可:

const char* str = "hello";

 

posted @ 2020-07-31 22:04  代萌  阅读(4641)  评论(0编辑  收藏  举报