运行时的C程序

数据和代码

编程语言理论经典对立之一就是代码和数据的区别,有些语言如LISP把两者视为一体,其他语言如C语言则维持两者的区别。编译绝大部分工作都跟翻译代码有关,必要的数据存储管理的绝不部分都在运行时进行。

学习运行时可以有三个好处,有助于优化代码,获得最佳效率,有助于理解更高级的材料,陷入麻烦的时候,可以更容易的分析问题。

段(Segments)

ELF:(原意为Extensibla Linker Format,可扩展链接器格式,现在代表Executable and Linking Format,可执行文件和链接格式),存在于绝大多SVr4实现。

COFF:(Common Ojbect-File Format,普通目标文件格式),存在于其他系统中。

上述两种不同的格式有一个共同的概念,段,段的概念和很多都相关,单就目标文件而言,段就是指二进制文件中简单的区域,里面保存了和某种特定类型(如符号表条目)相关的信息。术语Section也被广泛使用,section是ELF文件中的最小组织单位,一个段一般包含几个section。

上述段的概念是UNIX中的,和Intel X86架构的段完全没有关系。UNIX中段就表示一个二进制文件的内容块,intel X86中段表示一种设计的结果,在这种设计中,地址空间并非一个整体,而是分成64k大小的区域,称之为段,这种设计也是情非得已,为了向下兼容曾经的芯片。

a.out的结构

下图显示了编译器和连接器分别在这些段中写入了什么东西

BSS段是“Block Started by Symbol(由符号开始的块)”的缩写,它是旧式IBM704汇编程序的一个伪指令,UNIX借用了这个名字,至今仍然沿用。有些人喜欢把它记作“Better Save Space(更有效的节省空间)”,由于BSS段只保存没有值得变量,所以事实上并不需要保存这些变量的映象。运行时所需要的BSS段的大小记录在目标文件中,但BSS段不像其他段,它并不占用目标文件的任何空间

可以使用size命令和nm程序来查看程序各个部分的大小

操作系统之于a.out

为什么要用段来组织,因为段可以方便的映射到链接器,在运行时可以直接载入,载入器只需要提取文件中每个段的映像。段在正在执行的程序中是一块内存区域,每个区域有特定的目的。

文本段包括程序的指令

数据段包括经过初始化的全局变量和静态变量以及它们的值,BSS段的大小从可执行文件中得到,然后链接得到这个大小的内存块,紧跟数据块之后,这块内存进入程序后全部清零。

C语言运行时系统之于a.out

运行时数据结构包括,堆栈、活动记录、数据和堆等

  • 堆栈段:主要有三个用途,和函数以及表达式有关。除了递归调用,堆栈并不是必须的。在大多数CPU中,堆栈是向下增长的,也就是朝着低地址方向生长。

    • 为函数内部的局部变量提供存储空间
    • 函数调用过程中,保持一些维护性信息,称为堆栈结构或者过程活动记录
    • 暂存区,很长的算数表达式

和C++不同,C运行时函数个个短小精悍,使得C很高效

C语言不允许在函数中定义函数,也就是函数不能嵌套定义。

setjmp和longjmp

以上两个命令是C语言独有的,通过操纵过程活动记录实现的。

  • setjmp(jmp_buf j)必须首先被调用。它表示使用变量j记录现在的位置,函数返回0;
  • longjmp(jmp_buf j,int i)可以接着被调用,它表示回到j所记录的位置,让他看上去像刚从setjmp函数返回一样。但是函数返回i,使代码知道它实际上是通过longjmp返回的。
  • 当使用longjmp时,j的内容被销毁
    • setjmp保存了一份程序计数器和当前的栈顶指针,也可以保存一些初始值,longjmp恢复这些初始值。
    • 和goto不同,第一,goto不能跳出C语言的函数;第二,longjmp只能回到曾经到过的地方。
  • 需要注意的是,保证局部变量在longjmp过程中一直保持它的值得唯一可靠办法就是把它声明为volatile。
  • 两者组合最大用途是错误恢复,只要还没从函数返回,一旦发现一个不可恢复的错误,就可以把控制流转移到主输入循环。可以用它来从一串很深的代码中立即返回,提防潜在的危险代码。目前C++已经支持了异常系统
switch(setjmp(jbuf))
{
    case 0:
          apple = *suspicious;
          break;
    case 1:
          printf("suspicious is a bad pointer\n");
          break;
    default:
          die("unexpected value returned bt setjmp");
}
  • 和goto一样,不是必要的情况下不要使用它们。

UNIX和MS-DOS的堆栈段

  • 在UNIX中,当进程需要更多空间的时候,堆栈会自动生长。
  • DOS中,在建立可执行文件时,堆栈大小必须同时确定,而且它不能在运行时增长。Stack overflow是常见的堆栈溢出错误。

有用的C语言工具

Reference

C专家编程

时间: 2024-10-07 14:48:10

运行时的C程序的相关文章

C Runtime C运行时

顾名思义,C运行时是C程序运行时的环境,简称CRT C运行时主要包括以下几个部分: 1)引导程序(main)的入口函数和退出函数以及其依赖的各种函数 2)C的标准函数的实现 3)I/O功能的封装和实现 4)堆的封装和实现 5)C语言的特殊功能的实现 6)调试功能 7)其他 运行时(Runtime)是平台相关的,这里的平台指的是操作系统 它可以被理解成是C语言程序和不同操作系统平台的抽象层 * 并不是所有的C程序的操作都必须经由CRT实现 * CRT中并不都是C语言定义的操作

解决IntelliJ IDEA控制台乱码问题[包含程序运行时的log4j日志以及tomcat日志乱码]

这里使用的IntelliJ IDEA版本为[IntelliJ IDEA 14.1.4]: 一.控制台打印的程序运行时的log4j日志中包含中文乱码 在IDEA安装目录的bin目录下找到名为"idea.exe.vmoptions"的文件: 使用文本编译软件(Notepad++等)打开此文件,在文件内容从末尾追加一行设置(-Dfile.encoding=UTF-8),表示指定编码为UTF-8: 重启IDEA,再次测试,log4j日志不再乱码: 但是发现tomcat启动日志乱码了(修改IDE

请给出程序,功能为获取“一段程序代码”运行时长

有一天看到了几个java题目,我就写了一下,废话少说,直接上代码了 3.已知int型数组arr[t], 请:① 打印该数组.输出格式为:[arr1,arr2,arr3,...]. ② 输出数组的最大值max与最小值min. ③ 使用冒泡排序对数组进行排序,并打印排序后数组. 4. 请给出程序,功能为获取“一段程序代码”运行时长.要求: ① 程序框架在父类中定义. ② “一段程序代码”在子类中给出,并覆盖父类中对应方法. ③ “一段程序代码”为:打印一个由“*”组成的图形,见图1. 图1 第三题:

使用Mono打造轻量级的.NET程序运行时

??在使用Mono让.NET程序跨平台运行这篇文章中,我们已经对Mono以及.NET程序的运行机制有了初步的理解.今天我想来谈谈"使用Mono打造轻量级的.NET运行时"这样一个话题.为什么我会有这样一种想法呢?因为Mono和.NET都可以执行IL代码,所以我用Mono来作为.NET程序的运行时是一个顺理成章的想法.由于.NET程序需要.NET Framework提供运行支持,所以当目标设备没有安装.NET Framework或者.NET Framework版本不对的时候,我们的程序都

Java程序运行时,数据都保存到什么地方?

程序运行时,我们最好对数据保存到什么地方做到心中有数.特别要注意的是内存的分配.有六个地方都可以保存数据: 寄存器 这是最快的保存区域,因为它位于和其他所有保存方式不同的地方:处理器内部.然而,寄存器的数量十分有限,所以寄存器是根据需要由编译器分配.我们对此没有直接的控制权,也不可能在自己的程序里找到寄存器存在的任何踪迹. 堆栈 驻留于常规RAM(随机访问存储器)区域,但可通过它的"堆栈指针"获得处理的直接支持.堆栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存.这是一种特别

Java注解(2)-注解处理器(运行时|RetentionPolicy.RUNTIME)

如果没有用来读取注解的工具,那注解将基本没有任何作用,它也不会比注释更有用.读取注解的工具叫作注解处理器.Java提供了两种方式来处理注解:第一种是利用运行时反射机制:另一种是使用Java提供的API来处理编译期的注解. 反射机制方式的注解处理器 仅当定义的注解的@Retention为RUNTIME时,才能够通过运行时的反射机制来处理注解.下面结合例子来说明这种方式的处理方法. Java中的反射API(如java.lang.Class.java.lang.reflect.Field等)都实现了接

公共语言运行时 CLR

公共语言运行时(Common Language Runtime,CLR)是处于.NET核心Framework的面向对象引擎,其将各种语言编译器生成的中间代码翻译为执行应用程序所需的原生码(Native Code). 由于CLR将所有代码转换成公共的交互式语言,之后被编译成原生码.所以,从原则而言,.NET可以在Unix.Linux.Mac OS X或其他操作系统上实现. CLR在Web服务器上执行程序.CLR激活对象,对它们进行安全检查,在内存中创建它们,执行它们,并且进行垃圾回收.

iOS App 的运行时

App被启动时,从非运行状态到短暂的非激活状态,然后切换到运行状态或者后台运行状态.在启动过程中,操作系统对App创建了一个主线程来调用main方法. main方法是App的入口,用来调用UIKit框架和做一些程序运行前的预处理.XCode项目模板自动生成了mian方法,调用UIApplicationMain iOS也有自动内存管理,ARC(Automatic Refenerce Counting),@autoreleasepool中的代码的内存管理被ARC托管 App在后台运行时,会监听一些后

Android运行时ART加载OAT文件的过程分析

在前面一文中,我们介绍了Android运行时ART,它的核心是OAT文件.OAT文件是一种Android私有ELF文件格式,它不仅包含有从DEX文件翻译而来的本地机器指令,还包含有原来的DEX文件内容.这使得我们无需重新编译原有的APK就可以让它正常地在ART里面运行,也就是我们不需要改变原来的APK编程接口.本文我们通过OAT文件的加载过程分析OAT文件的结构,为后面分析ART的工作原理打基础. 老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注! OAT文件