Tại sao tôi không nên cố gắng thay đổi nó?
Bởi vì đó là hành vi không xác định. Trích dẫn từ dự thảo C99 N1256 6.7.8 / 32 "Khởi tạo" :
VÍ DỤ 8: Tuyên bố
char s[] = "abc", t[3] = "abc";
định nghĩa các đối tượng mảng char "đơn giản" s
và t
có các phần tử được khởi tạo bằng chuỗi ký tự.
Tuyên bố này là giống hệt với
char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };
Nội dung của các mảng có thể sửa đổi. Mặt khác, tuyên bố
char *p = "abc";
định nghĩa p
với kiểu "con trỏ tới char" và khởi tạo nó để trỏ đến một đối tượng có kiểu "mảng char" có độ dài 4 có các phần tử được khởi tạo với một chuỗi ký tự bằng chữ. Nếu một nỗ lực được thực hiện để sử dụng p
để sửa đổi nội dung của mảng, hành vi không được xác định.
Họ đi đâu?
GCC 4.8 x86-64 ELF Ubuntu 14.04:
char s[]
: cây rơm
char *s
:
.rodata
phần của tệp đối tượng
- cùng phân đoạn nơi
.text
phần của tệp đối tượng bị kết xuất, có quyền Đọc và Exec, nhưng không ghi
Chương trình:
#include <stdio.h>
int main() {
char *s = "abc";
printf("%s\n", s);
return 0;
}
Biên dịch và dịch ngược:
gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o
Đầu ra chứa:
char *s = "abc";
8: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
f: 00
c: R_X86_64_32S .rodata
Vì vậy, chuỗi được lưu trữ trong .rodata
phần.
Sau đó:
readelf -l a.out
Chứa (đơn giản hóa):
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000704 0x0000000000000704 R E 200000
Section to Segment mapping:
Segment Sections...
02 .text .rodata
Điều này có nghĩa là tập lệnh liên kết mặc định bỏ cả hai .text
và .rodata
vào một phân đoạn có thể được thực thi nhưng không được sửa đổi ( Flags = R E
). Cố gắng sửa đổi một phân đoạn như vậy dẫn đến một segfault trong Linux.
Nếu chúng ta làm tương tự cho char[]
:
char s[] = "abc";
chúng tôi đạt được:
17: c7 45 f0 61 62 63 00 movl $0x636261,-0x10(%rbp)
vì vậy nó được lưu trữ trong ngăn xếp (liên quan đến %rbp
) và tất nhiên chúng ta có thể sửa đổi nó.