这里我们来看一下字符串的存储
字符串str1,str2,str3,str4,str7;
整数:startAddress, n1
数组:str5,str6,以及p2
原程序如下
int main()
{
printf("Hello world!\n");
int startAddr = 0x12345678;
int nn1 = 0x12005600;
int nn2 = 0x00120056;
char* str1 = "hello";
//str1[2] = 'a'; //不能访问
char* str2 = "it";
//int* p1 = {1, 2, 3, 4};
int p2[] = {4, 3, 2, 1};
int n1 = 0x78654321;
char* str3 = "";
int n2 = 0x00120034;
char* str4 = "he";
char str5[] = {1, 2 , 3, 4};
char str6[] = "hello";
char* str7 = "llo";
int n3 = 0x78005600;
printf("n: %d, %d, %d\n", n1, n2, n3);
printf("start address: %p\n", &startAddr);
printf("str1 address: %p\n", str1);
printf("str2 address: %p\n", str2);
printf("str3 address: %p\n", str3);
printf("str4 address: %p\n", str4);
printf("str5 address: %p\n", str5);
printf("str6 address: %p\n", str6);
printf("str7 address: %p\n", str7);
//printf("p1 address: %p\n", &(*p1);
printf("p2 address: %p\n", &p2);
printf("strlen str1: %d\n", strlen(str1) );
printf("strlen str2: %d\n", strlen(str2) );
printf("strlen str3: %d\n", strlen(str3) );
printf("strlen str4: %d\n", strlen(str4) );
printf("strlen str5: %d\n", strlen(str5) );
return 0;
}
结果如下图,左侧打印了地址信息,右侧是地址。
从上图可以看出,
起始点的int数据用黑线标记,起始地址后的那些整数和指针以栈的形式从高字节0x0060feec处开始,逐个填入。
起点后蓝线标注整型数据,绿线标注字符串指针数据。
字符串指针仅仅只是存储了一个地址,比如0x00403044,它把这些字符串数据放在了数据区,而不是函数栈内。
字符数组是存储在栈内的,如起始点前面的蓝色部分数据,字符串数组“hello"与字符数组{1,2,3,4}
int整数数组也是存储在栈内,每个数据占据4个字节长度。
但是为什么它没按照我书写定义的顺序存储呢?
数组存储在最后面(低地址),原因是什么呢?被优化了?
最后我们来看看数据区
从对应的地址可以看出,数据区中的确存储了我们字符串指针指向的数据
除此之外,还可以发现,printf函数中传入的参数那些字符串也在数据区中。
扩展:
上述代码的汇编,以及自己的理解,仅供参考
0x00401334 push %ebp
0x00401335 mov %esp,%ebp
0x00401337 push %edi
0x00401338 push %esi
0x00401339 push %ebx
0x0040133A and $0xfffffff0,%esp
0x0040133D sub $0x50,%esp
0x00401340 call 0x4165f0 <__main>
0x00401345 movl $0x12345678,0x4c(%esp) //int startAddr = 0x12345678;
0x0040134D movl $0x12005600,0x48(%esp) //int nn1 = 0x12005600;
0x00401355 movl $0x120056,0x44(%esp) //int nn2 = 0x00120056;
0x0040135D movl $0x46e024,0x40(%esp) //char* str1 = "hello";
0x00401365 movl $0x46e02a,0x3c(%esp) //char* str2 = "it";
0x0040136D lea 0x14(%esp),%edx //int p2[] = {4, 3, 2, 1};
0x00401371 mov $0x46d000,%ebx
0x00401376 mov $0x4,%eax
0x0040137B mov %edx,%edi
0x0040137D mov %ebx,%esi
0x0040137F mov %eax,%ecx
0x00401381 rep movsl %ds:(%esi),%es:(%edi)
0x00401383 movl $0x78654321,0x38(%esp) //int n1 = 0x78654321;
0x0040138B movl $0x46e02d,0x34(%esp) //char* str3 = "";
0x00401393 movl $0x120034,0x30(%esp) //int n2 = 0x00120034;
0x0040139B movl $0x46e02e,0x2c(%esp) //char* str4 = "he";
0x004013A3 movb $0x1,0x10(%esp) //char str5[] = {1, 2 , 3, 4};
0x004013A8 movb $0x2,0x11(%esp)
0x004013AD movb $0x3,0x12(%esp)
0x004013B2 movb $0x4,0x13(%esp)
0x004013B7 movl $0x6c6c6568,0xa(%esp) //char str6[] = "hello";
0x004013BF movw $0x6f,0xe(%esp)
0x004013C6 movl $0x46e031,0x28(%esp) //char* str7 = "llo";
0x004013CE movl $0x78005600,0x24(%esp) //int n3 = 0x78005600;
0x004013D6 mov $0x0,%eax //return 0;
0x004013DB lea -0xc(%ebp),%esp
0x004013DE pop %ebx
0x004013DF pop %esi
0x004013E0 pop %edi
0x004013E1 pop %ebp
0x004013E2 ret
从中可以看到,首先对于数组,它与普通的数据并不存储在一起。
首先编译器应该知道main函数中数组占用空间大小len(sets)以及非数组成员的大小len(non)。通过len(sets)与len(non)的和确定main函数栈空间的大小,当碰到数组时,从len(non)的偏移往后分配空间。
最后,字符数组直接实现赋值,而对于int型数组,则是先将其值放在了数据区。最后从数据区复制到额外区