C 初始化指针时字符串文字与字符数组

C 初始化指针时字符串文字与字符数组,c,arrays,gcc,C,Arrays,Gcc,灵感来自 我们可以通过字符串文本初始化char指针: char *p = "ab"; 而且非常好。 人们可以认为这相当于以下几点: char *p = {'a', 'b', '\0'}; 但显然情况并非如此。不仅因为字符串文本存储在只读内存中,而且似乎即使字符串文本具有chararray类型,并且初始值设定项{…}具有chararray类型,两个声明的处理方式也不同,因为编译器发出了警告: 警告:标量初始值设定项中的元素过多 在第二种情况下。对这种行为的解释是什么 更新:

灵感来自

我们可以通过字符串文本初始化
char
指针:

char *p = "ab";
而且非常好。 人们可以认为这相当于以下几点:

char *p = {'a', 'b', '\0'};
但显然情况并非如此。不仅因为字符串文本存储在只读内存中,而且似乎即使字符串文本具有
char
array类型,并且初始值设定项
{…}
具有
char
array类型,两个声明的处理方式也不同,因为编译器发出了警告:

警告:标量初始值设定项中的元素过多

在第二种情况下。对这种行为的解释是什么

更新:

此外,在后一种情况下,指针
p
的值将为
0x61
(第一个数组元素
'a'
的值),而不是内存位置,因此编译器,如警告所示,只取初始值设定项的第一个元素,并将其从C99分配给
p

字符串文字是包含在字符串中的零个或多个多字节字符的序列 双引号

因此,在第二个定义中没有字符串文字,因为它不在双引号内。在向指针写入内容之前,应该为指针分配内存,或者如果您想使用初始值设定项列表,那么

char p[] = {'a','b','\0'};

这就是你想要的。基本上两者都是不同的声明。

第二个示例在语法上不正确。在C中,
{'a','b','\0'}
可用于初始化数组,但不能用于初始化指针

相反,您可以使用C99复合文字(在某些编译器中也可用作扩展,例如),如下所示:

char *p = (char []){'a', 'b', '\0'};
char str[] = {'h', 'e', 'l', 'l', 'o', '\0'};

请注意,它的功能更强大,因为初始值设定项不一定以null结尾。

我认为您会感到困惑,因为
char*p=“ab”
字符p[]=“ab”具有相似的语义,但含义不同

我认为后一种情况(
charp[]=“ab”;
)最好被看作是
charp[]={'a','b','\0'的简写符号(使用初始化器确定的大小初始化数组)。实际上,在本例中,您可以说,
“ab”
实际上没有用作字符串文字

但是,前一种情况(
char*p=“ab”
)的不同之处在于,它只是初始化指针
p
,以指向只读字符串文本的第一个元素
“ab”

我希望你能看到区别。而
charp[]=“ab”
可表示为初始化,如您所述,
char*p=“ab”不是,就像指针不是数组一样,用数组初始值设定项初始化它们会做一些完全不同的事情(即在您的例子中,给它们第一个元素的值,
0x61

长话短说,C编译器仅在合适的情况下使用
char
数组初始值设定项“替换”字符串文字,即它被用来初始化
char
数组。

字符串文字在C中具有“神奇”的状态。它们与其他任何东西都不同。为了理解原因,从内存管理的角度考虑这一点很有用。例如,问问自己,“字符串文字存储在内存中的什么地方?何时从内存中释放?”事情就会开始变得有意义

它们不同于易于转换为机器指令的数字文字。对于一个简化的示例,如下所示:

char *p = (char []){'a', 'b', '\0'};
char str[] = {'h', 'e', 'l', 'l', 'o', '\0'};
intx=123

。。。在机器层面上,可能会转化为如下内容:

mov ecx,123

当我们做以下事情时:

const char*str=“你好”

。。。我们现在面临一个两难境地:

mov-ecx,??

对于多字节、可变长度的字符串实际上是什么,硬件不一定有一些本机的理解。它主要了解位、字节和数字,并有专门用于存储这些内容的寄存器,而字符串是包含这些内容的多个的内存块

因此,编译器必须生成指令,将该字符串的内存块存储在某个位置,因此在编译代码时,编译器通常会生成指令,将该字符串存储在全局可访问的位置(通常是只读内存段或数据段)。它们还可以合并存储在同一内存区域中的多个相同的文本字符串,以避免冗余。现在,它可以生成一条
mov/load
指令,将地址加载到文本字符串中,然后您可以通过指针间接使用它

我们可能遇到的另一种情况是:

static const char* some_global_ptr = "blah";

int main()
{
    if (...)
    {
        const char* ptr = "hello";
        ...
        some_global_ptr = ptr;
    }
    printf("%s\n", some_global_ptr);
}
自然地,
ptr
超出了范围,但是我们需要这个文本字符串的内存,以便这个程序具有定义良好的行为。因此,文字字符串不仅转换为全局可访问内存块的地址,而且只要二进制文件/程序加载/运行,它们也不会被释放,因此您不必担心它们的内存管理。[编辑:排除潜在的优化:对于C程序员来说,我们永远不必担心文本字符串的内存管理,因此效果就像它总是存在一样]

关于字符数组,文字字符串本身不一定是字符数组。在软件中,任何时候我们都不能将它们捕获到一个数组r值中,该数组r值可以为我们提供使用
sizeof
分配的字节数。我们只能通过
char*/const char*

这段代码实际上为我们提供了这样一个数组的句柄,而不涉及指针:

char str[] = "hello";
这里发生了一些有趣的事情。生产编译器可能会应用所有类型的优化,但排除这些优化,在基本级别上,这样的代码可能会创建两个单独的内存块

第一个区块将持续一段时间