6.1 全局变量

6.2 编译预处理

6.3 大程序结构

6.1 全局变量

全局变量

定义在函数外面的变量是全局变量

全局变量具有全局的生存期和作用域

它们与任何函数都无关

在任何函数内部都可以使用它们

全局变量初始化

没有做初始化的全局变量会得到0值

指针会得到NULL值

只能用编译时刻已知的值来初始化全局变量

它们的初始化发生在main函数之前

被隐藏的全局变量

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

静态本地变量

在本地变量定义时加上static修饰符就成为静态本地变量

当函数离开的时候,静态本地变量会继续存在并保持其值

静态本地变量的初始化只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值

静态本地变量

静态本地变量实际上是特殊的全局变量

它们位于相同的内存区域

静态本地变量具有全局的生存期,函数内的局部作用域

static在这里的意思是局部作用域(本地可访问)

返回指针的函数

返回本地变量的指针式危险的

返回全局变量或静态本地变量的地址是安全的

返回在函数内malloc的内存是安全的,但是容易造成问题

最好的做法是返回传入的指针

tips

不要使用全局变量来在函数间传递参数和结果

尽量避免使用全局变量

使用全局变量和静态本地变量的函数是线程不安全的

6.2 编译预处理

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

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

#define用来定义一个宏

#define

#define<名字><值>

注意没有结尾的分号,因为不是C的语言

名字必须是一个单词,值可以是各种东西

在C语言的编译器开始编译之前,编译预处理程序(cpp)会把程序中的名字换成值

完全的文本替换

gcc--save-temps

如果一个宏的值中有其他的宏的名字,也是会被替换的

如果一个宏的值超过一行,最后一行之前的行末需要加\

宏的值后面出现的注释不会被当作宏的值的一部分

 #include <stdio.h>
#include <stdlib.h> #define PI2 2*PI
#define PI 3.14159 void main()
{
printf("%lf\n", PI); printf("%lf\n", PI2); system("pause");
}

没有值的宏

#define _DEBUG

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

预定义的宏

__LINE__

在源代码中插入当前源代码行号

__FILE__

在源代码中插入当前源代码文件名

__DATA__

在源代码中插入当前编译日期〔注意和当前系统日期区别开来〕

__TIME__

在源代码中插入当前编译时间〔注意和当前系统时间区别开来〕

__STDC__

当要求程序严格遵循ANSIC标准时该标识符被赋值为1

 #include <stdio.h>
#include <stdlib.h> void main()
{
printf("%s:%d\n", __FILE__, __LINE__); printf("%s,%s\n", __DATE__, __TIME__); system("pause");
}

像函数的宏

#define cube(x) ((x)*(x)*(x))

宏可以带参数

错误定义的宏

#define RADTODEG(x) (x*57.29578)

#define RADTODEG(x) (x)*57.29578

 #include <stdio.h>
#include <stdlib.h> #define RADTODEG1(x) (x*57.29578) #define RADTODEG2(x) (x)*57.29578 void main()
{
printf("%lf\n", RADTODEG1( + )); printf("%lf\n", RADTODEG2( + )); system("pause");
}

带参数的宏的原则

一切都要括号

整个值要括号

参数出现的每个地方都要括号

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

带参数的宏

可以带多个参数

#define MIN(a,b) ((a)>(b)?(b):(a))

也可以组合(嵌套)使用其他宏

分号?

结尾不要加上分号;

带参数的宏

在大型程序的代码中使用非常普遍

可以非常复杂,如“产生”函数

在#和##这两个运算符的帮助下

存在中西方文化差异

部分宏会被inline函数替代

其他编译预处理指令

条件编译

error

6.3 大程序结构

多个.c文件

main()里的代码太长了适合分成几个函数

一个源代码文件太长了适合分成几个文件

两个独立的源代码文件不能编译形成可执行的程序

编译单元

一个.c文件是一个编译单元

编译器每次编译只处理一个编译单元

头文件

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

#include

#include是一个编译预处理指令。和宏一样,在编译之前就处理了

它把那个文件的全部文本内容原封不动地插入到它所在的地方

所以也不是一定要在.c文件的最前面#include

""还是<>

#include有两种形式来指出要插入的文件

""要求编译器首先在当前目录(.c文件所在的目录)寻找这个文件,如果没有,到编译器指定的目录去找

<>让编译器只在指定的目录去找

编译器自己知道自己的标准库的头文件在哪里

环境变量和编译器命令行参数也可以指定寻找头文件的目录

#include的误区

#include不是用来引入库的

stdio.h里只有printf的原型,printf的代码在另外的地方,某个.lib(Windows)或.a(Unix)中

现在的C语言编译器默认会引入所有的标准库

#include <stdio.h>只是为了让编译器知道printf函数的原型,保证你调用时给的参数值是正确的类型

头文件

在使用和定义这个函数的地方都应该#include这个头文件

一般的做法就是任何.c都有对应的同名.h,把所有的对外公开的函数原型和全局变量的声明都放进去

不对外公开的函数

在函数前面加上static就使得它成为只能在所在的编译单元中被使用的函数

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

变量的声明

int i;是变量的定义

extern int i;是变量的声明

声明和定义

声明是不产生代码的东西

函数原型

变量声明

结构声明

宏声明

枚举声明

类型声明

inline声明

定义是产生代码的东西

头文件

只有声明可以被放在头文件中

是规则不是法律

否则会造成一个项目中多个编译单元里有重名的实体

某些编译器允许几个编译单元中存在同名的函数,或者用weak修饰符来强调这种存在

重复声明

同一个编译单元里,同名的结构不能被重复声明

如果你的头文件里有结构的声明,很难这个头文件不会在一个编译单元里被#include多次

所以需要“标准头文件结构”

标准头文件结构

 #ifndef __LIST_HEAD__
#define __LIST_HEAD__ #include "node.h" typedef struct _list
{
Node *head;
Node *tail;
}List; #endif // !

运用条件编译和宏,保证这个头文件在一个编译单元中只会被#include一次

#pragma once也能起到相同的作用,但是不是所有的编译器都支持

网易云课堂_C语言程序设计进阶_第6周:程序结构的更多相关文章

  1. 网易云课堂_C语言程序设计进阶_第三周:结构:结构、类型定义、联合

    3.1 枚举 3.2 结构 3.3 类型定义 3.1 枚举 枚举是一种用户定义的数据类型,它用关键字enum以如下语法来表明: enum 枚举类型名字{名字0,...,名字n}; 枚举类型名字通常并不 ...

  2. 网易云课堂_C语言程序设计进阶_第8周:图形交互程序

    8.2函数指针 8.2函数指针 #include <stdio.h> #include <stdlib.h> void f(int i) { printf("void ...

  3. 网易云课堂_C语言程序设计进阶_第七周:文件:文件访问、格式化输入输出、二进制输入输出

    7.1文件 7.2底层操作 7.1文件 格式化的输入输出 printf %[flags][width][.prec][hIL]type Flag 含义 - 左对齐 + 在前面放+或- (space) ...

  4. 网易云课堂_C语言程序设计进阶_第5周:链表_1逆序输出的数列

    1 逆序输出的数列(10分) 题目内容: 你的程序会读入一系列的正整数,预先不知道正整数的数量,一旦读到-1,就表示输入结束.然后,按照和输入相反的顺序输出所读到的数字,不包括最后标识结束的-1. 输 ...

  5. 网易云课堂_C语言程序设计进阶_第5周:链表

    5.1可变数组 5.2链表 5.1可变数组 Resizable Array Think about a set of functions that provide a mechanism of res ...

  6. 网易云课堂_C语言程序设计进阶_期末考试编程题部分

    1 字符串循环右移(5分) 题目内容: 输入一个字符串和一个非负整数N,要求将字符串循环右移N次. 输入格式: 输入在第1行中给出一个字符串,以'#'表示结束,‘#’不是字符串的一部分,字符串的长度未 ...

  7. 网易云课堂_C语言程序设计进阶_第二周:指针:取地址运算和指针、使用指针、指针与数组、指针与函数、指针与const、指针运算、动态内存分配_2信号报告

    2 信号报告(5分) 题目内容: 无线电台的RS制信号报告是由三两个部分组成的: R(Readability) 信号可辨度即清晰度. S(Strength)    信号强度即大小. 其中R位于报告第一 ...

  8. 网易云课堂_C语言程序设计进阶_第一周:数据类型:整数类型、浮点类型、枚举类型_1计算分数精确值

    1 计算分数精确值(10分) 题目内容: 由于计算机内部表达方式的限制,浮点运算都有精度问题,为了得到高精度的计算结果,就需要自己设计实现方法. (0,1)之间的任何浮点数都可以表达为两个正整数的商, ...

  9. 网易云课堂_C语言程序设计进阶_第四周:ACL图形库

    创建ACLLib程序 #include"acllib.h" #include<stdio.h> int Setup1() { initWindow(, );//初始化窗 ...

随机推荐

  1. PDO的事物处理机制

    Mysql的事务处理: 1.MySQL目前只有InnoDB 和BDB两个数据表类型才支持事务. 2.在默认条件下,MySQL是以自动提交(autocommit)模式运行的,这就意味着所执行的每一个语句 ...

  2. PHPMailer发送邮件方法

    /** * * 测试邮件发送s * @param 服务器 $Host * @param 端口 $Port * @param 昵称 $Fromname * @param 身份验证用户名 $Usernam ...

  3. Datagridview控件实现分页功能

    可以进行sql语句进行设置:      1.先新建一个窗体,一个DataGridView控件.两个label控件.两个Button控件   2.代码如下: using System; using Sy ...

  4. DNS域欺骗攻击详细教程之Linux篇

    .DNS域欺骗攻击原理 DNS欺骗即域名信息欺骗是最常见的DNS安全问题.当一 个DNS服务器掉入陷阱,使用了来自一个恶意DNS服务器的错误信息,那么该DNS服务器就被欺骗了.DNS欺骗会使那些易受攻 ...

  5. [开源]在iOS上实现Android风格的控件Toast

    [开源]在iOS上实现Android风格的控件Toast iOS的风格和Apple其他产品一样,简单而粗暴.没有给人其他选择的余地,让你又爱又恨.同样的,Apple对待iOS平台的开发人员和对待大众消 ...

  6. Python学习笔记3-Python元组、数组、字典集合的操作

    在Python中数组中的每一项可以是不同的数据类型 元组:只能读不能写的数组 aTuple=(1,'abc','tmc',79.0,False) print aTuple[1:3] print typ ...

  7. 日本語N1文法まとめ

    1.v连用形+ものの:虽然~但是(201212-32) -(の)かというと/かといえば(201412-38)a动词ます辞書形・た形/イ形+~ な形語幹/名+~a,"至于是否・・・・・・&qu ...

  8. C++ Primer 学习笔记_85_模板与泛型编程 --模板特化[续]

    模板与泛型编程 --模板特化[续] 三.特化成员而不特化类 除了特化整个模板之外,还能够仅仅特化push和pop成员.我们将特化push成员以复制字符数组,而且特化pop成员以释放该副本使用的内存: ...

  9. ftk学习记(icon篇)

    [声明:版权全部,欢迎转载.请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 在開始今天的文章之前.我们还是先看一下上一节的效果图. 大家能够參照上一节的解说,是不是存在这种 ...

  10. Android平台音频信号FFT的实现

    转载请标明出处:http://blog.csdn.net/sctu_vroy/article/details/45871823 功能:加载本地SD卡中moveDsp文件夹中的音频文件(包括录音获取文件 ...