程序结构


 

全局变量

1. 全局变量

  • 定义在函数外?的变量是全局变量
  • 全局变量具有全局的?存期和作?域
  1. 它们与任何函数都?关
  2. 在任何函数内部都可以使?它们

2. 全局变量初始化

  • 没有做初始化的全局变量会得到0值
  1. 指针会得到NULL值
  • 只能?编译时刻已知的值来初始化全局变量
  • 它们的初始化发?在main函数之前

3. 被隐藏的全局变量

如果函数内部存在与全局变量同名的变量,则全局变 量被隐藏

4. 静态本地变量

  • 在本地变量定义时加上static修饰符就成为静态本地 变量
  • 当函数离开的时候,静态本地变量会继续存在并保持 其值
  • 静态本地变量的初始化只会在第?次进?这个函数时 做,以后进?函数时会保持上次离开时的值
  • 静态本地变量实际上是特殊的全局变量
  • 它们位于相同的内存区域
  • 静态本地变量具有全局的?存期,函数内的局部作? 域
  • static在这?的意思是局部作?域(本地可访问)

5. *返回指针的函数

  • 返回本地变量的地址是危险的
  • 返回全局变量或静态本地变量的地址是安全的
  • 返回在函数内malloc的内存是安全的,但是容易造成 问题
  • 最好的做法是返回传?的指针

6. tips

  • 不要使?全局变量来在函数间传递参数和结果
  • 尽量避免使?全局变量
  1. 丰?汽?的案?
  • *使?全局变量和静态本地变量的函数是线程不安全 的

1.编译预处理指令

#开头的是编译预处理指令

它们不是C语?的成分,但是C语?程序离不开它们

#define?来定义?个宏

2.#define

  • #define <名字> <值>
  • 注意没有结尾的分号,因为不是C的语句
  • 名字必须是?个单词,值可以是各种东?
  • 在C语?的编译器开始编译之前,编译预处理程序 (cpp)会把程序中的名字换成值
  1. 完全的?本替换
  • gcc —save-temps

3. 宏

  • 如果?个宏的值中有其他的宏的名字,也是会被 替换的
  • 如果?个宏的值超过??,最后??之前的?末 需要加\
  • 宏的值后?出现的注释不会被当作宏的值的?部 分

4.没有值的宏

#define _DEBUG

这类宏是?于条件编译的,后?有其他的编译预处理 指令来检查这个宏是否已经被定义过了

5. 预定义的宏

__LINE__

__FILE__

__DATE__

__TIME__

__STDC__

6.带参数的宏

7.像函数的宏

  • #define cube(x) ((x)*(x)*(x))
  • 宏可以带参数

8.错误定义的宏

  • #define RADTODEG(x) (x * 57.29578)
  • #define RADTODEG(x) (x) * 57.29578

9.带参数的宏的原则

  • ?切都要括号
  1. 整个值要括号
  2. 参数出现的每个地?都要括号
  • #define RADTODEG(x) ((x) * 57.29578)

10. 带参数的宏

  • 可以带多个参数
  1. #define MIN(a,b) ((a)>(b)?(b):(a))
  • 也可以组合(嵌套)使?其他宏

11. 分号?

#define PRETTY_PRINT(msg) printf(msg);

if (n < 10)

PRETTY_PRINT("n is less than 10");

else

PRETTY_PRINT("n is at least 10");

12. 带参数的宏

  • 在?型程序的代码中使??常普遍
  • 可以?常复杂,如“产?”函数
  1. 在#和##这两个运算符的帮助下
  • 存在中???化差异
  • 部分宏会被inline函数替代

13. 其他编译预处理指令

条件编译

error

....

?程序

 

1.多个.c?件

  • main()?的代码太?了适合分成?个函数
  • ?个源代码?件太?了适合分成?个?件
  • 两个独?的源代码?件不能编译形成可执?的程序

2.编译单元

  • ?个.c?件是?个编译单元
  • 编译器每次编译只处理?个编译单元

3.项目

  • 在Dev C++中新建?个项?,然后把?个源代码?件 加?进去
  • 对于项?,Dev C++的编译会把?个项?中所有的源 代码?件都编译后,链接起来
  • 有的IDE有分开的编译和构建两个按钮,前者是对单 个源代码?件编译,后者是对整个项?做链接

4.头?件

把函数原型放到?个头?件(以.h结尾)中,在需要 调?这个函数的源代码?件(.c?件)中#include这 个头?件,就能让编译器在编译的时候知道函数的原 型

5. 函数原型

  • 如果不给出函数原型,编译器会猜测你所调?的函数 的所有参数都是int,返回类型也是int
  • 编译器在编译的时候只看当前的?个编译单元,它不 会去看同?个项?中的其他编译单元以找出那个函数 的原型
  • 如果你的函数并?如此,程序链接的时候不会出错
  • 但是执?的时候就不对了
  • 所以需要在调?函数的地?给出函数的原型,以告诉 编译器那个函数究竟?什么样

6.#include

  • #include是?个编译预处理指令,和宏?样,在编译 之前就处理了
  • 它把那个?件的全部?本内容原封不动地插?到它所 在的地?
  • 所以也不是?定要在.c?件的最前?#include

7. “”还是<>

  • #include有两种形式来指出要插?的?件
  1. “”要求编译器?先在当前?录(.c?件所在的?录) 寻找这个?件,如果没有,到编译器指定的?录去 找
  2. <>让编译器只在指定的?录去找
  • 编译器??知道??的标准库的头?件在哪?
  • 环境变量和编译器命令?参数也可以指定寻找头?件 的?录

8. 头?件

  • 在使?和定义这个函数的地?都应该#include这个头 ?件
  • ?般的做法就是任何.c都有对应的同名的.h,把所有 对外公开的函数的原型和全局变量的声明都放进去

9.不对外公开的函数

  • 在函数前?加上static就使得它成为只能在所在的编 译单元中被使?的函数
  • 在全局变量前?加上static就使得它成为只能在所在 的编译单元中被使?的全局变量

10. 变量的声明

  • int i;是变量的定义
  • extern int i;是变量的声明

11.声明和定义

  • 声明是不产?代码的东? 
  1. 函数原型 
  2. 变量声明 
  3. 结构声明 
  4. 宏声明 
  5. 枚举声明 
  6. 类型声明 
  7. inline函数 
  • 定义是产?代码的东?

12. 头?件

  • 只有声明可以被放在头?件中 ?
  1. 是规则不是法律 
  2. 否则会造成?个项?中多个编译单元?有重名的实体 
  • *某些编译器允许?个编译单元中存在同名的函数, 或者?weak修饰符来强调这种存在

13.重复声明

  • 同?个编译单元?,同名的结构不能被重复声明 
  • 如果你的头?件?有结构的声明,很难这个头?件不 会在?个编译单元?被#include多次 
  • 所以需要“标准头?件结构”

14.标准头?件结构

  • 运?条件编译和宏,保证这个头?件在 ?个编译单元中只会被#include?次
  • #pragma once也能起到相同的作?, 但是不是所有的编译器都?持

15. *前向声明

因为在这个地?不需要具体知道Node 是怎样的,所以可以?struct Node来 告诉编译器Node是?个结构