关于char[]和char*的理解
本质区别
char str[]表示str是一个字符数组,str这个字符数组里面所有的元素都是单个的字符,因此char[]强调的是数组的概念。
char* str表示str是一个指针,str这个指针指向了一个字符的地址,因此char*强调的是指针的概念。
char[]字符串
由于C语言中没有“字符串”这种数据类型,而字符串本质上就是将一个个的字符串联起来,那么完全可以使用字符数组来表示一个字符串:
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";