В каком сегменте данных хранится строка C?
мне интересно, в чем разница между char s[] = "hello"
и char *s = "hello"
.
после прочтения этой и этой, я все еще не очень ясно по этому вопросу.
как я знаю, есть пять сегментов данных в памяти, текст, BSS, данных, стека и кучи.
в моем понимании,
в случае char s[] = "hello"
:
-
"hello"
в тексте. s
в Данные, если это глобальная переменная или в стеке, если это локальная переменная.у нас также есть копия
"hello"
здесьs
хранится, поэтому мы можем изменить значение этой строки черезs
.
в случае char *s = "hello"
:
-
"hello"
в тексте. -
s
находится в данных, если это глобальная переменная или в стеке, если это локальная переменная. -
s
просто указывает на"hello"
в тексте и у нас нет его копии, поэтому изменение значения string с помощью этого указателя должно вызвать "ошибку сегментации".
Я прав?
2 ответов
вы правы, что "привет" для первого случая составляет mutable и для второго случая неизменяемые строку. И они хранятся в памяти до инициализации.
в первом случае Мутабельный is инициализирован/скопировал С неизменяемые строки. Во втором случае указатель ссылается на неизменяемые строки.
для первого случая wikipedia говорит:
значения этих переменных, изначально хранятся в память только для чтения (обычно внутри .текст) и копируются в .сегмент данных во время запуска программы.
давайте рассмотрим сегмент.файл c.
char*s = "hello"; // string
char sar[] = "hello"; // string array
char content[32];
int main(int argc, char*argv[]) {
char psar[] = "parhello"; // local/private string array
char*ps = "phello"; // private string
content[0] = 1;
sar[3] = 1; // OK
// sar++; // not allowed
// s[2] = 1; // segmentation fault
s = sar;
s[2] = 1; // OK
psar[3] = 1; // OK
// ps[2] = 1; // segmentation fault
ps = psar;
ps[2] = 1; // OK
return 0;
}
вот сборка, сгенерированная для сегмента.файл c. Обратите внимание, что оба s
и sar
находится в global
ака .data
сегменте. Кажется sar
is const pointer
в mutable инициализации память или не указатель вообще (практически это массив). И в конце концов это подразумевает, что sizeof(sar) = 6
отличается от sizeof(s) = 8
. Есть "привет" и "phello" в режиме readonly(.rodata
) раздел и эффективно неизменяемые.
.file "segment.c"
.globl s
.section .rodata
.LC0:
.string "hello"
.data
.align 8
.type s, @object
.size s, 8
s:
.quad .LC0
.globl sar
.type sar, @object
.size sar, 6
sar:
.string "hello"
.comm content,32,32
.section .rodata
.LC1:
.string "phello"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq , %rsp
movl %edi, -52(%rbp)
movq %rsi, -64(%rbp)
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movl 52326512, -32(%rbp)
movl 69376613, -28(%rbp)
movb , -24(%rbp)
movq $.LC1, -40(%rbp)
movb , content(%rip)
movb , sar+3(%rip)
movq $sar, s(%rip)
movq s(%rip), %rax
addq , %rax
movb , (%rax)
movb , -29(%rbp)
leaq -32(%rbp), %rax
movq %rax, -40(%rbp)
movq -40(%rbp), %rax
addq , %rax
movb , (%rax)
movl , %eax
movq -8(%rbp), %rdx
xorq %fs:40, %rdx
je .L2
call __stack_chk_fail
.L2:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",@progbits
снова местные переменная в main, компилятор не утруждает себя созданием имя. И это может держать его в зарегистрироваться или в стек память.
обратите внимание, что значение локальной переменной "parhello" оптимизировано в числа 1752326512 и 1869376613. Я обнаружил это, изменив значение "parhello"на " parhellp". Разница в выходных данных сборки выглядит следующим образом:
39c39
< movl 86153829, -28(%rbp)
---
> movl 69376613, -28(%rbp)
таким образом, нет отдельного неизменяемого хранилища для psar
. Он превращается в целые числа в сегменте кода.
ответ на ваш первый вопрос:
char s[] = "hello";
s
- это массив типа char
. Массив-это const
указатель, что означает, что вы не может изменить s
использование арифметики указателей (т. е. s++
). Данные не const
, хотя, так что вы можете изменить его.
См. этот пример кода:
#include <stdio.h>
void reverse(char *p){
char c;
char* q = p;
while (*q) q++;
q--; // point to the end
while (p < q) {
c = *p;
*p++ = *q;
*q-- = c;
}
}
int main(){
char s[] = "DCBA";
reverse( s);
printf("%s\n", s); // ABCD
}
который переворачивает текст "DCBA"
производит "ABCD"
.
char *p = "hello"
p
- это указатель на char. Вы can сделать указатель арифметика -- p++
скомпилирует -- и помещает данные в части памяти только для чтения (данные const).
и используя p[0]='a';
приведет к ошибке во время выполнения:
#include <stdio.h>
int main(){
char* s = "DCBA";
s[0]='D'; // compile ok but runtime error
printf("%s\n", s); // ABCD
}
это компилируется, но не запускается.
const char* const s = "DCBA";
С const char* const
, вы не можете изменить ни s
, ни по содержанию данных, которые указывают на (т. е. "DCBE"
). таким образом, данные и указатель const:
#include <stdio.h>
int main(){
const char* const s = "DCBA";
s[0]='D'; // compile error
printf("%s\n", s); // ABCD
}
сегмент текста обычно является сегментом, где ваш код хранится и const
, т. е. неизменны. Во встроенных системах это ROM, PROM или флэш-память; на настольном компьютере это может быть ОЗУ.
стек-это оперативная память, используемая для локальных переменных в функциях.
куча-это оперативная память, используемая для глобальных переменных и инициализированных кучей данных.
BSS содержит все глобальные переменные и статические переменные, которые инициализируются до нуля или не инициализируются vars.
дополнительные информация, см. соответствующих Википедия и этот соответствующий вопрос переполнения стека
в отношении s
сам: компилятор решает, куда его поместить (в пространство стека или регистры процессора).
дополнительные сведения о нарушениях защиты памяти и доступа или сбоях сегментации см. В разделе соответствующая страница Википедии
это очень широкая тема, и в конечном счете точные ответы зависят от вашего оборудование и компилятора.