第 19 章 汇编与C之间的关系
1. 函数调用
注意函数调用和返回过程中的这些规则:
1. 参数压栈传递,并且是从右向左依次压栈。
2. ebp总是指向当前栈帧的栈底。
3. 返回值通过eax寄存器传递。
这些规则并不是体系结构所强加的,ebp寄存器并不是必须这么用,函数的参数和返回值也不是必须这么传,只是操作系统和编译器选择了以这样的方式实现C代码中的函数调用,这称为Calling Convention [调用惯例],Calling Convention 是操作系统二进制接口规范(ABI,Application Binary Interface)的一部分。
注意:在公司的服务器上编程发现,第一条并非成立。在main
函数传递参数给函数时,参数先暂存在了edi
,esi
和edx
等寄存器上(传递三个参数)。
第 20 章 链接详解
2. 定义和声明
Storage Class | File Scope Declaration | Block Scope Declaration |
---|---|---|
none | previous linkage can define |
previous linkage cannot define |
extern | previous linkage can define |
previous linkage cannot define |
static | internal linkage can define |
N/A |
表 20.1. Storage Class关键字对函数声明的作用
Storage Class | File Scope Declaration | Block Scope Declaration |
---|---|---|
none | previous linkage can define |
previous linkage cannot define |
extern | previous linkage can define |
previous linkage cannot define |
static | internal linkage can define |
N/A |
表 20.2. Storage Class关键字对变量声明的作用
Storage Class | File Scope Declaration | Block Scope Declaration |
---|---|---|
none | external linkage static duration static initializer tentative definition |
no linkage automatic duration dynamic initializer definition |
extern | previous linkage static duration no initializer[*] not a definition |
previous linkage static duration no initializer not a definition |
static | internal linkage static duration static initializer tentative definition |
no linkage static duration static initializer definition |
上表的每个单元格里分成四行,分别描述变量的链接属性、生存期,以及这种变量如何初始化,是否算变量定义。链接属性有External Linkage、Internal Linkage、No Linkage和Previous Linkage四种情况,生存期有Static Duration和Automatic Duration两种情况,请参考本章和上一章的定义。初始化有Static Initializer和Dynamic Initializer两种情况,前者表示Initializer中只能使用常量表达式,表达式的值必须在编译时就能确定,后者表示Initializer中可以使用任意的右值表达式,表达式的值可以在运行时计算。是否算变量定义有三种情况,Definition(算变量定义)、Not a Definition(不算变量定义)和Tentative Definition(暂定的变量定义)。什么叫“暂定的变量定义”呢?一个变量声明具有文件作用域,没有Storage Class关键字修饰,或者用static关键字修饰,那么如果它有Initializer则编译器认为它就是一个变量定义,如果它没有Initializer则编译器暂定它是变量定义,如果程序文件中有这个变量的明确定义就用明确定义,如果程序文件没有这个变量的明确定义,就用这个暂定的变量定义[32],这种情况下变量以0初始化。在[C99]中有一个例子:
int i1 = 1; // definition, external linkage
static int i2 = 2; // definition, internal linkage
extern int i3 = 3; // definition, external linkage
int i4; // tentative definition, external linkage
static int i5; // tentative definition, internal linkage
int i1; // valid tentative definition, refers to previous
int i2; // 6.2.2 renders undefined, linkage disagreement
int i3; // valid tentative definition, refers to previous
int i4; // valid tentative definition, refers to previous
int i5; // 6.2.2 renders undefined, linkage disagreement
extern int i1; // refers to previous, whose linkage is external
extern int i2; // refers to previous, whose linkage is internal
extern int i3; // refers to previous, whose linkage is external
extern int i4; // refers to previous, whose linkage is external
extern int i5; // refers to previous, whose linkage is internal
用static声明局部变量,使其变为静态存储方式(静态数据区),作用域不变;用static声明外部变量,其本身就是静态变量,这只会改变其连接方式,使其只在本文件内部有效,而其他文件不可连接或引用该变量。
const作用: “只读(readonly)”
4. 共享库
参考:
ELF文件的加载和动态链接过程
动态链接库中函数的地址确定---PLT和GOT
Linux系统调用详解
exec
族函数
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
/* exec.c */
#include <unistd.h>
main()
{
char *envp[]={"PATH=/tmp",
"USER=lei",
"STATUS=testing",
NULL};
char *argv_execv[]={"echo", "excuted by execv", NULL};
char *argv_execvp[]={"echo", "executed by execvp", NULL};
char *argv_execve[]={"env", NULL};
if(fork()==0)
if(execl("/bin/echo", "echo", "executed by execl", NULL)<0)
perror("Err on execl");
if(fork()==0)
if(execlp("echo", "echo", "executed by execlp", NULL)<0)
perror("Err on execlp");
if(fork()==0)
if(execle("/usr/bin/env", "env", NULL, envp)<0)
perror("Err on execle");
if(fork()==0)
if(execv("/bin/echo", argv_execv)<0)
perror("Err on execv");
if(fork()==0)
if(execvp("echo", argv_execvp)<0)
perror("Err on execvp");
if(fork()==0)
if(execve("/usr/bin/env", argv_execve, envp)<0)
perror("Err on execve");
}
第 21 章 预处理
2. 宏定义
- 变量式宏定义
- 函数式宏定义
4. 其它预处理特性
/* assert.h standard header */
#undef assert /* remove existing definition */
#ifdef NDEBUG
#define assert(test) ((void)0)
#else /* NDEBUG not defined */
void _Assert(char *);
/* macros */
#define _STR(x) _VAL(x)
#define _VAL(x) #x
#define assert(test) ((test) ? (void)0 \
: _Assert(__FILE__ ":" _STR(__LINE__) " " #test))
#endif
/* xassert.c _Assert function */
#include <stdio.h>
#include <stdlib.h>
void _Assert(char *mesg)
{ /* print assertion message and abort */
fputs(mesg, stderr);
fputs(" -- assertion failed\n", stderr);
abort();
}
第 23 章 指针
2. 指针类型的参数和返回值
关键字restrict只用于限定指针;该关键字用于告知编译器,所有修改该指针所指向内容的操作全部都是基于(base on)该指针的,即不存在其它进行修改操作的途径;这样的后果是帮助编译器进行更好的代码优化,生成更有效率的汇编代码。
深入理解const charp,char constp,char const p,const char p,char constp,char constp,char*const p