宏观控制_C语言快速入门与计算机二级备考

全局变量

全局变量

  • 变量可以定义在函数外,使之区别于函数一节中的本地变量,称全局变量

  • 全局变量具有全局的生存期与使用域,它们与任何函数无关,在任何函数内都可使用

    对全局变量的访问与操作,将无视函数域,直接作用到其身上

  • 初始化:没有初始化的全局变量默认为0,指针默认指NULL(本地变量则不会有默认值)

    只能用编译时已知的值来初始化全局变量(不能用变量为全局变量赋值)

  • 如果函数内部有与全局变量同名的变量,则全局变量会被暂时隐藏,只对本地变量操作

静态本地变量

  • 创建静态本地变量:本地变量定义时加上static前缀修饰符

  • 在函数离开时,一般的本地变量生存期结束而消亡,但静态本地变量会继续存在并保存其值

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

  • 静态本地变量实际上是特殊的全局变量:其与全局变量一样有全局的生存期,但作用域只在函数内

全局变量的应用

  • 对于返回指针的函数:

    返回一个本地变量的指针是危险的,因为一旦离开这个函数本地变量就消亡了,其地址会被分配给别的变量使用

    因此,返回一个全局变量/静态本地变量的地址是安全的

    返回在函数内的malloc的内存是安全的,但容易造成问题,最好的做法是返回传入的指针

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

    甚至于尽量避免使用全局变量,使用全局变量和静态本地变量的函数是线程不安全的

定义宏

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

    它们并非C语言的一部分,其它语言也可以使用

  • #define 名字 值 用于定义一个宏

    名字必须是一个单词,一般用大写以区分变量,但并非规定

    值可以是任何东西,不受数据类型限制(注释除外)

    结尾没有分号(因为不是C的语句)

    例如:#difine PI 3.1415这样就将PI定义为了3.1415

    在编译时会预处理将程序中所有的PI替换为3.1415,这种替换是完全的文本替换

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

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

特殊的宏

  • 没有值的宏:#define _DEBUG

    这类宏用于条件编译,后面有其它的编译预处理指令来检查这个宏是否被定义过了(如果存在则编译一部分代码,不存在就编译另一部分)

  • 已经被预定义的宏,在程序中可以直接使用

    __LINE__ 源代码文件的行号

    __FILE__ 源代码文件的文件名(从根目录开始)

    __DATE__ __TIME__ 编译时的文件名

    __STDC__ 判断当前程序文件是不是标志C文件(若遵守ANSIC标准时为1)

带参数的宏

  • 宏可以带参数,类似于调用函数

  • 例如:#define cube(x) ((x)*(x)*(x))

    在使用时,cube(5)将等于5×5×5,也可以在括号中使用变量,运算式

  • 注意:替换是文本替换,于是使用运算式作参数时运算顺序可能与预期不同

    原则:定义时一切都要有括号:整个值与每次参数出现都要括号

  • 宏可以带多个参数,例如:#define MIN(a,b) ((a)>(b)?(b):(a))

  • 宏可以嵌套其它宏

多个源代码文件

  • 如果main()里面的代码太长,拆分为多个函数

    同理,一个源代码文件太长,拆分为几个文件

  • 直接将不同函数放到不同的.c文件是无法编译的

  • 使用多个源文件代码需要用到项目:

    1. 在编译器中创建新项目
    2. 在项目中添加所需的.c代码文件
    3. 注意,有时编译器有分开的编译与构建选项,前者对单个源代码文件编译,后者链接整个项目
  • 一个.c文件是一个编译单元,编译器每次编译只处理一个编译单元

头文件

  • 当不同函数拆分到不同.c文件后,倘若在编译一个函数时,没有对其中另一个函数的声明,编译器将猜测这另一个函数的参数类型为int

    这样极易在数据类型不同时出错

  • 因此,需要一个中间媒介来存放函数原型,以便编译器得知函数原型

    这个文件就是头文件(.h文件)

  • 在需要调用某个函数时,将这个函数的函数原型放到一个头文件中

    这个头文件同其它代码文件一样放到项目当中

    在需要调用这个函数的源代码.c文件中#include “这个头文件.h”

    在这个函数本身的代码中也要放这个头文件

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

    它的作用是将所include的文件的全部文本插入它所在的地方

    • #include 后的 ' '< > 表示了这个指令指出插入文件的两种形式

      " "先到当前目录(.c所在目录)里寻找,如果没有再去编译器指定目录

      < >只在指定目录(编译器知道自己的标准库的头文件)

    #include本身不用来引入库,它只是让编译器知道函数原型,例如stdio.h文件中只有printf的原型,实际的代码在别处

  • 一般的做法是:任何.c文件都有对应的同名.h文件,将所有对外公开的函数原型和全局变量声明放入

  • 不对外公开的函数:在函数/全局变量前加static使得其只在所在的编译单元(.c文件中)中被使用

声明

  • 在项目某处定义一个全局变量,在另一文件中想使用它

  • 在头文件中,使用前缀extern便可声明一个全局变量,例如

    //在某个.C文件中定义了一个全局变量:
    int gAll = 12;
    //在另一个.C文件中想使用它,则在头文件里写入:
    extern int gAll;
    //这样就向编译器声明了一个全局变量
    
  • 由此我们可知,C中的定义与声明是两码事:

    定义产生代码,分配内存,告诉编译器函数的具体作用或变量的值;如:函数 / 全局变量

    声明不产生代码,只用于告诉编译器变量名称和类型;如:函数原型/变量/结构/宏/枚举/类型等的声明 / inline函数

  • 关于头文件与声明:一般只有声明(各个函数原型与对全局变量的声明)可以放入头文件

    否则会导致一个项目多个编译单元中出现重名实体

  • 为了规避上述问题,利用条件编译和宏,引入标准头文件结构

    #ifndef __头文件名_H__ //如果没有定义过头文件这个宏
    #define __头文件名_H__ //就定义这个宏
    
    //头文件内容
    
    #endif //宏的内容到此结束
    //如果已经被定义过了,上述内容就不会再被编译了
    //由此保证了这个头文件在一个编译单元中只会被#include一次
    

posted on   无术师  阅读(4)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了

统计

点击右上角即可分享
微信分享提示