摘要:
不完全类型是暂时没有完全定义好的类型,编译器不知道这种类型该占几个字节的存储空间,例如: 具有不完全类型的变量可以通过多次声明组合成一个完全类型,比如数组str声明两次: 当编译器碰到第一个声明时,认为str是一个不完全类型,碰到第二个声明时str就组合成完全类型了,如果编译器处理到程序文件的末尾仍 阅读全文
摘要:
指向数组的指针 以下定义一个指向数组的指针,该数组有10个int元素: []比*有更高的优先级,如果a先和*结合则表示a是一个指针,如果a先和[]结合则表示a是一个数组。 int *a[10];这个定义可以拆成两句: t代表int *类型, a则是由这种类型的元素组成的数组。 int (*a)[10 阅读全文
摘要:
要通过指针p访问结构体成员可以写成(*p).c和(*p).num,为了书写方便, C语言提供了->运算符,也可以写成p->c和p->num。 阅读全文
摘要:
const限定符和指针结合起来常见的情况有以下几种。: 这两种写法是一样的, a是一个指向const int型的指针, a所指向的内存单元不可改写,所以(*a)++是不允许的,但a可以改写,所以a++是允许的。 a是一个指向int型的const指针, *a是可以改写的,但a不允许改写。 a是一个指向 阅读全文
摘要:
(1)首先指针pa指向a[0]的地址,注意后缀运算符的优先级高于单目运算符,所以是取a[0]的地址,而不是取a的地址。然后pa++让pa指向下一个元素(也就是a[1]),由于pa是int *指针,一个int型元素占4个字节,所以pa++使pa所指向的地址加4,注意不是加1。 (2)a[2]和pa[2 阅读全文
摘要:
我们知道,调用函数的传参过程相当于用实参定义并初始化形参, swap(&i, &j)这个调用相当于: 所以px和py分别指向main函数的局部变量i和j,在swap函数中读写*px和*py其实是读写main函数的i和j。尽管在swap函数的作用域中访问不到i和j这两个变量名,却可以通过地址访问它们, 阅读全文
摘要:
野指针和空指针 在堆栈上分配的变量初始值是不确定的,也就是说指针p所指向的内存地址是不确定的,后面用*p访问不确定的地址就会导致不确定的后果,如果导致段错误还比较容易改正,如果意外改写了数据而导致随后的运行中出错,就很难找到错误原因了。像这种指向不确定地址的指针称为“野指针”(Unbound Poi 阅读全文
摘要:
1、三连符替换成相应的单字符(例如用??=表示#字符。)。 2、把用\字符续行的多行代码接成一行。例如: 经过这个预处理步骤之后接成一行: 这种续行的写法要求\后面紧跟换行,中间不能有其它空白字符。 3、把注释(不管是单行注释还是多行注释)都替换成一个空格。 4、经过以上两步之后去掉了一些换行,有的 阅读全文
摘要:
关键字就是已被C语言本身使用,不能作为其它标识符由ANSI标准定义的C语言关键字共32个: 根据关键字的作用,可以将关键字分为数据类型关键字和流程控制关键字两大类。 (1) 数据类型关键字 1) 基本数据类型(5个) void;char;int;float;double 2) 类型修饰关键字(4个) 阅读全文
摘要:
扩展的行内汇编 在扩展的行内汇编中,可以将 C 语言表达式指定为汇编指令的操作数,而且不用去管如何将 C 语言表达式的值读入寄存器,以及如何将计算结果写回 C 变量,你只要告诉程序中 C 语言表达式与汇编指令操作数之间的对应关系即可,GCC 会自动插入代码完成必要的操作。 使用内嵌汇编,要先编写汇编 阅读全文
摘要:
Linux 操作系统内核代码绝大部分使用 C 语言编写,只有一小部分使用汇编语言编写,例如与特定体系结构相关的代码和对性能影响很大的代码。 GCC 提供了内嵌汇编的功能,可以在 C 代码中直接内嵌汇编语言语句,大大方便了程序设计。 基本行内汇编 基本行内汇编很容易理解,一般是按照下面的格式: 同时“ 阅读全文
摘要:
结构体 main函数中几条语句的反汇编结果如下: 从访问结构体成员的指令可以看出,结构体的四个成员在栈上是这样排列的: 虽然栈是从高地址向低地址增长的,但结构体成员也是从低地址向高地址排列的,这一点和数组类似。但有一点和数组不同,结构体的各成员并不是一个紧挨一个排列的,中间有空隙,称为填充(Padd 阅读全文
摘要:
我们在全局作用域和main函数的局部作用域各定义了一些变量,并且引入一些新的关键字const、 static、 register来修饰变量,那么这些变量的存储空间是怎么分配的呢?我们编译之后用readelf命令看它的符号表,了解各变量的地址分布。注意在下面的清单中我把符号表按地址从低到高的顺序重新排 阅读全文
摘要:
存储类修饰符 存储类修饰符(Storage Class Specifier)有以下几种关键字,可以修饰变量或函数声明: (1)static,用它修饰的变量的存储空间是静态分配的,用它修饰的文件作用域的变量或函数具有Internal Linkage。 (2)auto,用它修饰的变量在函数调用时自动在栈 阅读全文
摘要:
作用域 作用域是程序中定义的变量所存在的区域,超过该区域变量就不能被访问。编译器可以确认4种不同类型的作用域:文件作用域,函数作用域,代码块作用域,原型作用域。 代码块作用域 位于一对花括号之间的语句称为语句块,任何在代码块的开始位置声明的标识符都具有代码块作用域,表示它们可以被这个代码块中的所有语 阅读全文
摘要:
汇编程序的入口是_start,而C程序的入口是main函数。 汇编和链接步骤是: 以前我们常用gcc main.c -o main命令编译一个程序,其实也可以分三步做,第一步生成汇编代码,第二步生成目标文件,第三步生成可执行文件: -S选项生成汇编代码, -c选项生成目标文件,此外-E选项只做预处理 阅读全文
摘要:
下面一段C程序: 如果在编译时加上-g选项,那么用objdump反汇编时可以把C代码和汇编代码穿插起来显示,这样C代码和汇编代码的对应关系看得更清楚。反汇编的结果很长,以下只列出我们关心的部分。 要查看编译后的汇编代码,其实还有一种办法是gcc -S main.c,这样只生成汇编代码main.s,而 阅读全文
摘要:
汇编程序1 将这段程序保存为hello.s,然后用汇编器as把汇编程序中的助记符翻译成机器指令(汇编指令与机器指令是对应的)生成目标文件hello.o。然后用链接器ld把目标文件hello.o链接成可执行文件hello(虽然只有一个目标文件但是也需要经过链接才能成为可执行文件因为链接器要修改目标文件 阅读全文
摘要:
访问内存时在指令中可以用多种方式表示内存地址。内存寻址在指令中可以表示成如下的通用格式: ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER) 它所表示的地址可以这样计算出来: FINAL ADDRESS = ADDRESS_OR_OFFSET + 阅读全文
摘要:
通用寄存器 x86的通用寄存器有eax、ebx、ecx、edx、edi、esi。这些寄存器在大多数指令中是可以任意使用的。但有些指令限制只能用其中某些寄存器做某种用途,例如除法指令idivl规定被除数在eax寄存器中,edx寄存器必须是0,而除数可以是任何寄存器中。计算结果的商数保存在eax寄存器中 阅读全文