C语言-预处理与库

预处理、动态库、静态库

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 +10printf("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  XXXprintf("有定义宏名为XXX的宏\n");#elseprintf("没定义宏XXX\n");#endifreturn 0;
}

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

编译时定义宏:

在这里插入图片描述

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

#include <stdio.h>
#include "04_test.h"
#include "04_test.h"
int main(int argc, char const *argv[])
{#ifndef YYYprintf("1111\n");#elseprintf("2222\n");#endifreturn 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 > 85printf("A\n");#elif ScORE > 70printf("B\n");#elif ScORE >= 60printf("c\n");#elseprintf("D\n");#endifreturn 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.mzph.cn/news/191466.shtml

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

相关文章

国标GBT 27930关键点梳理

1、充电总流程 整个充电过程包括六个阶段:物理连接完成、低压辅助上电、充电握手阶段、充电参数配置阶段、充电阶段和充电结束阶段。 在各个阶段,充电机和 BMS 如果在规定的时间内没有收到对方报文或没有收到正确报文,即判定为超时(超时指在规定时间内没有收到对方的完整数据包…

PyLMKit(4):基于本地知识库的检索增强生成RAG

基于本地知识库的检索增强生成RAG 0.项目信息 日期&#xff1a; 2023-12-2作者&#xff1a;小知课题: RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09;是一种利用知识库检索的方法&#xff0c;提供与用户查询相关的内容&#xff0c;从而…

750mA Linear Charger with Power Path Management

一、General Description YHM2711 is a highly integrated, single-cell Li-ion battery charger with system power path management for space-limited portable applications. The full charger function features Trickle-charge, constant current fast charge and const…

Leetcode144. 二叉树的前序遍历-C语言

文章目录 题目介绍题目分析解题思路1.创建一个数组来储存二叉树节点的值2.根据二叉树的大小来开辟数组的大小3.边前序遍历边向创建的数组中存入二叉树节点的值 完整代码 题目介绍 题目分析 题目要求我们输出二叉树按前序遍历排列的每个节点的值。 解题思路 1.创建一个数组来…

TCA9548A I2C 多路复用器 Arduino 使用相同地址 I2C 设备

在本教程中&#xff0c;我们将学习如何将 TCA9548A I2C 多路复用器与 Arduino 结合使用。我们将讨论如何通过整合硬件解决方案来使用多个具有相同地址的 Arduino 的 I2C 设备。通过使用 TCA9548A I2C 多路复用器&#xff0c;我们将能够增加 Arduino 的 I2C 地址范围&#xff0c…

大模型的开源闭源

文章目录 开源&闭源开源和闭源的优劣势比较开源和闭源对大模型技术发展的影响开源与闭源的商业模式比较国内的大模型开源和闭源的现状和趋势 开源和闭源&#xff0c;两种截然不同的开发模式&#xff0c;对于大模型的发展有着重要影响。 开源让技术共享&#xff0c;吸引了众…

【LVS实战】04 LVS+Keepalived实现负载均衡高可用

一、介绍 Keepalived 是一个用于 Linux 平台的高可用性软件。它实现了虚拟路由器冗余协议 (VRRP) 和健康检查功能&#xff0c;可以用于确保在多台服务器之间提供服务的高可用性。Keepalived 可以检测服务器的故障&#xff0c;并在主服务器宕机时&#xff0c;自动将备份服务器提…

Golang数据类型(字符串)

字符串重要概念 根据Go语言官方的定义&#xff1a; In Go, a string is in effect a read-only slice of bytes. 意思是Go中的字符串是一组只读的字节切片&#xff08;slice of bytes&#xff09;&#xff0c;每个字符串都使用一个或多个字节表示&#xff08;当字符为 ASCII 码…

Spring Boot实现热部署

Spring Boot提供了一个名为spring-boot-devtools的开发工具&#xff0c;它可以实现热部署功能。通过使用spring-boot-devtools&#xff0c;可以在修改了resources目录下的内容后&#xff0c;自动重新加载应用程序&#xff0c;而无需手动重启。 以下是使用spring-boot-devtools…

Springboot3+vue3从0到1开发实战项目(二)

前面完成了注册功能这次就来写登录功能&#xff0c; 还是按照这个方式来 明确需求&#xff1a; 登录接口 前置工作 &#xff1a; 想象一下登录界面&#xff08;随便在百度上找一张&#xff09; 看前端的能力咋样了&#xff0c; 现在我们不管后端看要什么参数就好 阅读接口文档…

人工智能对我们的生活影响有多大?

一、标题解析 本文标题为“人工智能对我们的生活影响有多大&#xff1f;”&#xff0c;这是一个典型的知乎风格SEO文案标题&#xff0c;既能够吸引读者&#xff0c;又能够体现文章的核心内容。 二、内容创作 1. 引言&#xff1a;在开头&#xff0c;我们可以简要介绍人工智能…

TLSF算法概念,原理,内存碎片问题分析

TLSF算法介绍 TLSF&#xff08;Two-Level Segregated Fit&#xff0c;两级分割适应算法&#xff09;。 第一级&#xff08;first level,简称fl&#xff09;&#xff1a;将内存大小按2的幂次方划分一个粗粒度的范围&#xff0c;如一个72字节的空闲内存的fl是6&#xff08;72介…

Docker快速入门(docker加速,镜像,容器,数据卷常见命令操作整理)

Docker本质是将代码所需的环境依赖进行打包运行,而在Docker中最重要的是镜像和容器 镜像:可以简单地理解为每启动一个docker镜像就会占用计算机一个进程,这个进程和另外起的docker镜像的进程是相互独立的,以数据库为例,每个镜像都会copy一份数据库,在他所在的进程中.别的镜像在…

电脑提示mfc100u.dll缺失如何解决?分享有效的5个解决方法

由于各种原因&#xff0c;电脑可能会出现一些问题&#xff0c;其中之一就是电脑提示mfc100u.dll的错误。这个问题可能会导致电脑无法正常运行某些程序或功能。为了解决这个问题&#xff0c;我将分享验证有效的五个修复方法&#xff0c;帮助大家恢复电脑的正常运行。 首先&#…

159.库存管理(TOPk问题!)

思路&#xff1a;也是tok的问题&#xff0c;与上篇博客思路一样&#xff0c;只不过是求前k个小的元素&#xff01; 基于快排分块思路的代码如下&#xff1a; class Solution { public:int getkey(vector<int>&nums,int left,int right){int rrand();return nums[r%…

算法通关村第四关—栈的经典算法问题(白银)

emsp;emsp;栈的经典算法问题 一、括号匹配问题 emsp;首先看题目要求&#xff0c;LeetCode20.给定一个只包括’(‘&#xff0c;)’&#xff0c;‘{&#xff0c;’&#xff0c;[&#xff0c;]的字符串s&#xff0c;,判断字符串是否有效。有效字符串需满足&#xff1a; 1.左括号…

我爱上这38个酷炫的数据大屏(附 Python 源码)

随着大数据的发展&#xff0c;可视化大屏在各行各业得到越来越广泛的应用。 可视化大屏不再只是电影里奇幻的画面&#xff0c;而是被实实在在地应用在政府、商业、金融、制造等各个行业的业务场景中&#xff0c;切切实实地实现着大数据的价值。 所以本着学习的态度&#xff0…

win10 修改任务栏颜色 “开始菜单、任务栏和操作中心” 是灰色无法点击,一共就两步,彻底解决有图有真相。

电脑恢复了一下出厂设置、然后任务栏修改要修改一下颜色&#xff0c;之前会后来忘记了&#xff0c;擦。 查了半天文档没用&#xff0c;最后找到官网才算是看到问题解决办法。 问题现象: 解决办法: 往上滑、找到这里 浅色改成深色、然后就可以了&#xff0c;就这么简单。 w…

P1 嵌入式开发之什么是Linux应用开发

目录 前言 01 .Linux应用与裸机编程、驱动编程之间的区别 1.1裸机编程&#xff1a; 1.2 驱动编程 1.3应用编程 前言 &#x1f3ac; 个人主页&#xff1a;ChenPi &#x1f43b;推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ &#x1f525; 推荐专栏2: 《Linux C应用编程&a…

Python练习题(二)

&#x1f4d1;前言 本文主要是【Python】——Python练习题的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句&am…