字符串和char数组的内存分配

时间:2011-06-25 21:37:09

标签: c string memory-management

我无法理解如何在以下代码中分配内存:

#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 个答案:

答案 0 :(得分:7)

语言不保证放在哪里。所以,你的实验没什么意义。它可能会起作用,也可能不起作用。行为未定义。您的b不是字符串,使用strlen的字符串不是字符串是UB。

从纯粹实际的角度来看,局部变量通常在堆栈上分配,而堆栈可能在现代平台(如x86)上向向后,即从较高地址到较低地址。因此,如果您正在使用这些平台之一,您的编译器可能会按照其声明的顺序(a第一个和b秒)决定分配变量,但因为堆栈向后增长{{ 1}}最终在内存中的地址低于b。即a在内存中 b之前结束了

可以注意到,典型的实现通常不会逐个为局部变量分配堆栈空间。相反,立即分配所有局部变量(堆栈帧)的整个存储器块,这意味着上面描述的逻辑不一定适用。然而,编译器仍然可能仍然遵循局部变量布局的“反向”方法,即先前声明的变量稍后放置在本地存储器帧中,“好像”它们是按顺序逐个分配的。他们的声明。

答案 1 :(得分:2)

您的“b”字符数组不以空值终止。要理解char a []声明等同于:

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

换句话说,strlen(b)是未定义的,它只是通过随机存储器查找NULL字符(0字节)。

答案 2 :(得分:1)

我没有得到相同的输出,请参阅我的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),因为您的char数组没有空终止符(\ 0)。无论如何......

虽然a和b看起来都一样,但它们并不是由于你如何定义字符数组。

char a[] = "text";

这个数组在内存中的样子如下:

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

双引号表示“文本字符串”并自动添加\ 0(这就是为什么大小为5)。在b中,您必须手动添加它,但大小为4. b中的strlen()正在搜索,直到您的实现结束,其中可能包含垃圾字符。在编码非空终止的char数组的许多安全方面,这是一个大问题。

答案 3 :(得分:1)

我使用-S标志使用GCC在Linux / x86上编译代码以查看程序集输出。这表明对我来说,b []被分配在比[]更高的内存地址,所以我没有得到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 []在[]之前被压入堆栈,因为堆栈在此编译器上增长。

如果用-S(或等效的)编译,你应该看到b []的地址低于[],所以你得strlen(b)= 8。