我在一本书中读到,当你有一个像"blabla"
这样的字符串时,它意味着有一个隐藏的char数组,而这个表达式将地址返回给第一个元素 ,它就像一个 const 数组。
这让我对两种情况感到困惑:
char a[7] = "blabla"
是不可能的,因为“blabla”会将 地址 返回到数组的第一个元素,所以你会怎么做地址为a
而非实际元素?
它说当你看到“blabla”时它意味着像const
char数组,这意味着我根本不能改变a
(这不是真的)。 / p>
我想这里真正基本的东西对我来说还不清楚。
答案 0 :(得分:4)
根据C标准(6.3.2.1 Lvalues,数组和函数指示符)
3 除非 sizeof运算符的操作数或 一元&运算符,或者是用于初始化的字符串文字 数组,将类型为''array of type''的表达式转换为 带有''指向类型'的指针的表达式,指向初始值 数组对象的元素,而不是左值。如果是数组对象 具有寄存器存储类,行为未定义。
所以在这个宣言中
char a[7] = "blabla";
由于包含终止零作为字符串文字的元素而具有字符数组char[7]
类型的字符串文字的元素用于初始化字符数组a
<的元素/ p>
事实上,这个声明等同于声明
char a[7] = { 'b', 'l', 'a', 'b', 'l', 'a', '\0' };
考虑到在C字符串文字中有非常量字符数组的类型。然而,它们本身可能无法修改。
来自C标准(6.4.5字符串文字)
7未指明这些阵列是否与它们不同 元素具有适当的值。如果程序试图 修改这样的数组,行为是未定义的。
所以你可以写一些例如
char *s = "blabla";
在这种情况下,根据C标准的第一个引用,字符串文字将转换为指向其第一个元素的指针,指针的值将分配给变量s
。
在静态内存中创建了一个未命名的字符数组,并将数组的第一个元素的地址分配给指针s
。您可能不会使用指针来更改您可能不会写的文字,例如
char *s = "blabla";
s[0] = 'B';
在C ++中,字符串文字确实具有常量字符数组的类型。所以你必须用C ++程序编写
const char *s = "blabla";
在C中你也可以写
char a[6] = "blabla";
^^^^
在这种情况下,字符串文字的终止零不会用于初始化字符数组a
。所以数组不会包含字符串。
在C ++中,这样的声明无效。
答案 1 :(得分:3)
第一种情况,
char a[7] = "blabla"
,是不可能的[...]
是的,这是可能的,这是初始化。
引用C11
,章节§6.7.9/ P14,初始化,
字符数组的数组可以用字符串文字或
UTF−8
字符串初始化 文字,可选择括在括号中。字符串文字的连续字节(包括 如果有空间或数组的大小未知,则终止空字符)初始化 数组的元素。
第二种情况,
它说当你看到“blabla”时它就像一个const char数组,这意味着我根本无法改变a(这不是真的)。
[从直接尝试修改字符串文字的角度]
你可以,但你必须没有。
从章节§6.4.5
[...]如果程序试图修改这样的数组,行为是 未定义。
也就是说,在 你的情况 中,a
不是指向字符串文字的指针,它是一个数组,其元素初始化为来自字符串文字。完全允许您修改a
数组的内容。
答案 2 :(得分:2)
&#34;布拉布拉&#34;这是本书所说的,一个7字节的字符数组,最后一个是&#39; \ 0&#39;,放在只读数据空间中(如果可能的话)。
(1)当你写:
char a[7] = "blabla";
您告诉编译器在堆栈上创建一个包含7个字符的可变数组,并在其中复制只读数组。请注意,您也可以写:
char a[] = "blabla";
...这样更安全,因为编译器会为你计算字符。
(2)鉴于[]是&#34; blabla&#34;的副本。你可以毫无问题地写信给它。如果你想保留只读属性,你可以写:
const char *a = "blabla";
这次a将是指向常量字符串的const指针,其内容将不可变。无论如何,您将能够重新分配指针:
const char *a = "blabla";
a = "blublu";
答案 3 :(得分:1)
为时已晚,但我仍然提供了答案。
所以让我们区分
main()
{
char *a="blabla";
a[3]='x';
}
这一个,你的。
main()
{
char a[7] = "blabla"
a[3]='x';
}
因此他们之间存在很大差异。
在第一种情况下,对象a
是一个指针,其值指向blabla
字符串的开头。
转储汇编的代码,我们看到:
4004aa: 48 c7 45 f8 54 05 40 movq $0x400554,-0x8(%rbp)
4004b1: 00
4004b2: 48 8b 45 f8 mov -0x8(%rbp),%rax
4004b6: 48 83 c0 03 add $0x3,%rax
4004ba: c6 00 78 movb $0x78,(%rax)
因此,它尝试并将指针设置为地址0x400554
。
Objdumpo报告此地址位于.rodata
段。
反汇编部分.rodata:
0000000000400550 <_IO_stdin_used>:
400550: 01 00 add %eax,(%rax)
400552: 02 00 add (%rax),%al
400554: 62 (bad)
400555: 6c insb (%dx),%es:(%rdi)
400556: 61 (bad)
400557: 62 .byte 0x62
400558: 6c insb (%dx),%es:(%rdi)
400559: 61 (bad)
因此,编译器在该地址的.rodata中安装了字符串blabla
,然后尝试修改.rodata段,完成分段错误。
readelf
报告.rodata上没有W
访问权限:
[13] .rodata PROGBITS 0000000000400550 00000550
000000000000000b 0000000000000000 A 0 0 4
另一方面,您尝试做的事情(第二个程序)编译如下:
00000000004004a6 <main>:
4004a6: 55 push %rbp
4004a7: 48 89 e5 mov %rsp,%rbp
4004aa: c7 45 f0 62 6c 61 62 movl $0x62616c62,-0x10(%rbp)
4004b1: 66 c7 45 f4 6c 61 movw $0x616c,-0xc(%rbp)
4004b7: c6 45 f6 00 movb $0x0,-0xa(%rbp)
4004bb: c6 45 f3 78 movb $0x78,-0xd(%rbp)
在这种情况下,数组对象a
在堆栈帧上分配7个字节,从偏移%RBP-0xA
开始到%RBP-0x10
。
当它试图做[3] =&#39; x&#39;它将修改%RBP-0xD
处的堆栈。堆栈有write
权限,一切都可以。