我无法理解如何在以下代码中分配内存:
#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首先被声明。
答案 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。