静态字符串文字的内存分配

时间:2015-09-08 17:22:49

标签: c

考虑以下结构:

struct example_t {
char * a;
char * b;
};

struct example_t test {
"Chocolate",
"Cookies"
};

我知道char *的内存分配的实现特定性质,但字符串文字是什么?

在这种情况下,对于“巧克力”和“饼干”的相邻位置,C标准是否有任何保证?

在大多数实现中,我测试了两个文字没有填充,并且直接相邻。

这允许使用memcpy快速复制结构,但我怀疑这种行为是未定义的。有没有人有关于这个主题的任何信息?

3 个答案:

答案 0 :(得分:4)

在您的示例中,两个字符串文字相对于彼此的邻接/放置没有绝对保证。在这种情况下,海湾合作委员会恰好证明了这种行为,但它没有义务表现出这种行为。

在这个例子中,我们看不到填充,我们甚至可以使用未定义的行为来证明字符串文字的相邻性。这适用于GCC,但使用备用libc或不同的编译器,您可以获得其他行为,例如检测翻译单元中的重复字符串文字,并减少冗余以节省最终应用程序中的内存。

此外,虽然您声明的指针属于char *类型,但文字实际应该是const char*,因为它们将存储在RODATA中,并且写入该内存将导致段错误。

代码清单

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

struct example_t {
char * a;
char * b;
char * c;
};


int main(void) {

    struct example_t test = {
        "Chocolate",
        "Cookies",
        "And milk"
    };
    size_t len = strlen(test.a) + strlen(test.b) + strlen(test.c) + ((3-1) * sizeof(char));

    char* t= test.a;
    int i;
    for (i = 0; i< len; i++) {
        printf("%c", t[i]);
    }

    return 0;
}

示例输出

./a.out 
ChocolateCookiesAnd milk

输出gcc -S

    .file   "test.c"
    .section    .rodata
.LC0:
    .string "Chocolate"
.LC1:
    .string "Cookies"
.LC2:
    .string "And milk"
    .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
    pushq   %rbx
    subq    $72, %rsp
    .cfi_offset 3, -24
    movq    $.LC0, -48(%rbp)
    movq    $.LC1, -40(%rbp)
    movq    $.LC2, -32(%rbp)
    movq    -48(%rbp), %rax
    movq    %rax, %rdi
    call    strlen
    movq    %rax, %rbx
    movq    -40(%rbp), %rax
    movq    %rax, %rdi
    call    strlen
    addq    %rax, %rbx
    movq    -32(%rbp), %rax
    movq    %rax, %rdi
    call    strlen
    addq    %rbx, %rax
    addq    $2, %rax
    movq    %rax, -64(%rbp)
    movq    -48(%rbp), %rax
    movq    %rax, -56(%rbp)
    movl    $0, -68(%rbp)
    jmp .L2
.L3:
    movl    -68(%rbp), %eax
    movslq  %eax, %rdx
    movq    -56(%rbp), %rax
    addq    %rdx, %rax
    movzbl  (%rax), %eax
    movsbl  %al, %eax
    movl    %eax, %edi
    call    putchar
    addl    $1, -68(%rbp)
.L2:
    movl    -68(%rbp), %eax
    cltq
    cmpq    -64(%rbp), %rax
    jb  .L3
    movl    $0, %eax
    addq    $72, %rsp
    popq    %rbx
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4"
    .section    .note.GNU-stack,"",@progbits

答案 1 :(得分:1)

不,不保证相邻放置。

实际编译器将它们分开的一种情况是,如果相同的字符串文字出现在不同的位置(作为只读对象),并且启用了字符串组合优化。

示例:

 char *foo = "foo";
 char *baz = "baz";
 struct example_t bar = {
     "foo",
     "bar"
 }

可能最终会以"foo"结尾,然后是"baz",然后是"bar"

答案 2 :(得分:1)

这是一个演示字符串不相邻的真实场景的示例。 GCC决定重用之前的字符串"Chocolate"

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

const char *a = "Chocolate";
const char *b = "Spinach";

struct test_t {
    const char *a;
    const char *b;
};

struct test_t test = {"Chocolate", "Cookies"};

int main(void)
{
    printf("%p %p\n", (const void *) a, (const void *) b);
    printf("%p %p\n", (const void *) test.a, (const void *) test.b);
    return EXIT_SUCCESS;
}

输出:

0x400614 0x40061e
0x400614 0x400626