在C中,我们可以创建一个指向常量字符串的指针
char *s = "abc";
Run Code Online (Sandbox Code Playgroud)
但由于字符串基本上是空终止字符数组,为什么不允许这样做?
char *s = {'a', 'b', 'c' , '\0' };
Run Code Online (Sandbox Code Playgroud)
Kei*_*son 10
这基本上是因为语言不允许它.
字符串文字是类型的表达式char[N+1],其中N是文字的长度.它指的是具有静态存储持续时间的匿名数组对象,这意味着该对象存在于整个程序执行中.
(请注意,与C++不同,它不是 const char[N+1],但尝试修改它具有未定义的行为.)
和数组类型的任何表达式一样,它隐式转换为指向大多数上下文中数组的第一个元素的指针.
所以在这个宣言中:
char *s = "abc";
Run Code Online (Sandbox Code Playgroud)
表达式"abc"被隐式转换为指向数组的第一个字符的指针,用于初始化s.但是这样编写它会更安全:
const char *s = "abc";
Run Code Online (Sandbox Code Playgroud)
这样你就不会意外地尝试修改数组.
那么为什么这不起作用呢?
char *s = { 'a', 'b', 'c', '\0' };
Run Code Online (Sandbox Code Playgroud)
这是因为{ 'a', 'b', 'c', '\0' } 不是表达.它是一个初始化程序,可用于初始化数组对象,但不能用于初始化指针对象.如果你改为写:
char arr[] = { 'a', 'b', 'c', '\0' };
Run Code Online (Sandbox Code Playgroud)
然后初始化器用于初始化数组对象; 它不会创建数组对象.要使char *s版本起作用,必须为指向的指针创建一个对象s.Brace封闭的初始化列表只是不这样做.
C99添加了一个新功能,复合文字,在某些方面类似于字符串文字但更通用.例如,这个:
(char[]){ 'a', 'b', 'c', '\0' }
Run Code Online (Sandbox Code Playgroud)
是类型的表达式char[4]- 再次,就像数组类型的任何表达式一样,它在大多数上下文中被隐式转换为指针.所以这:
char *arr = (char[]){ 'a', 'b', 'c', '\0' };
Run Code Online (Sandbox Code Playgroud)
是有效的,并arr指向由复合文字创建的匿名数组对象的第一个元素.
有一个显着的区别:该数组对象的生命周期不一定是静态的.如果复合文字出现在任何函数之外,则数组对象确实存在于整个程序执行中; 否则,它就像一个具有自动存储持续时间的本地对象.
如果你使用的是不支持C99的编译器(*cough*Microsoft*cough*),你可以先声明一个数组对象:
const char arr[] = { 'a', 'b', 'c', '\0' };
const char *s = arr;
Run Code Online (Sandbox Code Playgroud)
另请参阅comp.lang.c FAQ的第6节,其中讨论了数组和指针(并且很好地解决了一些关于它们的常见误解).
这是因为指针不是数组.(您可以将数组衰减到指向第一个元素地址的指针,但这不会使指针成为数组.)
你可以这样做:
char s[] = {'a', 'b', 'c', '\0'};
// ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
// array valid initializer for char[]
Run Code Online (Sandbox Code Playgroud)
但不是这个:
char* s = {'a', 'b', 'c', '\0'};
// ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
// pointer invalid initializer for char*
Run Code Online (Sandbox Code Playgroud)