В каком сегменте данных хранится строка C?

мне интересно, в чем разница между char s[] = "hello" и char *s = "hello".

после прочтения этой и этой, я все еще не очень ясно по этому вопросу.


как я знаю, есть пять сегментов данных в памяти, текст, BSS, данных, стека и кучи.

в моем понимании,

в случае char s[] = "hello":

  1. "hello" в тексте.
  2. s в Данные, если это глобальная переменная или в стеке, если это локальная переменная.

  3. у нас также есть копия "hello" здесь s хранится, поэтому мы можем изменить значение этой строки через s.

в случае char *s = "hello":

  1. "hello" в тексте.
  2. s находится в данных, если это глобальная переменная или в стеке, если это локальная переменная.
  3. 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 сам: компилятор решает, куда его поместить (в пространство стека или регистры процессора).

дополнительные сведения о нарушениях защиты памяти и доступа или сбоях сегментации см. В разделе соответствующая страница Википедии

это очень широкая тема, и в конечном счете точные ответы зависят от вашего оборудование и компилятора.