开发者

memory allocation for string and array of char

I can't understand how memory is allocated in the following code:

#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;
}

The output is

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

By looking into memory addresses and the output code it seems that variable b is placed before variable a, and that's why strlen(b), by looking for \0, returns 8. Why does this happen? I expected variable a to be declared first.


The language makes no guarantees about what is placed where. So, your experiment make very little sense. It might work, it might not. The behavior is undefined. Your b is not a string and it is UB to use strlen with something that is not a string.

From the purely practical point of view though, local variables are usually allocated on the stack, and the stack on may moderns platforms (like x86) grows backwards, i.e. from higher addresses to lower addresses. So, if you are using one of these platforms, it is possible that your compiler decided to allocate variables in the order of their declaration (a first and b second), but because stack grows backwards b ended up at lower addresses in the memory than a. I.e. b ended up before a in memory.

One can note though that a typical implementation does not normally allocate stack space for local variables one-by-one. Instead, the entire block of memory for all local variables (stack frame) is allocated at once, meaning that the logic I described above does not necessarily apply. Yet, it is still possible that the compiler still follows the "reverse" approach to local variable layout anyway, i.e. variables declared earlier are placed later in the local memory frame, "as if" they were allocated one-by-one in the order of their declaration.


Your "b" character array is not null-terminated. To understand consider that the char a[] declaration is equivalent to:

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

In otherwords strlen(b) is undefined, it just looks through random memory for a NULL character (0 byte).


I do not get the same output see here on my ideone snippet: http://ideone.com/zHhHc

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

When I use codepad, I see different output than you: http://codepad.org/MXJWY136

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

Also, when I compile it a C++ compiler, I get the same output: http://ideone.com/aLNjv

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

So something is definitely wrong on your platform and/or compiler. It could be undefined behavior (UB) due to the fact that your char array does not have a null-terminator (\0). At any rate...

While both a and b may look the same, they are not due to how you have defined the character arrays.

char a[] = "text";

What this array looks like in memory is the following:

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

The double quotes mean "text string" and will add the \0 automatically (that's why the size is 5). In b, you have to add it manually but the size is 4. The strlen() in b is searching until end in your implementation, which could include garbage characters. This is a big problem in many security aspects of coding for char arrays that are not null terminated.


I compiled your code on Linux/x86 with GCC using the -S flag to see assembly output. That shows that for me, b[] is allocated at a higher memory address than a[], so I didn't get 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

In the code above, $1954047348 followed by $0 is a[] with the null termination. The 4 bytes after that are b[]. This means b[] was pushed on the stack before a[] since the stack grows down on this compiler.

If you compile with -S (or equivalent), you should see b[] at a lower address than a[], so you'll get strlen(b)=8.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜