@yiltoncent
2016-03-29T08:32:55.000000Z
字数 4324
阅读 2856
LINUX C语言基础
注意函数调用和返回过程中的这些规则:
1. 参数压栈传递,并且是从右向左依次压栈。
2. ebp总是指向当前栈帧的栈底。
3. 返回值通过eax寄存器传递。
这些规则并不是体系结构所强加的,ebp寄存器并不是必须这么用,函数的参数和返回值也不是必须这么传,只是操作系统和编译器选择了以这样的方式实现C代码中的函数调用,这称为Calling Convention [调用惯例],Calling Convention 是操作系统二进制接口规范(ABI,Application Binary Interface)的一部分。
注意:在公司的服务器上编程发现,第一条并非成立。在main函数传递参数给函数时,参数先暂存在了edi,esi和edx等寄存器上(传递三个参数)。
表 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 linkagestatic int i2 = 2; // definition, internal linkageextern int i3 = 3; // definition, external linkageint i4; // tentative definition, external linkagestatic int i5; // tentative definition, internal linkageint i1; // valid tentative definition, refers to previousint i2; // 6.2.2 renders undefined, linkage disagreementint i3; // valid tentative definition, refers to previousint i4; // valid tentative definition, refers to previousint i5; // 6.2.2 renders undefined, linkage disagreementextern int i1; // refers to previous, whose linkage is externalextern int i2; // refers to previous, whose linkage is internalextern int i3; // refers to previous, whose linkage is externalextern int i4; // refers to previous, whose linkage is externalextern int i5; // refers to previous, whose linkage is internal
用static声明局部变量,使其变为静态存储方式(静态数据区),作用域不变;用static声明外部变量,其本身就是静态变量,这只会改变其连接方式,使其只在本文件内部有效,而其他文件不可连接或引用该变量。
const作用: “只读(readonly)”
参考:
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");}
/* 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();}
关键字restrict只用于限定指针;该关键字用于告知编译器,所有修改该指针所指向内容的操作全部都是基于(base on)该指针的,即不存在其它进行修改操作的途径;这样的后果是帮助编译器进行更好的代码优化,生成更有效率的汇编代码。
深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p