char arr[]
(字符数组,C类型字符串)
- C语言中的字符串的概念:以NULL字节结尾的零个或者多个字符。而字符数组可以不以
'\0'
结束,而且不能为空。 - 字符串通常存在字符数组中,这也是C语言中没有显式的字符串类型的原因。
- 因为字符串以NULL结束,所以字符串内部不能有NULL字节。
- 为什么选择NULL作为字符串的终止符,因为它不是一个可打印的字符。
如下所示,是char str[]
,即字符数组的定义。str
是局部变量,存放在栈里面。 1
2
3char str1[] = "hello";
char str2[] = {'h', 'e', 'l', 'l', 'o', '\0'};
char str3[] = {'h', 'e', 'l', 'l', 'o'};
char*
(字符指针)
char*
是一个指针,指向一个char
,理论上来说,它并不是一个数组。char *ptr;
并不为它指向的内容分配内存,而是只分配一个char *
大小的内存存放指针变量ptr
。char arr[10];
是一个数组,不是一个指针,char *
和char[10]
不是一个类型。
如下所示:
1 | char str[] = "hello world"; |
char**
(指向字符指针的指针)
char**
是一个指针,指向一个指向char
的指针,它可以指向一个指针数组。如下所示:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main() {
char *arr[] = {
"hello",
"world",
"hi"
};
// arr是一个数组,数组的元素是`char*`类型。
// 数组名可以看成指向数组首元素的指针。
char **p = arr;
printf("%s\n", *p);
printf("%s\n", *(p+1));
printf("%c\n", *(*(p+1)+2));
}
p
是char**
类型的元素,即指向char*
类型的指针,p+1
获取下一个指向char*
的指针;*p
是char*
类型的元素,即指向char
类型的指针,指向第一个字符串的第一个字符,*(p+1)
指向第二个字符串的第二个字符;**p
是char
类型的元素,是第一个字符串的第一个字符,*(*(p+1)+2)
指向第二个字符串的第三个字符。
事实上,这个p和arr的作用是一样的。注:关于指针运算可以查看复合类型-指针。
怎么理解?如下图所示:
std::string
字符串字面值和string
是不同的类型,字符串字面值是为了和C语言兼容,它不是标准库中string
对象的内容。
字符串字面值常量
字符串字面值常量是用一对双引号包围一串字符。如"hello"
, "hi\n"
, ""
等,'a'
是字面值字符常量。
程序在使用字符串常量时,编译器会将字符串常量存放在数据区的常量区。当一个字符串常量出现在在一个表达式中,表达式使用的值是字符串常量在内存中的地址,而不是这些字符串常量本身。可以把字符串赋值给“指向字符的指针”,即让指针指向字符串常量在内存中的地址。但是不能把字符串常量赋值给一个字符数组,即不能把字符串常量的地址赋值给字符数组。比如1
char *message1 = "Hello world!";
这个代码是将字符串常量中第一个字符的地址传递给message。
字符数组和字面值常量的区别
1 | char message1[] = "Hello world!; |
第一种方式,其实是一种约定,它等于char message1[] = {'H', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', '\0'};
。
而第二种方式中,"Hello world"是一个字面值常量,它在内存中只能以数组的形式存在,是一个不可修改的左值表达式。而message2实际上指向了这个字符串数组的首字符。
常见的指针数组
main
函数的形参char *argv[]
就是一个指针数组getline
的第一个参数是char **lineptr
。
参考文献
1.《C++ Primer第五版》
2.https://www.zhihu.com/question/307261590/answer/563448215
3.https://www.zhihu.com/question/307261590/answer/563630017
4.https://stackoverflow.com/questions/10004511/why-are-string-literals-l-value-while-all-other-literals-are-r-value