C语言-预处理与库

news2024/12/23 10:16:27

预处理、动态库、静态库

1. 声明与定义分离

一个源文件对应一个头文件

注意:

  • 头文件名以 .h 作为后缀
  • 头文件名要与对应的原文件名 一致

例:

源文件:01_code.c

#include <stdio.h>
int num01 = 10;
int num02 = 20;
void add(int a, int b)
{
    int sum = a + b;
    printf("%d + %d = %d\n", a, b, sum);
}

void mul(int a, int b)
{
    int mul = a + b;
    printf("%d + %d = %d\n", a, b, mul);
}

头文件:01_code.h

extern int num01;
extern int num02;
extern void add(int, int);
extern void mul(int, int);

测试文件:test.c

#include <stdio.h>
#include "01_code.h"

int main(int argc, char const *argv[])
{
    printf("num01 = %d\n", num01);
    printf("num02 = %d\n", num02);
    add(10, 20);
    mul(10, 20);
    return 0;
}

编译:

  • 命令:gcc test.c 01_code.c ./a.out

  • 输出:

    num01 = 10
    num02 = 20
    10 + 20 = 30
    10 + 20 = 30
    

2. 预处理

2.1 c语言编译过程

gcc -E hello.c -o hello.i 	1、预处理
gcc -S hello.i –o hello.s 	2、编译
gcc -c hello.s -o hello.o 	3、汇编
gcc hello.o -o hello_elf 	4、链接

1、预编译

  • .c 中的头文件展开宏展开
  • 生成的文件是 .i 文件

2、编译

  • 将预处理之后的 .i 文件生成 .s 汇编文件

3、汇编

  • .s 汇编文件 生成 .o 文件

4、链接

  • .o 文件 链接成目标文件(即可执行文件)

预编译包含

展开头文件
定义头文件
选择性编译

注意:
	预编译的内容以 # 开头

2.2 include

作用:展开头文件

语法:

#include <> 

尖括号包含的头文件, 在 系统指定的路径下 找头文件

#:表示预编译

#include ""

双引号 包含头文件,先在当前目录下找 头文件,找不到,再到系统指定的路径下找

注意:

1、include 经常用来包含头文件,可以包含 .c 文件,但是大家不要包含 .c 因为 include 包含的文件会在预编译被展开,如果一个.c 被包含多次,展开多次,会导致函数重复定义。所以不要包含.c 文件

2、预处理只是对 include 等预处理操作进行处理,并 不会进行语法检查,这个阶段有语法错误也不会报错,第二个阶段即 编译阶段才进行语法检查

例:

#include "01_code.h"

//等价于 下面, 即在源文件中展开下面代码

extern int num01;
extern int num02;
extern void add(int, int);
extern void mul(int, int);

2.2 宏:define

作用:在预处理 处理定义 类似于 变量函数的东西。即:宏是在预编译的时候进行替换 。

2.2.1 不带参宏

语法:

#define 宏名 值   //宏定义

#undef 宏名       //取消宏定义

注意:

1、如果定义该类型的宏(不带参的宏),值可以省略

2、无需分号结束

3、在 宏定义后,取消定义前 可以使用

4、只能在 当前文件中 使用

例:

#include <stdio.h>
#define PI 3.14
int main(int argc, char const *argv[])
{
    printf("pi = %f\n", PI);
#undef PI  //取消宏定义
    return 0;
}
2.2.2 带参宏

语法:

#define  宏名(形参)  体

注意:

1、形参没有数据类型

2、带参宏带参函数的区别

  • 宏:在预编译时 对其进行 替换,如果一个文件中多次使用宏,那意味着要替换多次,此时就需占用内存,所以占据的内存多

    • 产生的预编译时期
    • 占内存多
    • 速度快
  • 函数:在程序运行时在代码区存储一份,每次调用该函数都需在代码区寻找,将其放入栈内存中(压栈),当函数执行完毕后,从栈中移除(弹栈)

    • 产生在运行时
    • 占内存少

例:

#include <stdio.h>
#define ADD(a, b) a+b
#define MUL(a, b) a*b
#define MUL02(a, b) (a)*(b)
int main(int argc, char const *argv[])
{
    int sum = ADD(20, 30);
    printf("sum=%d\n", sum);

    int mul = MUL(20, 30);
    printf("mul=%d\n", mul);

    int mul02 = MUL(20+10, 30+10);  //20 + 10 * 30 +10
    printf("mul=%d\n", mul02);

    int mul03 = MUL02(20+10, 30+10); //(20 + 10) * (30 + 10)
    printf("mul=%d\n", mul03);

    return 0;
}

在这里插入图片描述

2.2.3 小结
  • 宏就是在预编译时期对其进行替换

  • 不带参宏替换的是一个值

  • 带参宏替换的是一段代码

2.3 选择性编译

作用:选择代码是否被编译

语法:
在这里插入图片描述

例1:判断存在

优点:节省内存,只加载需要的部分

#include <stdio.h>

int main(int argc, char const *argv[])
{
    #ifdef  XXX
        printf("有定义宏名为XXX的宏\n");
    #else
        printf("没定义宏XXX\n");
    #endif

    return 0;
}

在这里插入图片描述
在这里插入图片描述

编译时定义宏:

在这里插入图片描述

例2:判断不存在,和头文件配合使用,防止多次引用头文件

#include <stdio.h>
#include "04_test.h"
#include "04_test.h"
int main(int argc, char const *argv[])
{
    #ifndef YYY
        printf("1111\n");
    #else
        printf("2222\n");
    #endif
    return 0;
}

头文件:04_test.h

#ifndef TEST
#define TEST
extern int num;
//...
#endif

#ifndef 使用含义:

1、第一次引用头文件,没有定义TEST宏,然后定义,再写头文件内容;

2、假如再次引用头文件时,第一次已经定义过TEST宏了,所以直接结束,啥也不干。

源码写法:

在这里插入图片描述

例3:判断是否成立

#include <stdio.h>
int main(int argc, char const *argv[])
{
    #if ScORE > 85
        printf("A\n");
    #elif ScORE > 70
        printf("B\n");
    #elif ScORE >= 60
        printf("c\n");
    #else
        printf("D\n");
    #endif
    return 0;
}

在这里插入图片描述

3. 库

概念:库也叫代码库,可以把一个些目标文件合并在一起方便使用。

3.1 分类

静态库

动态库

静态库、动态库的区别:

在这里插入图片描述

注意:

  • 程序中引入的文件在动态库与静态库同时存在两份
  • 静态编译程序引入静态库中的该文件
  • 动态编译程序引入动态库中的该文件

3.2 编译命令

动态编译:

gcc 源文件名 -o 生成的可执行文件名

静态编译:

gcc -static 源文件名 -o 生成的可执行文件名

3.3 静态库

3.3.1 制作
gcc -c 源文件名.c -o 生成的二进制文件名.o
ar rc lib静态库名称.a 生成的二进制文件名.o

注意:静态库起名的时候必须 lib 开头.a 结尾

步骤:

  • 新建文件夹: 06_code

  • 源文件:myfun.c

    #include <stdio.h>
    
    void add(int a, int b)
    {
        printf("my_sum = %d\n", (a+b));
    }
    
    void mul(int a, int b)
    {
        printf("my_mul = %d\n", (a*b));
    }
    
  • 头文件:myfun.h

    extern void add(int a, int b);
    extern void mul(int a, int b);
    
  • 制作

    在这里插入图片描述

3.3.2 使用

情况1:使用静态库的文件与静态库 在同一文件夹下

命令:

gcc 源文件名 静态库名称 -o 生成的可执行文件名

测试文件:test01.c

#include <stdio.h>
#include "myfun.h"   //可以不写,但是会报警告
int main(int argc, char const *argv[])
{
    add(10, 3);
    return 0;
}

编译:

在这里插入图片描述

情况2:使用静态库的文件与静态库 不在同一文件夹下

注意:

  • 为了让静态库文件与其对应的头文件和使用静态库文件不在同一文件夹下,所以

    • 创建includes与libs文件夹

    • includes文件用于存储头文件

    • libs文件夹存储静态库文件

      mkdir includes
      mkdir libs
      mv myfun.h includes/
      mv libmyfun.a libs/
      
  • 参数

    -L 引用的静态库所在的路径
    -l 静态库名, 去掉lib与.a
    -I 头文件所在路径
    
  • 命令

    gcc 源文件名 -L 静态库所在的路径 -l 静态库名 -I 头文件所在路径 -o 生成的可执行文件名
    

情况3:静态库文件与对应的头文件 在系统文件夹下

  • 系统库路径:

    /usr/include 		存储头文件
    /usr/lib 或 /lib 	存储库文件
    
  • 注意:

    # 为了让静态库文件与其对应的头文件和系统文件夹下,所以需要移动
    sudo mv includes/myfun.h /usr/include
    sudo mv libs/libmyfun.a /usr/lib  
    
  • 命令:

    gcc 源文件名 -l 静态库名 -o 生成的可执行文件名  
    

3.4 动态库

3.4.1 制作

命令:

gcc -shared 源文件名 -o 生成的动态库文件名.so
3.4.2 使用

情况1:使用动态库的文件与动态库在同一文件夹下

命令:

gcc 源文件名 动态库名称 -o 生成的可执行文件名  

情况2:使用动态库的文件与动态库不在同一文件夹下

命令:

gcc 源文件名 -L 动态库所在路径 -l 动态库名称 -I 头文件所在路径

注意:

  • 动态库名需要去掉前面的 lib 与后面 .so

情况3:静态库文件与对应的头文件在系统文件夹下

命令:

gcc 源文件名 -l 静态库名 -o 生成的可执行文件名

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1277818.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

08-中介者模式-C语言实现

中介者模式&#xff1a; Define an object that encapsulates how a set of objects interact.Mediator promotes loose coupling by keeping objects from referring to each other explicitly,and it lets you vary their interaction independently.&#xff08;用一个中介对…

【Python3】【力扣题】383. 赎金信

【力扣题】题目描述&#xff1a; 题解&#xff1a; 两个字符串ransomNote和magazine&#xff0c;ransomNote中每个字母都在magazine中一一对应&#xff08;顺序可以不同&#xff09;。 即分别统计两个字符串中每个字母出现的次数&#xff0c;ransomNote中每个字母的个数小于等…

分享83个节日PPT,总有一款适合您

分享83个节日PPT&#xff0c;总有一款适合您 83个节日PPT下载链接&#xff1a;https://pan.baidu.com/s/1xX4tVpl3sSW-d2nlFzN-mg?pwd6666 提取码&#xff1a;6666 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不易…

openGauss学习笔记-137 openGauss 数据库运维-例行维护-检查和清理日志

文章目录 openGauss学习笔记-137 openGauss 数据库运维-例行维护-检查和清理日志137.1 检查操作系统日志137.2 检查openGauss运行日志137.3 清理运行日志 openGauss学习笔记-137 openGauss 数据库运维-例行维护-检查和清理日志 日志是检查系统运行及故障定位的关键手段。建议按…

R语言实验三

1、读取一个文件并进行如下操作。 ①使用命令清空工作空间&#xff0c;使用read.table读取exam_1.txt文件&#xff0c;将文件保存到data变量中&#xff0c;数据第一行设置为列名&#xff0c;第一列是行名。 ②判断对象data是否为矩阵。 ③将对象转换为矩阵&#xff0c;记为d…

pyqt5使用pyqtgraph实现动态热力图

pyqt5使用pyqtgraph实现动态热力图 一、效果图 二、流程 1、打开Designer创建一个UI界面 2、把UI转成py 3、创建一个main.py文件 4、在main文件中渲染画布、创建初始数据、画热力图、创建更新数据线程、绑定按钮触发事件三、UI界面 其中h_map.py代码如下: # -*- coding: ut…

【多线程】-- 09 线程同步之三大不安全案例举例

多线程 6 线程同步 “多个线程操作同一个资源” 处理多线程问题时&#xff0c;多个线程访问同一个对象&#xff0c;并且某些线程还想修改这个对象&#xff0c;这时候就需要线程同步。线程同步其实就是一种等待机制&#xff0c;多个需要同时访问此对象的线程进入这个对象的等…

1.0 十大经典排序算法

分类 算法 本系列算法整理自&#xff1a;https://github.com/hustcc/JS-Sorting-Algorithm 同时也参考了维基百科做了一些补充。 排序算法是《数据结构与算法》中最基本的算法之一。 排序算法可以分为内部排序和外部排序&#xff0c;内部排序是数据记录在内存中进行排序&#…

vue3使用vue-router路由(路由懒加载、路由传参)

vue-router 是 vue的一个插件库 1. 专门用来实现一个SPA单页面应用 2 .基于vue的项目基本都会用到此库 SPA的理解 1) 单页Web应用&#xff08;single page web application&#xff0c;SPA&#xff09; 2) 整个应用只有一个完整的页面 3) 点击页面中的链接不会刷新页面, 本…

2023年12月3日支付宝蚂蚁庄园小课堂今日答案是什么?

问题&#xff1a;雪天行车&#xff0c;路面会有不少前车行驶的轨迹&#xff0c;最好&#xff1f; 答案&#xff1a;顺着前车轨迹行驶 解析&#xff1a;雪天路面湿滑&#xff0c;而且可能有冰雪等堆积物遮盖路面&#xff0c;所以&#xff0c;最好顺着前车轨迹减速慢行&#xf…

网络和Linux网络_8(传输层)TCP协议_续(流量控制+滑动窗口+拥塞控制+紧急指针+listen第二个参数)

目录 1. 流量控制 2. 滑动窗口 2.1 滑动窗口概念 2.2 滑动窗口模型详解 高速重发控制&#xff08;快重传&#xff09; 3. 拥塞控制和拥塞窗口 4. 延迟应答 5. 捎带应答 6. 面向字节流 7. 粘包问题 8. 16位紧急指针 9. listen的第二个参数 10. TCP总结异常情况与UD…

【开箱即用】前后端同时开源!周末和AI用Go语言共同研发了一款笔记留言小程序!

大家好&#xff0c;我是豆小匠。 真的是当你在怀疑AI会不会取代人类的时候&#xff0c;别人已经用AI工具加速几倍的生产速度了… 周末体验了和AI共同开发的感受&#xff0c;小项目真的可以一人全干了… 本次实验使用的AI工具有两个&#xff1a;1. GitHub Copilot&#xff08;…

怎么通过邻接矩阵求图的通路总数和回路总数?

通路总数 邻接矩阵所有元素的之和 回路总数 邻接矩阵对角线元素之和

Unittest单元测试之unittest用例执行顺序

unittest用例执行顺序 当在一个测试类或多个测试模块下&#xff0c;用例数量较多时&#xff0c;unittest在执行用例 &#xff08;test_xxx&#xff09;时&#xff0c;并不是按从上到下的顺序执行&#xff0c;有特定的顺序。 unittest框架默认根据ACSII码的顺序加载测试用例&a…

有文件实体的后门无文件实体的后门rootkit后门

有文件实体后门和无文件实体后门&RootKit后门 什么是有文件的实体后门&#xff1a; 在传统的webshell当中&#xff0c;后门代码都是可以精确定位到某一个文件上去的&#xff0c;你可以rm删除它&#xff0c;可以鼠标右键操作它&#xff0c;它是有一个文件实体对象存在的。…

ClassCMS2.4漏洞复现

CMS源码在附件中 环境搭建 使用phpstudy2016搭建web环境&#xff0c;php版本为5.5 安装CMS 这里选择Mysql数据库进行安装 用户名和密码都写默认的admin方便记忆 输入完成后点击安装 点击安装 CMS的安装过程中有个报错忽略就好&#xff0c;登录不进后台的话刷新一下页面 进入了C…

1688API接口系列,1688开放平台接口使用方案(商品详情数据+搜索商品列表+商家订单类)

1688商品详情接口是指1688平台提供的API接口&#xff0c;用于获取商品详情信息。通过该接口&#xff0c;您可以获取到商品的详细信息&#xff0c;包括商品标题、价格、库存、描述、图片等。 要使用1688商品详情接口&#xff0c;您需要先申请1688的API权限&#xff0c;并获取ac…

LeetCode - 965. 单值二叉树(C语言,二叉树,配图)

二叉树每个节点都具有相同的值&#xff0c;我们就可以比较每个树的根节点与左右两个孩子节点的值是否相同&#xff0c;如果不同返回false&#xff0c;否则&#xff0c;返回true。 如果是叶子节点&#xff0c;不存在还孩子节点&#xff0c;则这个叶子节点为根的树是单值二叉树。…

Course2-Week1-神经网络

Course2-Week1-神经网络 文章目录 Course2-Week1-神经网络1. 神经网络概述1.1 欢迎来到Course21.2 神经元和大脑1.3 引入神经网络-需求预测1.4 神经网络的其他示例-图像感知 2. 神经网络的数学表达式2.1 单层的神经网络-需求预测2.3 前向传播的神经网络-手写数字识别 3. Tensor…

蓝桥杯每日一题2023.12.2

题目描述 蓝桥杯大赛历届真题 - C 语言 B 组 - 蓝桥云课 (lanqiao.cn) 题目分析 答案&#xff1a;3598180 由题目分析可以知道&#xff0c;给小明发的牌一共有13种类型&#xff0c;每种类型的牌一共有四张。对于每种牌&#xff0c;我们都有5种选择&#xff0c;不拿、拿一张、…