C专家编程 第6章 运行的诗章:运行时数据结构 6.2 段

    段
    在绝大多数SVr4实现中都采用了一种称作ELF(原意为Extensible Linker Format[可扩展链接器格式],现在表示Executable and Linking Format[可执行文件和链接格式]的格式,在其他系统中,可执行文件的格式是COFF(Common Object-FILE Format,普通目标文件格式)。在BSD UNIX中,a.out文件具有a.out格式。可以通过输入man a.out在主文档中查看有关UNIX系统所使用的格式的信息。

    所有不同的格式具有一个共同的概念,那就是段(segment)。但就目标文件而言,它们是二进制文件中简单的区域,里面保存了和某种特定类型(如符号表条目)相关的所有信息。术语section也被广泛试用,section是ELF文件中的最小组织单位。一个段一般包括几个section.

    在UNIX中,段表示一个二进制文件相关的内容块。
    在Intel x86的内存模型中,段表示一个设计的结果。在这种设计中(基于兼容性原因),地址空间并非一个整体,而是分成一些64KB大小的区域,称之为段。
    在本书的剩余部分,如果不做特殊说明,段这个术语是指UNIX上的段。

    当在一个可执行文件中运行size命令时,它会告诉你这个文件中的3个段(文本段、数据段和bss段)的大小:
    %echo; echo "text data bss total"; size a.out
    size命令并不打印标题,所以要用echo命令产生它们。
    检查可执行文件的内容的另一种方法是使用nm或dump实用工具。编译下面的源文件,在结果的a.out文件上运行nm程序。 
    char pear[40];
    static double peach;
    int mango = 13;
    static long melon = 2001;

    int main() {
        int i = 3, j, *ip;
        ip = malloc(sizeof(i));
        pear[5] = i;
        peach = 2.0 * mango;
        return 0;
    } 
    nm程序运行结果的摘要如下
    %nm -sx a.out
    Symbols from a.out
    [Index] Value  Size  Type    Bind    Segment Name
                                    OBJT  LOCL  .bss         peach
                                    OBJT  GLOB .bss          pear
                                    OBJT  GLOB .data        mango
                                    OBJT  LOCL  .data        melon
                                    FUNC GLOB  .text         main
                                    FUNC GLOB  UNDEF   malloc
                     
    编译器和链接器分别在这些段中写入了什么东西
    关键: 
    这个类型的C源文件---------------->(通过编译器编译)进入这个段
    源文件                                                                                  a.out文件                
    char pear[40];(OBJT GLOB BSS) (1)                               a.out神奇数字 
    static double peach(OBJT LOCL BSS)(2)                        a.out的其他内容 
                                                                                             BSS段所需的大小(1)(2)
    int mango = 13;(OBJT GLOB DATA)    (3)                       数据段 
    static long melon = 2001;(OBJT LOCL DATA)(4)                  初始化后的全局变量和静态变量                                                                                                        (3)(4)

    main() {(FUNC GLOB TEXT)                                           文本段 
        int i = 3, j, *ip(局部变量并不进入a.out,它们在运行时创建)  可执行文段的指令(5)(6)(7) 
    
        ip = malloc(sizeof(i));(FUNC GLOB UNDEF)(5)
        pear[5] = i;(6)
        peach = 20 * mango;(7)
    } 
                                   图6-1       C语言的各部分会出现在那些段中

    BSS段这个名字是“Block Started by Symbol”(由符号开始的块)的缩写,它是旧式IBM 704汇编程序的一个伪指令,UNIX借用了这个名字,至今依然沿用。有些人喜欢把它记做“Better Save Space”(更有效的节省空间)。运行时所需要的BSS段的大小记录在目标文件中,但BSS段(不像其他段)并不占据目标文件的任何空间。

    /*
    **编程挑战
    */
    1.编译hello world程序,在可执行文件中执行ls -l,得到文件的总体大小。运行size得到文件里各个段的大小。
    解析:
    由于电脑是window系统,所以这里采用dir执行UNIX中ls命令的功能。 
    /*program.c*/
    #include <stdio.h>
    #include <stdlib.h>
    
    int main( void ){
        printf( "%s\n", "hello, world!");
    
        return EXIT_SUCCESS;
    }

输出:

     2.增加一个全局的int[1000]数组声明,重新进行编译,再用上面的命令得到总体及各个段的大小,注意前后的区别。 
    解析:
    由于电脑是window系统,所以这里采用dir执行UNIX中ls命令的功能。 

    /* program_2.c */     
    #include <stdio.h>
    #include <stdlib.h>
    int a[1000];
 
    int main( void ){
        printf( "%s\n", "hello, world!");
     
        return EXIT_SUCCESS;
    }

输出:

    3.现在,在数组的声明中增加初始值(记住,C语言并不强迫对数组进行初始化时为每个元素提供初始值),这将使数组从BSS段转换到数据段。重复上面的测量,注意各个段前后大小的区别。 
    解析:
    由于电脑是window系统,所以这里采用dir执行UNIX中ls命令的功能。 

    /* program_3.c */
    #include <stdio.h>
    #include <stdlib.h>
    int a[1000] = {1};
 
    int main( void ){
        printf( "%s\n", "hello, world!");
     
        return EXIT_SUCCESS;
    }

输出:

    4.现在,在函数体内声明一个巨大的数组。然后再声明一个巨大的局部数组,但这次加上初始值。重复上面的测量。定义于函数内部的局部数组存储在可执行文件中吗?有没有初始化有什么不同吗? 
    解析:
    由于电脑是window系统,所以这里采用dir执行UNIX中ls命令的功能。 

    /* program_4.c */
    #include <stdio.h>
    #include <stdlib.h>
    int a[1000] = {1};
 
    int main( void ){
        int b[1000]; 
        printf( "%s\n", "hello, world!");
     
        return EXIT_SUCCESS;
    } 

输出: 

    /* program_5.c */
    #include <stdio.h>
    #include <stdlib.h>
    int a[1000] = {1};
 
    int main( void ){
        int b[1000]; 
        int c[1000] = {2};
        printf( "%s\n", "hello, world!");
     
        return EXIT_SUCCESS;
    }

输出:

结果说明定义于函数内部的局部数组无论是否进行了初始化都没有存储于可执行文件中。
    5.如果在调试状态下编译,文件和段的大小有没有变化?是为了最大限度的优化吗?
    分析上面“编程挑战”的结果,使自己确信:
    *数据段保存在目标文件中;
    *BSS段不保存在目标文件中(除了记录BSS段在运行时所需要的大小);
    *文本段是最容易受优化措施影响的段;
    *a.out文件的大小受调试状态下编译的影响,但段不影响。 

    /* program.c */
    #include <stdio.h>
    #include <stdlib.h>
 
    int main( void ){
        printf( "%s\n", "hello, world!");
     
        return EXIT_SUCCESS;
    }

输出:

    /* program_6.c */    

    #include <stdio.h>
    #include <stdlib.h>
    int a[1000];
    int a2[1000] = {1};
 
    int main( void ){
        int b[1000]; 
        int c[1000] = {2};
        printf( "%s\n", "hello, world!");
     
        return EXIT_SUCCESS;
    }

输出:

我的操作系统是windows,这里保存了bss段和data段保存到了目标文件中,跟在UNIX系统中的结果不一样。

    /* program_7.c */

    #include <stdio.h>
    #include <stdlib.h>
    int a[1000];
    int a2[1000] = {1};
 
    int main( void ){
        int b[1000]; 
        int c[1000] = {2};
        int i;
        for( i = 0; i < 10; ++i ){
            printf( "%d ", b[i] );
        } 
        printf( "%s\n", "hello, world!");
     
        return EXIT_SUCCESS;
    }

输出:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_40186813

你的能量无可限量。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值