C 语言精华

 

操作系统的虚拟内存。(见文章尾)

 

类型转换:有符号无符号的类型转换注意不同类型的转换方式。长度更长,精度更高的方向转换。

sizeof判断的依据:根据变量的类型判断,属于运算符,而不是函数。编译时,sizeof类型的大小就已经确定。

宏定义和函数的区别:宏定义只是在预处理阶段进行符号替换。

书写规范的宏定义:完备的括号

static类型的函数和变量的特点

static函数只能在本源文件内被调用;static全局变量,只能在本源文件内被使用。因此他们都可以在不同源文件中重复定义而不会冲突(他们占用不同的内存空间)。static局部变量,生命周期在程序运行期间都有效。(因此可以把static局部变量的地址作为返回值。例如getenv

如何看懂复杂的变量或函数声明:从左向右找到第一个标识符,该标志符就是我们要分析的符号。以该标识符为中心,从内向外分析。先分析该标识符的右边,若是()[],表明他是个函数或数组;然后分析该标识符的左边,例如,*或类型说明符。

对于constvolatile等限定符,判断它限定的是什么类型。先看限定符的右边,若右边是类型说明符比如char,则它限定的是右边的类型,否则他限定的是左边的类型。

restrictc99才支持的,形参指针用restrict修饰的函数,告诉编译器在该函数内只通过这一个指针引用他所指的内存,即,告诉编译器对该指针的操作可以代码优化。编译时需要在gcc后面加 -std=c99

volatile用该符号限定变量,是告诉编译器每次使用到变量的时候,都要从内存里去取它的值。对于那些值容易变化的变量,需要加这个限定符。通常用在多线程编程中

restrictvolatile都是为了通知编译器生成相应的机器指令。

对齐规则C语言标准未严格规定该如何对齐。因此在不同的系统对齐规则略有不同。通常,基本类型chardouble等,的首地址是该类型大小的倍数。结构体对齐规则,按结构体成员中类型大小最大的对齐。比如linux/IA32下单个double变量都是8字节对齐(无论全局变量还是局部变量),当在结构体里时,若有类型大小小于8的成员,结构体及在结构体内的类型最大以4字节对齐。

因为有对齐规则,所以会出现为了对齐两个不同类型的变量之间会有填充。结构体中通常可以按类型从大到小或从小到大的顺序排列。

 

大端小端模式:一个基本类型的字节序的先后顺序的差异。产生原因:网络传输数据的需要。intelarm架构都使用小端模式。

 

位操作:熟悉常用的位操作

 

指针和数组的异同

 

 

栈的典型分布(《深入理解计算机系统》8.4.5节)

 

 

 

 

函数栈的特点:一块连续的地址空间,实现了“后进先出”的结构。类似于叠在一起的盘子,只能从顶部取出。

栈段的用途:

1)        栈为函数内部声明的局部变量提供存储空间

2)        进行函数调用时,栈存储与此相关的一些维护性信息。这些信息称为栈结构或栈帧(即是上面例子里的盘子)。这些信息包括函数调用地址(即当所调用的函数结束后跳回的地方)、任何不适合装入寄存器的参数以及一些寄存器值的保存。

3)        栈可被用作暂时存储区。有时候程序需要一些临时存储,比如计算一个很长的算术表达式时,可以把中间结果暂时存储到栈中,用的时候再取出。

 对于函数调用的分析,有必要知道系统如何利用栈,来跟踪哪些函数调用了哪些函数,当函数执行return语句后,控制将返回到哪里。当产生函数调用时(例如调用函数fun),系统给被调用的函数fun)分配一个栈帧,在这里可以记录上面提到三方面的信息。

 

 

 

对于下面的代码,当函数caller调用swap_add时,caller的函数栈空间如右图所示。

 

 

为何不能从函数中返回一个指向该函数局部变量的指针。

若想返回一个指向在函数内部定义的变量的指针,要把那个变量声明为static。这样该静态局部变量就的存储位置在数据段中而不是栈中了。(在高级编程中有一些函数会使用此法来返回指向静态局部变量的值。)

 

如何分析递归程序

知道每次递归都重新给局部变量分配空间,局部变量的初始化操作在运行时进行。而静态变量的初始化操作在编译时。

 

例子演示

int a=5;

void fun()

{

        int b;

        printf("局部变量 b的地址: %d\n",&b);

        --a;

        if(a>0)

                  fun();

}

void a(int i){

        if(i>0)

                  a(--i);

        else

                  printf("i has reached zero\n");

        return;

}

void main(){

        a(1);

}

 

 

------------------------

内存泄露

 

导致段错误的几个原因

1)        解除引用一个包含非法值的指针

2)        解除引用一个空指针(常常由于从系统程序返回空指针,并未经检查就是用)

3)        未得到正确的权限时进行访问(例如,试图往一个只读的文本段存储值就会引起段错误)

4)        用完了堆和栈的空间(虚拟内存虽然大但绝非无限)

(《深入理解计算机系统》第九章9.11C专家编程》第七章)

 

虚拟内存

 

由存储单元(字节或字)组成的一维连续的地址空间,简称内存空间。用来存放当前正在运行程序的代码及数据。

逻辑地址(相对地址,虚拟地址):用户的程序经过汇编或编译后形成目标代码,目标代码通常采用相对地址的形式。CPU执行指令时看到的是逻辑地址。

Ø        其首地址为0,其余指令中的地址都相对于首地址来编址。

Ø        不能用逻辑地址在内存中读取信息。

物理地址(绝对地址,实地址):物理内存中存储单元的地址。物理地址可直接寻址。

地址映射:将用户程序中的逻辑地址转换为运行时由机器直接寻址的物理地址。

当程序运行,即需要装入内存时,操作系统要为该程序分配一个合适的内存空间,由于程序的逻辑地址与分配到内存物理地址不一致,所以当cpu访问内存时,先要进行地址转换。

页表:记录逻辑地址和物理地址的对应关系。

内存管理单元MMU:该硬件读取页表,进行地址映射和内存保护。

 

 

程序的虚拟地址范围与系统有关。这个范围的大小由CPU的地址总线位数决定,例如一个32位地址总线的CPU,它可寻址的地址范围是0~0xFFFFFFFF (4G),而对于一个64位的CPU,它的地址范围为0~0xFFFFFFFFFFFFFFFF (64T)。这个范围就是我们的程序能够使用的地址范围,我们把这个地址范围称为虚拟地址空间,该空间中的某一个地址我们称之为虚拟地址。对于一台内存为256M32bit x86主机来说,它的虚拟地址空间范围是0~0xFFFFFFFF4G,而物理地址空间范围是0x000000000~0x0FFFFFFF256M)。

现代的操作系统都使用一种称为分页(paging)机制。虚拟地址空间划分成称为页(page)的单位,而相应的物理地址空间也被进行划分,单位是页桢(frame)。页和页桢的大小必须相同。例如,若页的大小为4K,则页桢大小也为4K——这点是必须保证的,因为内存和外围存储器(磁盘等)之间的传输总是以页为单位的。对应4G的虚拟地址和256M的物理存储器,他们分别包含了1M个页和64K个页桢。页表就是记录页与物理内存的页帧的对应关系的。注意:并不是每个页都对应一个页帧。若一个页不对应任何页帧,则该页不可访问,试图去访问就会出现常见的段错误。例如,linux系统保证对0号页不对应任何页帧,即虚地址0X0不对应任何物理地址,因此对虚地址0X0的访问会出现段错误。

 

 

每个进程都有独立的虚拟地址空间。32bit x86主机来说,每个进程都有4G的虚拟地址空间。这个功能是通过页表(将不同进程相同的虚拟地址映射到不同的物理地址上)来实现的。例如,进程A在地址0X8049000写一个字符’m’,进程B同样在地址0X8049000写字符’n’,如果A进程读地址0X8049000,读到的是进程A写入的’m’,而进程B读取地址0X8049000时,则读到的是B写入的’n’。两者不会冲突。这里,进程A的虚拟地址0X8049000和进程B的虚拟地址0X8049000对应的物理地址不同,因此他们之间没有干扰。

 

 

进程的4G虚拟地址空间又分为用户空间和系统空间。

用户空间:0~0Xbfffffff的地址空间,用于存放用户的程序和数据。

系统空间:0xc0000000~0xffffffff的地址空间,用于存放内核和内核数据。

下面是一个linux4G空间的典型布局。其中阴影部分代表未被使用。(《深入理解计算机系统》第九章)

 

 

 

 

下面对各个段介绍。linux系统对不同的段赋予不同的权限。

 

.text文本段:包含程序的指令,这个段是可读和可执行的。在32位系统中,文本段固定存放在虚拟地址0X8048000开始的地方。

.data已初始化数据段存放已经初始化的全局变量和静态变量。

.bss未初始化数据段存放未初始化的全局变量和静态变量。

.data段和.bss段统称为数据段,数据段在文本段的下一个页开始的地方。数据段的下个页开始是,堆向上生长,即向高地址方向延伸。

栈(又称为堆栈段):用于函数调用,存放局部变量,传递参数,返回地址等。在接近系统空间的地方,栈向下生长,栈的大小是有限制的,一般12M大小。

下面是C源文件和可执行程序的对应关系。

 

可执行文件和内存中的各的对应关系。(《C专家编程》第六章)

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值