第十二周:程序结构

简介: 你已经快坚持到了最后,很棒!

12.1-1全局变量:定义在函数之外的变量,全局的生存期和作用域

全局变量

  1. 定义在函数外面的变量是全局变量
  2. 全局变量具有全局的生存期和作用域
  1. 他们和任何函数无关
  2. 在任何函数内部都可以使用他们

#include

intf(void);

intgAll=12;//全局变量

intmain(intargc,charconst*argv[])

{

   printf("in %s gAll=%d\n",__func__,gAll);

   f();

   printf("agn in %s gAll = %d\n",__func__,gAll);

   return0;

}

//_func_是一个字符串,表达的是当前函数的名字,也就是main的名字

intf(void)

{

   printf("in %s gAll=%d\n",__func__,gAll);

   gAll+=2;

   printf("agn in %s gAll=%d\n",__func__,gAll);

   returngAll;

}

全局变量初始化

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

被隐藏的全局变量

  1. 如果函数内部存在与全局变量同名的变量,则全局变量被隐藏(局部的优先度会更高噢,会覆盖掉外面的变量)

12.1-2静态本地变量:能在函数结束后继续保有原值的本地变量

静态本地变量

  1. 在本地变量定义时加上static修饰符就成为静态本地变量
  2. 当函数离开的时候,静态本地变量会继续存在并保持其值
  3. 静态本地变量的初始化只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值

#include

intf(void);

intgAll=12;//全局变量

intmain(intargc,charconst*argv[])

{

   f();

   f();

   f();

   return0;

}

//_func_是一个字符串,表达的是当前函数的名字,也就是main的名字

intf(void)

{

   staticintall=1;

   printf("in %s gAll=%d\n",_func_,gAll);

   gAll+=2;

   printf("agn in %s gAll=%d\n",_func_,gAll);

   returngAll;

}

  1. 静态本地变量实际上是特殊的全局变量
  2. 它们(静态本地变量和全局变量)位于相同的内存区域
  3. 静态本地变量具有全局的生存期,函数内的局部作用域
  4. static 在这里的意思是局部作用域(本地可访问)

12.1-3后记:返回指针的函数,使用全局变量的贴士

*返回指针的函数

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

tips(贴士)

  1. 不要使用全局变量来在函数间传递参数和结果
  2. 尽量避免使用全局变量
  1. 丰田汽车的案子(ks)  这里给出传送门(想知道的可以看看):https://www.sohu.com/a/133455549_464086
  1. *使用全局变量和静态本地变量的函数是线程不安全的

12.2-1宏定义

编译预处理指令

  1. #开头的是编译预处理指令
  2. 它们不是c语言的成分,但是C语言程序离不开它们
  3. #define用来定义一个宏 ,其实只是一个原始的文本替换

#define

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

  1. 如果一个宏的值中有其他宏的名字,也是会被替换的
  2. 如果一个宏的值超过来一行,最后一行之前的行末需要加\
  3. 宏的值后面出现的注释不会被当作宏的值的一部分

没有值的宏

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

预定义的宏

  1. LINE表达代码所在的行号
  2. FILE表达代码所在的文件名
  3. DATE编译时候的日期
  4. TIME编译时候的时间
  5. STDC

12.2-2带参数的宏

像函数的宏

  1. #define cube(x)((x)(x)(x))
  2. 宏可以带参数

#include

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

intmain(intargc,charconst*argv[])

{

   printf("%d\n",cube(5));

   //cube(5)会被替换成cube((5)*(5)*(5));

   

   return0;

}

错误定义的宏

  1. #define RADTODEG(x)(x*57.29578)
  2. #define RADTODEG(x)(x)*57.29578

带参数的宏的原则

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

带参数的宏

  1. 可以带多个参数
  1. #define MIN(a,b)((a)>(b)?(b):(a))
  1. 也可以组合(嵌套)使用其他宏
  2. 定义宏的时候后面千万不要加分号
  3. 在大型程序的代码中使用非常普遍
  4. 可以非常复杂,如"产生"函数
  1. 在#和##这两个运算符的帮助下
  1. 存在中西方文化差异
  2. 部分宏会被inline函数替代

宏的缺陷

没有可以去检查宏有没有问题的机制

其他预编译

  1. 条件编译
  2. error
  3. ...

12.3-1多个源代码文件

多个.c文件

  1. main()里的代码太长了适合分成几个函数
  2. 一个源代码文件太长了适合分成几个文件
  3. 两个独立的源代码文件不能编译形成可执行的程序

项目

因为看的是翁恺老师2014年的版本(比后来的课程内容多不少,后来的课程缩水了),所以这部分已经有更好的就进行省略了

编译单元

  1. 一个.c文件是一个编译单元
  2. 编译器每次编译只处理一个编译单元

12.3-2头文件

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

#include

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

“”还是<>

  1. #include有两种形式来指出要插入的文件
  1. “”要求编译器首先在当前目录(.c文件所在的目录)寻找这个文件,如果没有,到编译器指定的目录去找,例如“MAX.h”啥的自己设定的那些
  2. <>让编译器只在指定的目录去找,例如
  1. 编译器自己知道自己的标准库的头文件在哪里
  2. 环境变量和编译器命令行参数也可以指定寻找头文件的目录

#include的误区

  1. #include不是用来引入库的
  2. stdio.h里只有printf的原型,printf的代码在另外的地方,某个.lib(Windows)或.a(Unix)中
  3. 现在的C语言编译器默认会引入所有的标准库
  4. #include只是为了让编译器指定printf函数的原型,保证年调用时给出的参数值是正确的类型

头文件

  1. 在使用和定义这个函数的地方都应该%include这个头文件
  2. 一般的做法就是任何.c都有对应的同名的.h,把所有对外公开的函数的原型和全局变量的声明都放进去
  3. 全局变量是可以在多个.c之间共享的

不对外公开的函数

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

12.3-3声明

变量的声明

  1. int i;是变量的定义
  2. extern int i;是变量的声明

声明和定义

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

头文件

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

重复声明

  1. 同一个编译单元里,同名的结构不能被重复声明
  2. 如果你的头文件里有结构的声明,很难这个头文件不会在一个编译单元里被#include多次所以需要"标准头文件"

标准头文件结构

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

#ifndef __LIST_HEAD__

#define __LIST_HEAD__

#include"node.h"

typedefstruct_list{

   Node*head;

   Node*tail;

}List;

#endif

目录
相关文章
【期末不挂科-单片机考前速过系列P6】(第六章:10题速过定时计数器的结构和工作方式例题)经典例题盘点(带图解析)
【期末不挂科-单片机考前速过系列P6】(第六章:10题速过定时计数器的结构和工作方式例题)经典例题盘点(带图解析)
|
7月前
|
算法 Java API
算法编程(二十四):日期之间隔几天
算法编程(二十四):日期之间隔几天
81 0
|
机器学习/深度学习 人工智能 C++
C++第6周项目2 - 程序填空
课程首页地址:http://blog.csdn.net/sxhelijian/article/details/7910565,本周题目链接:http://blog.csdn.net/sxhelijian/article/details/8748730 【题目】程序填空:按要求将缺少的代码填进去,经调试确认符合要求 a. 用指针访问对象 class A { private: int a
945 1
2013C++第9周项目——多分支结构程序设计
课程首页在:http://blog.csdn.net/sxhelijian/article/details/11890759 【项目1分段函数求值】从键盘输入x的值(整型),根据公式1计算并输出x和y的值。  要求用switch语句构造多分支结构完成求解,利用BB平台上“第9周练习源代码及博文模板.txt”中给出了博文模板发布报告。 提示: 解法1:类似例3.9的思路,各分段的标准都
1423 0
|
C++ 算法 存储
C++程序设计-第1周温故知新上机实践项目
回到课程主页,链接:C++程序设计课程主页-2012级   本次上机对应的教学内容:第7章 自定义类型,主要为结构体与枚举类型 【项目1】热身运动 回顾上学期学习过的知识,编程序解决下面的问题1、编写函数stringcat,实现字符串的连接,程序中需要使用指针形式访问字符串 int main() { char s1[30]="I love "; char *s2="C++"; st
1262 0
|
C++
C++程序设计-第17周指针上机实践项目
回到课程主页,链接:C++程序设计课程主页-2012级   本次上机对应的教学内容:第6章 指针 指针和数组,指针和字符串等 【项目1-体会函数参数传递】1、下面三段程序都试图通过定义函数,实现实际参数在必要时的交换,哪些能够实现,哪些不能实现?通过运行程序和单步执行,让自己对这一过程更加清楚,然后请在博文中给出明确的回答,并用你自己的话展开描述(自己的话,但要用术语,用术语讲技术的事,是必须
1277 0
|
存储 数据安全/隐私保护 C++
C++程序设计-第14周数组上机实践项目
回到课程主页,链接:C++程序设计课程主页-2012级   本次上机对应的教学内容:第5章   数组 - 一维数组 【项目1 - 数组大折腾】创建一个长度为20的整型数组,通过初始化,为数组中的前10个元素赋初值,然后通过键盘输入,使后10个元素获得值,将所有元素值加倍后保存在数组中,最后由前往后输出数组中所有元素的值,再由后往前输出数组中所有元素的值,再输出数组中的所有偶数,以及下标为3
1104 0
|
程序员 C++
C++第12周项目1——重温星号图
课程首页地址:http://blog.csdn.net/sxhelijian/article/details/7910565 【项目1-调用函数输出星号图】   这一组的练习意在通过调用函数输出星号图,体会与理解函数的工作过程,并为其后编制自定义函数实现特定功能,打下基础。  任务1:补充完下面的程序,使程序输出上面的两个星号图,要求在main()函数中调用自定义函数printstars(
1418 0
|
C++
C++程序设计-第12周函数序设计上机实践项目
回到课程主页,链接:C++程序设计课程主页-2012级   本次上机对应的教学内容:第4章   函数 【项目1-调用函数输出星号图】   这一组的练习意在通过调用函数输出星号图,体会与理解函数的工作过程,并为其后编制自定义函数实现特定功能,打下基础。  任务1:补充完下面的程序,使程序输出上面的两个星号图,要求在main()函数中调用自定义函数printstars()。  给出的程序如下:
1433 0
|
网络架构 C++ 安全
C02-程序设计基础提高班(C++)第12周上机任务-类的继承
第12周:阅读教材第11章(p347-394),主要内容是类的继承,完成第12周上机任务; (回到C02-程序设计基础提高班(C++)学习安排) 【任务1】定义一个名为CPerson的类,有以下私有成员:姓名、身份证号、性别和年龄,成员函数:构造函数、析构函数、输出信息的函数。并在此基础上派生出CEmployee类,派生类CEmployee增加了两个新的数据成员,分别用于表示部门和薪水。要
1225 0