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