const char helloworld[] = {'H', 'e', 'l', 'l', 'o', ' ','w', 'o', 'r', 'l', 'd', ' ', '%', 'd', '\', 'n'}; 最后应该是'\n'而不是'\', 'n'吧。。
谢谢指出!
gcc -S main.c 取main.s如下片段: 26 .section .rodata 27 .LC1: 28 .string "hello world %d\n" 29 .LC0: 30 .string "hello world.\n" 可以看到字符串是放在rodata段中的。 而你的书中却写的是: 给b初始化用的这个字符串"Hello world"并没有分配在.rodata段 为什么?
我也不知道为什么,在我这里编译成汇编,确实没有 29 .LC0: 30 .string "hello world.\n" 这个字符串。你的程序跟我书上的不一样吧。我书上的程序是char b[] = "Hello world";,明明没有\n,或者是编译器版本不同,我的是gcc 4.3.3
我用这一节的例程: 下面的是编译器版本: gcc version 4.2.4 (Ubuntu 4.2.4-1ubuntu4) gcc -S main.c 代码片段如下: 26 .section .rodata @这句指示下面是分配在rodata段中的 27 .LC1: 28 .string "Hello world %d\n" 29 .LC0: 30 .string "Hello world"
衫哥怎么不介绍下常量在内存中的布局?只讨论变量在内存和程序中的布局? "5"、'5'、5 ------------------------------ 这3个表达式如果出现在程序里都是表示常量吧,在elf文件里分别在那个段里? 好像字符串 常量是放在.rodata段里的,不在代码段里。 如 int a =0x123; 生成的指令 是mov % 0x123,eXXXX 0x123,这个数字常量在代码段里,可你在刚说linux汇编入门时提到,0x123是立即数,由cpu内部直接产生, 意思就是 0x123,不是从内存里的代码段里读取的?怎么理解?
我用的是gcc version 4.3.3 (Ubuntu 4.3.3-5ubuntu4),编译生成的代码中并没有 .LC0: .string "Hello world" 如果用你的gcc版本,那么char b[] = "Hello world";这一句编译之后是如何初始化的?是直接把字符的ASCII码写死在指令中呢?还是从.rodata段的.string "Hello world"拷贝? 这只是一个细节问题。不管是哪种方式,最终数组b都在栈上分配,初始数据都相同,所以不影响本节的结论。
回复linux——00xx00xxooxx: 虽然是你的理解有偏差,但你提的问题很典型,我将会在后续版本中仔细解释这个问题。简单说,String literal有内存位置,而'5'和5是constant,没有内存位置,constant是直接写在指令中的。写在指令中的数是CPU取指令取上来的,在执行时由CPU内部生成这个数,而不是由读写内存的指令读上来直接保存在寄存器中。
19.3中,"注意,虽然栈是从高地址向低地址增长的,但数组总是从低地址向高地址排列的", 里面的“栈”应该指的是“堆栈”吧。 堆栈(stack)是从高地址向低地址增长, 栈(heap)是从低地址向高地址增长的。是这样的么?
不好意思,是我弄错了。 你这里的“栈”指的就是 stack , “堆”就是 heap 。
我把程序修改一下, 用来观察变量的存储布局非常直观。 hexdump -C a.out #include <stdio.h> const int A = 0x11111111; int a = 0x22222222; static int b = 0x33333333; int c; int main(void) { static int a = 0x44444444; char b[] = "aabbccdd"; register int c = 0x55555555; printf("aaaaaaaaaaa %d bbbbbbbbbbbbb\n", c); return 0; }
我也来谈谈char b[] = "Hello world";这个语句的编译器实现问题。 从字面上可以理解为用字符串常量"Hello world"来初始化数组b。由于数组的元素是可写的,这里的初始化一定是值拷贝的,而不是指针赋值。(注意与char *str = "Hello world"的区别) 既然是值拷贝,以后对数组b的操作跟"Hello world"毫无关系,假设后面的代码不再引用这个字符串常量(本人猜想是非必要条件),那么完全可以用立即数的方式来初始化数组b,也就不需要在.rdata中存放"Hello world"了。(彬兄的情况就属于这个) 当然,这应该是由编译器决定的,本人的环境(gcc (GCC) 4.1.2 20071124 (Red Hat 4.1.2-42))下是有"Hello world"这个字符串常量的: 0x08048395 <main+17>: mov 0x80484b4,%eax 0x0804839a <main+22>: mov %eax,0xfffffff0(%ebp) 0x0804839d <main+25>: mov 0x80484b8,%eax 0x080483a2 <main+30>: mov %eax,0xfffffff4(%ebp) 0x080483a5 <main+33>: mov 0x80484bc,%eax 0x080483aa <main+38>: mov %eax,0xfffffff8(%ebp) ... (gdb) x/12bs 0x80484b4 0x80484b4 <A+20>: "Hello world" 前面说了,假设后面的代码不再引用这个字符串常量"Hello world", 那么要是后面的代码引用了呢?加入代码改为如下: int main(void) { static int a = 40; char b[] = "Hello world"; register int c = 50; printf("Hello world"); /*再次使用"Hello world"*/ return 0; } 我猜想,这个代码在彬哥的环境下,就不会用立即数给数组b赋值了吧。(要是用立即数赋值的话,其实也没问题,殊途同归而已。)
我的名字是杉不是彬,谢谢
则那个b和这个b不代表同一个变量。具有Internal Linkage的标识符编译后在符号表中是LOCAL的符号,但main函数里面那个a不能算Internal Linkage的,因为即使在同一个程序文件中,在不同的函数中声明多次,也不代表同一个变量。 代表不同变量
如果您有建设性意见,哪怕只是纠正一个错别字,也请不吝赐教,您留下的姓名和email将会出现在本书前言的致谢中。再次感谢您的宝贵意见!