嵌入式学习之路 13(C语言基础学习——预处理命令)

news2025/1/11 10:11:17

编程流程

在进行程序开发时,通常遵循编辑源代码、编译、运行和调试这几个主要步骤。

  1. 编辑源代码:使用文本编辑器创建或修改程序的源代码,这是整个编程过程的起点。
  2. 编译:将源代码转换为可执行文件的关键步骤。
    • 预处理:这是编译过程的前置阶段,通过执行预处理命令来处理源代码。预处理命令包括 #include (包含头文件)、#define (定义宏)等。例如,#include <stdio.h> 会将 stdio.h 头文件的内容插入到当前代码位置。预处理最终生成一个只包含纯 C 语言代码的中间文件。
      • 指令:gcc -E main.c -o main.i ,此命令执行预处理操作,并将结果输出到 main.i 文件。
    • 编译:对经过预处理的代码进行语法检查,并将其转换为汇编代码。这一阶段会检查代码的语法错误,如遗漏的分号、错误的变量使用等。
      • 指令:gcc -S main.c -o main.s ,生成汇编代码文件 main.s 。
    • 汇编:将汇编代码转换为机器代码,生成目标文件(通常以 .o 为扩展名)。
      • 指令:gcc -c main.s -o main.o ,生成目标文件 main.o 。
  3. 链接:将多个目标文件以及所需的库文件链接在一起,生成最终的可执行文件。例如,如果程序中使用了外部库函数,链接阶段会将这些函数的实现与当前代码连接起来,确保程序能够正常运行。
  4. 运行:执行生成的可执行文件,观察程序的输出结果。
  5. 调试:如果程序运行结果不符合预期,通过调试工具来查找和修复代码中的错误。


预处理

        预处理不属于 C 语言的核心部分,其主要作用是进行文本替换,包括宏定义、文件包含和条件编译。

宏定义

  • 语法形式:#define 标识符 字符串 或 #define 宏名 宏值
    • 例如:#define N 10 ,在代码中出现的 N 都会被替换为 10 。
    • 注意事项:预处理命令均以 # 开头,宏名的命名规则与普通标识符相同,通常写成大写以作区分。
  • 宏名的作用域:从定义处开始,到 #undef 结束。#undef 宏名 用于结束宏名的作用范围。需要注意的是,宏名的作用仅在预处理阶段发挥作用。
#include <stdio.h>

#define ARRAY_SIZE 10

int main()
{
    int a[ARRAY_SIZE] = {1,2,3,4,5,6,7,8,9,10};

	int i;

	for ( i=0; i<ARRAY_SIZE; i++ )
	{
		printf("%d ",a[i]);
	}

	printf("\n");

    return 0;
}

宏的应用

  • 提高代码的可读性。
  • 方便代码修改,实现一改全改。

带参宏定义(宏函数)

  • 语法:#define 宏名(参数) 宏值
    • 例如:#define ADD(a,b) a+b
    • 注意:虽然被称为宏函数,但它与真正的函数有本质区别。
  • 处理阶段不同:宏定义发生在预处理阶段,而函数在编译阶段。
  • 使用方式不同:宏在预处理阶段通过文本原样替换完成使用,其参数仅用于文本替换,不进行语法检查;函数在调用时使用,参数具有类型,编译阶段会进行类型检查。
  • 应用场景:对于一些短小的代码(通常不超过 5 行),可考虑写成带参宏。
#include <stdio.h>

#define ADD(a,b) a+b
#define SUB(a,b) a-b
#define MUL(a,b) a*b
#define DIV(a,b) a/b

int main()
{
	int x,y;
	printf("x and y : ");
	scanf("%d%d",&x,&y);

	printf("x + y = %d\n",ADD(x,y));
	printf("x - y = %d\n",SUB(x,y));
	printf("x * y = %d\n",MUL(x,y));
	printf("x / y = %d\n",DIV(x,y));

	return 0;
}

宏的副作用

        由于宏是原样替换,在进行一些运算时可能会产生意外的结果。例如,对于 #define ADD(a,b) a+b ,如果使用 ADD(2, 3) * 4 ,会被替换为 2 + 3 * 4 ,而不是期望的 (2 + 3) * 4 。为避免这种情况,能加括号的地方都应加上括号,如 #define ADD(a,b) (a+b) 。


文件包含

文件包含是 C 语言预处理的一个重要操作,通过 #include 指令来实现。

#include <文件名> 和 #include "文件名" 是两种常见的文件包含形式,它们的主要区别在于查找头文件的方式不同。

  • 当使用 <文件名> 时,编译器会直接到系统默认的路径去寻找对应的头文件。这些系统默认路径通常由编译器的设置决定,一般包含标准库的头文件所在的位置。
    • 例如:#include <stdio.h> ,编译器会在系统默认路径中查找 stdio.h 头文件。
  • 当使用 "文件名" 时,编译器首先会在当前目录下寻找指定的头文件,如果在当前目录下没有找到,才会到系统默认路径下寻找。
    • 例如,如果当前目录下有一个自定义的头文件 myheader.h ,可以使用 #include "myheader.h" 来包含它。

在实际编程中,了解文件包含的查找方式有助于我们正确组织和管理项目中的头文件。

例如,如果我们正在开发一个较大的项目,并且有许多自定义的头文件,为了避免命名冲突和提高代码的可维护性,通常会将自定义头文件放在特定的目录中,并在使用时使用相对路径或绝对路径来指定头文件的位置。

另外,对于一些大型项目,可能会涉及多个开发人员同时工作,此时需要建立统一的文件组织结构和包含规则,以确保每个人都能正确地引用所需的头文件。

如果在包含头文件时出现找不到文件的错误,我们可以首先检查文件名是否正确,然后根据使用的包含方式,确认查找路径是否正确。

总之,合理使用文件包含,并清楚其查找机制,能够有效地提高代码的可重用性和可维护性。


条件编译

条件编译是 C 语言预处理的一个重要特性,它允许根据不同的条件来决定哪些代码被编译,哪些代码被忽略。

常见的条件编译形式

  1. #ifdef 标识符
     程序段 1
    #else
     程序段 2
    #endif
     

    含义:它的作用是若所指定的标识符已经被# define 命令定义过,则在程序编译阶段编译程序段 1; 否则编译程序段 2。其中# else 部分可以没有。

    举例:假设定义了标识符 DEBUG ,在调试时可以有以下代码:

    #ifdef DEBUG
        printf("正在调试中...\n");
    #else
        // 正常运行时的代码
    #endif
  1. #ifndef 标识符
     程序段 1
    #else
     程序段 2
    #endif

    含义:只是第一行与第一种形式不同:将 "ifdef" 改为 "ifndef" 。它的作用是若标识符未被定义过则编译程序段 1; 否则编译程序段 2。这种形式与第一种形式的作用相反。

  2. #if 表达式
     程序段 1
    #else
     程序段 2
    #endif

    含义:它的作用是当指定的表达式值为真(非零)时就编译程序段 1; 否则编译程序段 2。可以事先给定条件,使程序在不同的条件下执行不同的功能。

    例如:#if 0 后面的程序段 1 会被当作注释处理。

用途

  1. 调试代码:在开发过程中,可以通过定义特定的标识符来开启或关闭调试输出,便于在不同的阶段控制代码的行为。

  2. 设计头文件:防止头文件被重复包含,通过条件编译来确保头文件中的内容只被处理一次。

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

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

相关文章

LeetCode.27.移除元素

题目描述&#xff1a; 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k&#xff0c;要通过此题&#xff0c;您需要执行以…

Mojo使用调试工具(Visual Studio Code)详解

Visual Studio Code 的 Mojo 扩展使您可以将 VS Code 的内置调试器与 Mojo 程序一起使用。(Mojo 扩展还支持调试 C、C++ 和 Objective-C。) 有关 VS Code 调试功能的完整介绍,请参阅 Visual Studio Code 中的调试。 本文介绍了可通过 Mojo 扩展获得的功能,以及 Mojo 调试…

【k8s集群部署篇】在openEuler环境下部署多master高可用kubernetes集群详细教程(V1.30版本)

【k8s集群部署篇】在openEuler环境下部署多master高可用kubernetes集群详细教程(V1.30版本) 一、相关名词介绍1.1 k8s简介1.2 Keepalived简介1.3 HAProxy简介二、本次实践介绍2.1 环境规划介绍2.2 本次实践简介三、所有节点基础环境配置3.1 主机配置工作3.2 关闭防火墙和seli…

[Unity实战]Mirror网络与Addressable场景管理

前言 很遗憾&#xff0c;Mirror包括UNET的场景管理不支持Addressables。我做手机游戏&#xff0c;需要实现服务器广播场景切换&#xff0c;但主程序没有场景&#xff0c;热更代码和资源都在AB包里。我已经在Google上搜索了一段时间&#xff0c;没有找到有用的解决方案。 Redd…

KVM——虚拟机中使用命令行形式安装虚拟机

目录 一. 删除虚拟机中的虚拟机步骤 二. 虚拟机中以命令行形式安装虚拟机 一. 删除虚拟机中的虚拟机步骤 这样就删除成功了。 二. 虚拟机中以命令行形式安装虚拟机 安装 [rootkvm-server ~]# virt-install --connect qemu:///system -n vm10 -r 3100 --disk path/var/lib…

【Material-UI】复杂按钮 (Complex Button) 自定义详解

文章目录 一、ButtonBase 组件简介二、实例讲解&#xff1a;创建复杂的图片按钮1. 样式定义2. 核心组件构建3. 交互效果 三、高级自定义技巧1. 响应式设计2. 动态内容与动画 四、总结 在现代 Web 应用中&#xff0c;按钮不仅仅是一个点击交互元素&#xff0c;它们也承载着传递信…

批发行业进销存-登录适配 android 横竖屏幕 源码CyberWinApp-SAAS 本地化及未来之窗行业应用跨平台架构

一、横竖屏切换的意义 以下是移动端横屏竖屏可切换在进销存中的一些重要应用&#xff1a; a、数据录入与查看 在录入商品信息、库存数量等大量数据时&#xff0c;横屏模式可以提供更宽阔的输入区域&#xff0c;减少输入错误。例如&#xff0c;在输入长串的商品编码或详细的商…

数值分析【1】

第一章&#xff1a; 相对误差 四 规格化浮点数 秦九韶 第二章&#xff1a; 二分法 不动点迭代法 Taylor 埃特金加速 构造新的同根函数 有个公式 牛顿法&#xff08;切线法&#xff09;、弦截法&#xff08;割线法&#xff09;

谷粒商城实战笔记-137-商城业务-首页-整合dev-tools渲染一级分类数据

文章目录 一&#xff0c;使用热加载工具spring-boot-devtools1&#xff0c;引入devtools依赖2&#xff0c;ctrlshiftf9 编译静态资源 二&#xff0c;thymeleaf原理三&#xff0c;渲染一级分类 一&#xff0c;使用热加载工具spring-boot-devtools 因为我们采用的前后端一体的开…

Windows 平台 Docker Protainer可视化平台,忘记登录密码,重置密码

下载protainer 的文件 运行重置密码的密令 docker run --rm -v C:\Users\Administrator\AppData\Local\Docker:/data portainer/helper-reset-password成功运行后可以看到重置的密码 把key 和db 文件重新上传到容器里 docker cp portainer.key portainer:/data/portainer.k…

5.10.结构化开发方法-结构化分析

案例分析第一题会考 结构化特点&#xff1a;自顶向下&#xff0c;逐步分解&#xff0c;面向数据。三大模型&#xff1a;功能模型&#xff08;数据流图&#xff09;、行为模型&#xff08;状态转换图&#xff09;、数据模型(E-R图)以及数据字典。 数据流图DFD 数据流图DFD基…

网络IO模型及零拷贝问题

文章目录 BIOaccept监听案例read案例利用多线程 NIO案例NIO存在的问题 IO multiplexing-IO多路复用文件描述符(FD&#xff0c;句柄&#xff09; 是什么reactor反应模式 select方法优点缺点小总结 poll方法优点缺点 epoll方法 Reactor模式单Reactor单线程 单Reactor多线程主从Re…

linux docker安装 gitlab后忘记root密码如何找回

1. docker ps - a 查看当前gitlab 当前的id2. docker exec -it gitlab /bin/bash 进入docker git 容器中【gitlab 注意可以上图中的name&#xff0c;也可以是id都可以的】,如下图3.gitlab-rails console -e production 输入该指令&#xff0c;启动Ruby on Rails控制台&…

Copy as cURL 字段含义

当前端在开发过程中&#xff0c;遇到接口错误反馈给后端人员时&#xff0c;一般在此接口处右键复制为cURL。 格式如下&#xff1a; curl https://xxx.xxx.cn/xxx/xxx/management/record/list \-H accept: application/json, text/plain, */* \-H accept-language: zh-CN,zh;q0…

LVS集群中的负载均衡技术

目录 1、LVS技术原理 2、NAT模式原理及部署方法 1、工作原理 2、部署方法 1、网络配置 2、软件安装与启用 3、测试 2、DR模式工作原理及部署方法 1、工作原理 2、部署方法 1、网络配置 2、解决vip响应问题 3、测试 3、ipvsadm命令及参数 1.管理集群服务中的增删…

数据同步工具之Flink CDC

Flink CDC&#xff08;Change Data Capture&#xff09;是基于Apache Flink的一个扩展&#xff0c;用于捕获和处理数据库中的数据变化。它能够实时捕获关系数据库中的数据变更&#xff08;如插入、更新、删除操作&#xff09;&#xff0c;并将这些变更流式传输到Flink进行处理。…

【OceanBase系列】—— OceanBase应急三板斧

作者&#xff1a; 花名&#xff1a;洪波&#xff0c; OceanBase 数据库解决方案架构师 目前随着OceanBase数据库越来越流行&#xff0c;社区已经有很多用户在生产环境使用了OceanBase&#xff0c;也有不少用户的核心业务用到了OceanBase数据库&#xff0c;在使用OceanBase数据库…

演示:基于WPF的DrawingVisual开发的矢量地图

一、目的&#xff1a;基于WPF的DrawingVisual开发的矢量地图 二、预览 默认样式 深黑样式 深蓝色样式 深蓝色透明样式 三、环境 VS2022&#xff0c;Net7&#xff0c;GDAL,审图号为GS(2019)1822号矢量数据,DrawingVisual 四、主要功能 支持多种显示样式&#xff08;默认样式&…

代码随想录27期|Python|Day39|​62. 不同路径​|​63. 不同路径 II​

62. 不同路径 简单题。由于规定了只能走右边和下边&#xff0c;所以右下角的值等于左对角线的两数之和。 1、确定dp和下标&#xff1a;二维数组&#xff0c;i&#xff0c;j分别为行和列&#xff0c;dp值为所需步数&#xff1b; 2、 初始化&#xff1a;只有上边和左边全部初始…

Java二十三种设计模式-享元模式(12/23)

享元模式&#xff1a;高效管理大量对象的设计模式 引言 在软件开发中&#xff0c;有时需要处理大量相似或重复的对象&#xff0c;这可能导致内存使用效率低下和性能问题。享元模式提供了一种解决方案&#xff0c;通过共享对象的共同部分来减少内存占用。 基础知识&#xff0c…