[关闭]
@yiltoncent 2016-03-29T08:32:55.000000Z 字数 4324 阅读 780

Linux C编程一站式学习【学习笔记】

LINUX C语言基础


第 19 章 汇编与C之间的关系

1. 函数调用

注意函数调用和返回过程中的这些规则:

1. 参数压栈传递,并且是从右向左依次压栈。
2. ebp总是指向当前栈帧的栈底。
3. 返回值通过eax寄存器传递。

这些规则并不是体系结构所强加的,ebp寄存器并不是必须这么用,函数的参数和返回值也不是必须这么传,只是操作系统和编译器选择了以这样的方式实现C代码中的函数调用,这称为Calling Convention [调用惯例],Calling Convention 是操作系统二进制接口规范ABI,Application Binary Interface)的一部分。

注意:在公司的服务器上编程发现,第一条并非成立。在main函数传递参数给函数时,参数先暂存在了ediesiedx等寄存器上(传递三个参数)。

第 20 章 链接详解

2. 定义和声明

表 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]中有一个例子:

  1. int i1 = 1; // definition, external linkage
  2. static int i2 = 2; // definition, internal linkage
  3. extern int i3 = 3; // definition, external linkage
  4. int i4; // tentative definition, external linkage
  5. static int i5; // tentative definition, internal linkage
  6. int i1; // valid tentative definition, refers to previous
  7. int i2; // 6.2.2 renders undefined, linkage disagreement
  8. int i3; // valid tentative definition, refers to previous
  9. int i4; // valid tentative definition, refers to previous
  10. int i5; // 6.2.2 renders undefined, linkage disagreement
  11. extern int i1; // refers to previous, whose linkage is external
  12. extern int i2; // refers to previous, whose linkage is internal
  13. extern int i3; // refers to previous, whose linkage is external
  14. extern int i4; // refers to previous, whose linkage is external
  15. extern int i5; // refers to previous, whose linkage is internal

补充:C语言中Static和Const关键字的的作用

用static声明局部变量,使其变为静态存储方式(静态数据区),作用域不变;用static声明外部变量,其本身就是静态变量,这只会改变其连接方式,使其只在本文件内部有效,而其他文件不可连接或引用该变量。
const作用: “只读(readonly)”

4. 共享库

参考:
ELF文件的加载和动态链接过程
动态链接库中函数的地址确定---PLT和GOT
Linux系统调用详解

exec族函数

  1. #include <unistd.h>
  2. int execl(const char *path, const char *arg, ...);
  3. int execlp(const char *file, const char *arg, ...);
  4. int execle(const char *path, const char *arg, ..., char *const envp[]);
  5. int execv(const char *path, char *const argv[]);
  6. int execvp(const char *file, char *const argv[]);
  7. int execve(const char *path, char *const argv[], char *const envp[]);
  8. /* exec.c */
  9. #include <unistd.h>
  10. main()
  11. {
  12. char *envp[]={"PATH=/tmp",
  13. "USER=lei",
  14. "STATUS=testing",
  15. NULL};
  16. char *argv_execv[]={"echo", "excuted by execv", NULL};
  17. char *argv_execvp[]={"echo", "executed by execvp", NULL};
  18. char *argv_execve[]={"env", NULL};
  19. if(fork()==0)
  20. if(execl("/bin/echo", "echo", "executed by execl", NULL)<0)
  21. perror("Err on execl");
  22. if(fork()==0)
  23. if(execlp("echo", "echo", "executed by execlp", NULL)<0)
  24. perror("Err on execlp");
  25. if(fork()==0)
  26. if(execle("/usr/bin/env", "env", NULL, envp)<0)
  27. perror("Err on execle");
  28. if(fork()==0)
  29. if(execv("/bin/echo", argv_execv)<0)
  30. perror("Err on execv");
  31. if(fork()==0)
  32. if(execvp("echo", argv_execvp)<0)
  33. perror("Err on execvp");
  34. if(fork()==0)
  35. if(execve("/usr/bin/env", argv_execve, envp)<0)
  36. perror("Err on execve");
  37. }

第 21 章 预处理

2. 宏定义

4. 其它预处理特性

  1. /* assert.h standard header */
  2. #undef assert /* remove existing definition */
  3. #ifdef NDEBUG
  4. #define assert(test) ((void)0)
  5. #else /* NDEBUG not defined */
  6. void _Assert(char *);
  7. /* macros */
  8. #define _STR(x) _VAL(x)
  9. #define _VAL(x) #x
  10. #define assert(test) ((test) ? (void)0 \
  11. : _Assert(__FILE__ ":" _STR(__LINE__) " " #test))
  12. #endif
  1. /* xassert.c _Assert function */
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. void _Assert(char *mesg)
  5. { /* print assertion message and abort */
  6. fputs(mesg, stderr);
  7. fputs(" -- assertion failed\n", stderr);
  8. abort();
  9. }

第 23 章 指针

2. 指针类型的参数和返回值

restrict关键字用法

关键字restrict只用于限定指针;该关键字用于告知编译器,所有修改该指针所指向内容的操作全部都是基于(base on)该指针的,即不存在其它进行修改操作的途径;这样的后果是帮助编译器进行更好的代码优化,生成更有效率的汇编代码。

深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注