1

在 C 中,我们可以创建一个指向常量字符串的指针,例如

char *s = "abc";

但既然字符串基本上都是以空字符结尾的字符数组,为什么不允许这样做呢?

char *s = {'a', 'b', 'c' , '\0' };
4

3 回答 3

10

这基本上是因为语言不允许。

字符串文字是 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 节,其中讨论了数组和指针(并且很好地消除了对它们的一些常见误解)。

于 2013-07-28T18:59:04.307 回答
8

这是因为指针不是数组。(您可以将数组衰减为指向第一个元素地址的指针,但这不会使指针成为数组。)

你可以这样做:

   char s[] = {'a', 'b', 'c', '\0'};
// ^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^
// array      valid initializer for char[]

但不是这个:

   char* s = {'a', 'b', 'c', '\0'};
// ^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^
// pointer   invalid initializer for char*
于 2013-07-28T18:14:31.433 回答
3

可以这样编码:

 char s[] = { 'a', 'b', 'c', '\0` };

这与

 char s[] = "abc";

但是,字符串文字实际上是只读的(因为在文本段中)。因此,您应该将字符串文字理解为 a const char s[](即使它不完全是,如果您完全关心 C 标准)。特别是编码"abc"[1] = 'X';未定义的行为(并且可能会因 Linux 上的分段违规而崩溃)。

如果您启用所有警告(例如使用gcc -Wall

当然,正如这个答案所解释的,C 中的指针和数组并不相同(数组可以衰减为指针)。

于 2013-07-28T18:14:23.327 回答