выделение памяти для строки и массива char - PullRequest
5 голосов
/ 26 июня 2011

Я не могу понять, как выделяется память в следующем коде:

#include<stdio.h>
#include<string.h>

int main()
{
    char a[]={"text"};
    char b[]={'t','e','x','t'};
    printf(":%s: sizeof(a)=%d, strlen(a)=%d\n",a, sizeof(a), strlen(a));
    printf(":%s: sizeof(b)=%d, strlen(b)=%d\n",b, sizeof(b), strlen(b));
    return 0;
}

Выход

:text: sizeof(a)=5, strlen(a)=4
:texttext: sizeof(b)=4, strlen(b)=8

Изучая адреса памяти и выходной код, кажется, что переменная b находится перед переменной a, и поэтому strlen (b), ища \ 0, возвращает 8. Почему это происходит? Я ожидал, что переменная a будет объявлена ​​первой.

Ответы [ 4 ]

7 голосов
/ 26 июня 2011

Язык не дает никаких гарантий относительно того, что и где находится.Итак, ваш эксперимент имеет мало смысла.Это может сработать, а может и нет.Поведение не определено.Ваш b не является строкой, и это UB для использования strlen с чем-то, что не является строкой.

С чисто практической точки зрения локальные переменные обычно размещаются в стеке, истек на майских современных платформах (например, x86) растет назад , то есть с более высоких адресов на более низкие адреса.Таким образом, если вы используете одну из этих платформ, возможно, ваш компилятор решил разместить переменные в порядке их объявления (a first и b second), но так как стек увеличивается назад b в итогеболее низкие адреса в памяти, чем a.Т.е. b закончилось до a в памяти.

Можно заметить, однако, что типичная реализация обычно не выделяет пространство стека для локальных переменных один за другим.Вместо этого весь блок памяти для всех локальных переменных (кадр стека) выделяется сразу, а это означает, что логика, описанная выше, не обязательно применяется.Тем не менее, все еще возможно, что компилятор все равно следует «обратному» подходу к расположению локальных переменных, то есть объявленные ранее переменные помещаются позже во фрейм локальной памяти, «как если бы» они были расположены один за другим в порядкеих декларации.

2 голосов
/ 26 июня 2011

Ваш массив символов "b" не заканчивается нулем.Чтобы понять, учтите, что объявление char a [] эквивалентно:

char a[] = { 't', 'e', 'x', 't', '\0' };

Другими словами, strlen (b) не определен, он просто просматривает случайную память на наличие символа NULL (0 байт).

1 голос
/ 26 июня 2011

Я скомпилировал ваш код в Linux / x86 с GCC, используя флаг -S, чтобы увидеть выходные данные сборки.Это показывает, что для меня b [] размещается по более высокому адресу памяти, чем a [], поэтому я не получил strlen (b) = 4.

    .file   "str.c"
    .section    .rodata
    .align 4
.LC0:
    .string ":%s: sizeof(a)=%d, strlen(a)=%d\n"
    .align 4
.LC1:
    .string ":%s: sizeof(b)=%d, strlen(b)=%d\n"
    .text
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    subl    $32, %esp
    movl    %gs:20, %eax
    movl    %eax, 28(%esp)
    xorl    %eax, %eax
    movl    $1954047348, 19(%esp)
    movb    $0, 23(%esp)
    movb    $116, 24(%esp)
    movb    $101, 25(%esp)
    movb    $120, 26(%esp)
    movb    $116, 27(%esp)
    leal    19(%esp), %eax
    movl    %eax, (%esp)
    call    strlen
    movl    %eax, %edx
    movl    $.LC0, %eax
    movl    %edx, 12(%esp)
    movl    $5, 8(%esp)
    leal    19(%esp), %edx
    movl    %edx, 4(%esp)
    movl    %eax, (%esp)
    call    printf
    leal    24(%esp), %eax
    movl    %eax, (%esp)
    call    strlen
    movl    $.LC1, %edx
    movl    %eax, 12(%esp)
    movl    $4, 8(%esp)
    leal    24(%esp), %eax
    movl    %eax, 4(%esp)
    movl    %edx, (%esp)
    call    printf
    movl    $0, %eax
    movl    28(%esp), %edx
    xorl    %gs:20, %edx
    je  .L2
    call    __stack_chk_fail
.L2:
    leave
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
    .section    .note.GNU-stack,"",@progbits

В приведенном выше коде следуют $ 1954047348$ 0 - это [] с нулевым окончанием.4 байта после этого - b [].Это означает, что b [] было помещено в стек до a [], так как в этом компиляторе размер стека уменьшается.

Если вы компилируете с -S (или эквивалентным), вы должны увидеть b [] по более низкому адресучем [], так что вы получите strlen (b) = 8.

1 голос
/ 26 июня 2011

Я не получаю такой же вывод, см. Здесь в моем фрагменте ideone: http://ideone.com/zHhHc

:text: sizeof(a)=5, strlen(a)=4
:text

Когда я использую кодовую панель, я вижу вывод, отличный от вас: http://codepad.org/MXJWY136

:text: sizeof(a)=5, strlen(a)=4
:text: sizeof(b)=4, strlen(b)=4

Кроме того, когда я компилирую его в компилятор C ++, я получаю такой же вывод: http://ideone.com/aLNjv

:text: sizeof(a)=5, strlen(a)=4
:text: sizeof(b)=4, strlen(b)=4

Так что что-то определенно не так на вашей платформе и / или компиляторе. Это может быть неопределенное поведение (UB) из-за того, что ваш массив символов не имеет нулевого терминатора (\ 0). Во всяком случае ...

Хотя a и b могут выглядеть одинаково, они не связаны с тем, как вы определили массивы символов.

char a[] = "text";

Как выглядит этот массив в памяти:

----------------------
| t | e | x | t | \0 |
----------------------

Двойные кавычки означают «текстовую строку» и автоматически добавят \ 0 (поэтому размер равен 5). В b вы должны добавить его вручную, но его размер равен 4. strlen() в b выполняет поиск до конца в вашей реализации, что может включать символы мусора. Это большая проблема во многих аспектах безопасности кодирования для массивов символов, которые не имеют нулевого завершения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...