当前位置: 首页 > news >正文

C语言——预处理和指针

C语言——预处理和指针

  • 预处理
      • 宏定义
      • 宏的作用域
      • 带参的宏
    • 文件包含
    • 条件编译
  • 指针
    • 指针的概念
    • 指针的定义
    • 指针变量初始化
    • 指针+一维整型数组

预处理

编程的流程分为:编辑、编译、运行、调试四个阶段;
预处理属于编译阶段,编译过程又可以分为:预处理、编译、汇编、链接;

预处理
预处理是将代码中相关的预处理命令执行最终生成只包含c语言代码的文件,详细来说预处理过程实质上是处理“#”,将#include包含的头文件直接拷贝到.c当中;将#define定义的宏进行替换;将#if #else #endif定义的无用代码过滤掉,同时将代码中没用的注释部分删除等。
预处理所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。

编译,编译是对语法进行检查将源代码生成汇编代码。

汇编,汇编是将汇编代码生成机器代码。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。
目标文件由段组成。通常一个目标文件中至少有两个段:
1、代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
2、数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

链接,链接是将使用的其他代码链接到一起生成可执行文件。详细来说链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。

下面详细说明预处理的过程,预处理指令有宏定义、文件包含、条件编译;

宏定义

宏定义的语法形式:

#define 标识符 字符串
或者是#define 宏名 宏值
预处理命令都是以#开头的

例如:#define n 10
注意在定义宏时在宏值后面是不可以加分号的如果加了分号在预处理进行文本替换的时候会一并把分号一起替换了,比如说我在定义#define N 10的后面加上分号可以看到在进行预处理时N会被替换成10;
在这里插入图片描述

宏名的命名遵循标识符的命名规则,在定义宏名时为了区分宏名和普通变量名通常把宏名写成大写,比如#define N 10,这个宏定义的含义是将来代码中出现的的N都代表10,在编写代码是可以用N来表示10。这个本质就是在预处理的时候会进行文本替换也就是把宏名替换成宏值。通过预处理指令可以看到上述效果:

在这里插入图片描述
在这里插入图片描述
通过预处理指令gcc -E testH.c -o testH.i把testH.c文件只做预处理操作得到的目标文件testH.i打开testH.I可以看出宏名N被替换成了10。

宏的作用域

宏的作用域是从定义位置开始往下发挥作用

#include <stdio.h>int main(void)
{printf("N = %d\n", N);return 0;
}#define N 10void test(void)
{printf("N = %d\n", N);
}

在这里插入图片描述
预处理后的代码为:
显示

编译上述代码看到说main函数中的N未定义的错误,然后通过预处理指令对testH.c只做预处理操作可以看到main函数中的N并没有报错,进一步说明宏的作用域是从定义位置开始往下发挥作用。如果我们想限制宏的作用域应该怎么做呢?我可以通过**#define 宏名 宏值 #undef 宏名**来限制宏的作用域,通过下面的例子来详细说明:
在这里插入图片描述

在这里插入图片描述
上述代码中我把#define N 10宏定义限制在main函数的范围内,然后对testH.c文件只做预处理操作可以看出在进行预处理时只有main函数的N替换成了宏值10而test()函数中的N并没有替换成宏值10。所以**#define 宏名 宏值 #undef 宏名**具有限制宏的作用域的作用。

带参的宏

语法:
#define 宏名(参数) 宏值
比如说#define ADD(a, b) a+b这个宏在预处理时会把代码中的ADD(a, b)都替换成a+b从而实现两个数相加的效果,在形式上看着带参的宏有点像函数实际上带参的宏和函数是有本质上的区别的:
1、带参宏和函数的处理阶段不一样,宏是在预处理阶段而函数是在编译阶段;
2、二者的使用阶段也不一样,宏是在预处理阶段就使用结束了而函数是在调用的时候才会进行使用,宏的本质是进行文本的原样替换而函数的使用本质上是函数代码的调用,,宏的参数只是进行文本替换用的不会进行语法检查,而函数的参数是有类型的在编译阶段会进行类型的检查。

宏的副作用

使用宏是可能会是运算优先级发生改变下面以一个具体的例子说明吧;
在这里插入图片描述
在这里插入图片描述
在调用宏时理想的结果是先让1+2和3+4求和然后再将二者的和相乘可以在进行文本的原样替换时把MUL(1 + 2, 3 + 4)替换成了1 + 2*3+4所以计算结果是11;为了避免发生这样的结果通常在进行宏定义时该加括号的加括号。

文件包含

文件包含分为
1、#include <>
2、#include “”
二者的区别是:查找头文件的方式不一样,<>是到系统默认的路径去找头文件而""实现到当前目录下寻找如果没有再到系统默认的目录下寻找。

条件编译

条件编译总共有三种形式:
1、
# ifdef 标识符
程序段 1
#else
程序段 2
#endif

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

上述形式只是第一行与第一种形式不同:将 “ifdef” 改为 “ifndef” 。它的作用是若标识符未被定义过则编译程序段 1; 否则编译程序段 2。这种形式与第一种形式的作用相反。
3、
#if 表达式
程序段1
#else
程序段2
#endif

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

指针

指针的概念

指针就是地址而地址就是内存单元的编号。指针也是一种数据类型是专门用来处理地址这种数据的类型。

指针的定义

数据类型 变量名
语法:
基类型 * 变量名

其中基类型包含整型、浮点型、字符型、数组类型、指针类型、结构体类型 、函数类型等等;
该类型表示指针类型指向的内存空间所存放的数据是什么样的类型;
**“*”**表示表示此时定义的是一个 指针类型 的变量;
变量名符合标识符的命名规则;

举个例子说明吧:
int a = 10; //表示a的内存空间中存放的是整型类型的数据;
float b = 10;//表示b的内存空间中存放的是浮点型类型的数据;
int p = &a;
int p = &b;
其中&a表示a所在内存空间的首地址,表示获得了一块 可以存在int型数据的内存空间的地址。
int
p;
int
含义 首先表示是一个 指针类型,表示指向int型数据的指针类型 。

指针变量的引用
int a = 10;
int *p = &a; 这里p指向a,因为p中保存了a的地址;

“*”是指针运算符,它是一种单目运算符,且运算的对象只能是地址;
*p:表示访问p所指向的基类型的内存空间这种访问是间接访问可以通过a直接访问;
*p的访问完整流程是:
1、首先拿出p中地址,到内存中定位
2、偏移出sizeof(基类型)大小的一块空间
3、将偏移出的这块空间,当做一个基类型变量来看
p最后的运算效果相当于就是一个基类型的变量,也就是p等价于a;

指针变量初始化

如果指针变量没有初始化此时就是随机值,该指针叫野指针。野指针对程序的执行是有风险的所以在初始化的时候必须让指针有明确的指向例如:
int a = 1;
int *p = &a;
int *p = NULL;此时p表示的是一个空指针,p的地址编号是0;

指针的赋值:
int *p;
p = NULL;

定义多个指针变量:
int*p,*q;
*是用来修饰变量的表示此时定义的是一个指针类型的变量;而不能写成int *p,q;如果这样写p代表的是一个指针变量而q是一个int类型的变量。

指针的作为函数的参数一个重要功能就是实现被调修改主调那么如何实现被调修改主调呢?
其实指针作为函数的参数通过把背调的地址传过来然后就能通过这个地址找到其在内存中所存在的位置,从而访问内存空间中存放的数据来实现被调修改主调的效果。

指针作为函数参数:
形参是一个指针类型的变量,用来接受实参而实参是要操作内存空间的地址;
实参是要修改谁就传谁的地址,且被调函数中一定要有*p运算;

下面以一个例子来说明值传递和址传递要注意的问题:

#include <stdio.h>void minMax(int a, int b, int *max, int *min)
{*max = a > b ? a : b;*min = a < b ? a : b;
}int main(void)
{int a = 0, b = 0;int max = 0, min = 0;scanf("%d %d", &a, &b);minMax(a, b, &max, &min);printf("max = %d min = %d\n", max, min);return 0;
}

在这里插入图片描述
通过上述代码我们可以看到在进行函数的传参时既有值传递也有址传递那什么时候用值传递什么时候用址传递呢?如果你想要通过形参去改变实参那么就要用址传递的形式。就比如说上述代码中我要从函数中带出一个最大值和一个最小值但是不能有返回值,那么就要实现形参改变实参的效果通过值传递是实现不了这个效果的所以max和min采用了址传递的方式,我们想改变的是max和min而a和b这两个数是不需要改变的所以采用值传递就可以了。

指针+一维整型数组

如果要定义一个一维数组指针那我们要定义一个什么类型的指针呢?谁又能代表数组首元素的地址呢?首先我们得理解数组名的含义,1、数组名代表数组的类型;2、数组名代表数组首元素的地址;由数组名的含义我们可以知道数组名的首元素可以代表数组首元素的地址,数组首元素也就是a[0]而a[0]对应的数据类型是int型代表a取了一块int类型数据的地址也就是int类型,所以a的类型是int型,这样我们在定义一个一维数组指针是要定义一个int*类型的指针例如:int a[50];int p = a;指的是创建一个int类型的变量也就是创建了一个指针p,p指向的是a数组所在的内存空间的首地址,p所指向内存空间里存放的数据的数据类型是int型。

指针的访问方式;
下面以一个具体的例子来说明指针的访问方式:

#include <stdio.h>void printArray(int *a, int len)
{int i = 0;for(i = 0; i < len; ++i){printf("%d\n", *(a + i));}
}int main(void)
{int a[] = { 1, 2, 3, 4, 5};printArray(a, 5);return 0;
}

上述程序实现了一个数组元素打印的过程,通过把数组首元素的地址传给函数在进行数组遍历的时候就能通过数组首元素的地址找到该数组所在的内存空间,在遍历数组元素时通过指针运算来控制指针的偏移使指针指向数组每一个元素所在空间的地址然后通过指针运算符来实现对数组元素的访问,从而实现数组元素的打印功能。其中(a + i)等价于a[i](a[i]还可以写成i[a]因为*(a + i)等价于*(i + a))。
总的来说数组作为函数参数 :
第一,形参要是数组形式,其本质上是一个指针类型变量例如int *a;除了传进一个指针还要传进去数组长度方便对数组的遍历;
第二,实参是数组名和数组长度,数组名代表的是数组首地址。

相关文章:

C语言——预处理和指针

C语言——预处理和指针 预处理宏宏定义宏的作用域带参的宏 文件包含条件编译 指针指针的概念指针的定义指针变量初始化指针一维整型数组 预处理 编程的流程分为&#xff1a;编辑、编译、运行、调试四个阶段&#xff1b; 预处理属于编译阶段&#xff0c;编译过程又可以分为&…...

iptables防火墙(一)

目录 1、Linux防火墙基础 2、iptables的四表五链结构 2.1 iptables的四表五链结构介绍 2.2 四表五链 2.2.1 四表 2.2.2 五链 2.3 包过滤的匹配流程 2.3.1 规则链之间匹配顺序 2.3.2 规则链内部的处理规则 2.3.3 数据包过滤的匹配流程 3、 编写防火墙规则 3.1 iptabe…...

(leetcode学习)50. Pow(x, n)

实现 pow(x, n) &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即&#xff0c;xn &#xff09;。 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n 10 输出&#xff1a;1024.00000示例 2&#xff1a; 输入&#xff1a;x 2.10000, n 3 输出&#xff1a;9.26100示例 …...

QT 5.12.0 for Windows 安装包 QT静态库 采用源码静态编译生成

qt-5.12.0-static.zip 下载地址(资源整理不易&#xff0c;下载使用需付费&#xff0c;且文件较大&#xff0c;不能接受请勿浪费时间下载): 链接&#xff1a;https://pan.baidu.com/s/1ftfHFG_jGFwVaOAvBVrNFg?pwdtvtp 提取码&#xff1a;tvtp...

【生成式人工智能-三-promote 神奇咒语RL增强式学习RAG】

如何激发模型的能力 提示词 promotCoTRL 增强式学习Reforcement learning提供更多的资料提供一些范例Incontext- learning 任务拆解让模型自己检查错误让模型多次生成答案Tree of Thoughts让模型使用其他工具RAG写程序POT其他工具 让多个模型合作参考 在模型不变的情况下&#…...

C++连接oracle数据库连接字符串

//远程连接&#xff0c;需要安装oracle客户端sprintf(szConnect4, ("Provider OraOLEDB.Oracle.1; Password %s; Persist Security Info True; User ID %s; Data Source \"(DESCRIPTION (ADDRESS_LIST (ADDRESS (PROTOCOL TCP)(HOST %s)(PORT 1521)) )(CONN…...

判断字符串是否接近:深入解析及优化【字符串、哈希表、优化过程】

本文将详细解析解决这个问题的思路&#xff0c;并逐步优化实现方案。 问题描述 给定两个字符串 word1 和 word2&#xff0c;如果通过以下操作可以将 word1 转换为 word2&#xff0c;则认为它们是接近的&#xff1a; 交换任意两个现有字符。将一个现有字符的每次出现转换为另…...

C 和 C++ 中信号处理简单介绍

信号处理是编程中一个重要的主题&#xff0c;特别是在需要处理异步事件和错误情况的系统中。在 C 和 C 语言中&#xff0c;信号处理机制提供了一种优雅的方式来响应特定的系统事件&#xff0c;例如用户中断、异常情况或其他信号。在这里&#xff0c;我将详细介绍 C 和 C 中信号…...

什么是云边协同?

当今信息技术高速发展的时代&#xff0c;"云边协同"&#xff08;Edge Cloud Collaboration&#xff09;已经成为一个备受关注的话题。它涉及到云计算和边缘计算的结合&#xff0c;为数据处理、存储和应用提供了全新的可能性。本文将介绍云边协同的概念、优势以及在不…...

YOLOv5改进 | 主干网络 | 将backbone替换为MobileNetV2【小白必备教程+附完整代码】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录&#xff1a; 《YOLOv5入门 改…...

ARMxy边缘计算网关用于过程控制子系统

在现代工业生产中&#xff0c;过程控制系统的优化对于提高生产效率、保证产品质量、降低能源消耗等方面都具有重要意义。而 ARMxy 工控机作为一种高性能、高可靠性的工业控制设备&#xff0c;正逐渐成为过程控制系统优化的新选择。 ARMxy 工控机采用了先进的 ARM 架构处理器&am…...

Python | TypeError: unsupported operand type(s) for +=: ‘int’ and ‘str’

Python | TypeError: unsupported operand type(s) for : ‘int’ and ‘str’&#xff1a;深度解析 在Python编程中&#xff0c;遇到“TypeError: unsupported operand type(s) for : ‘int’ and ‘str’”这类错误通常意味着你尝试将一个整数&#xff08;int&#xff09;和…...

什么是开源什么是闭源?以及它们之间的关系

开源软件&#xff08;Open Source Software&#xff09; 定义&#xff1a;开源软件是指其源代码可以被公众访问和使用的软件。用户可以查看、修改和增强软件的源代码。 许可&#xff1a;通常遵循特定的开源许可证&#xff0c;如GNU通用公共许可证&#xff08;GPL&#xff09;、…...

SpringBoot+Mybatis Plus实际开发中的注解

SpringBoot+Mybatis Plus实际开发中的注解 实体类Service层Mapper层Controller层启动类配置类SpringBoot+Mybatis Plus实际开发中的注解 实体类 @Data : 底层实现了getter、setter、toString、hashCode、equals 和无参构造@AllArgsConstructor: 底层实现了有参构造@NoArgsCon…...

【香橙派系列教程】(八)一小时速通Python

【八】一小时速通Python 本章内容服务于香橙派下的开发&#xff0c;用C语言的视角来学习即可&#xff0c;会改就行。 详细说明&#xff0c;请看链接:python全篇教学 Python是一种动态解释型的编程语言&#xff0c;Python可以在Windows、UNIX、MAC等多种操作系统上 使用&…...

了解JavaScript 作用、历史和转变

JavaScript 是一种即时执行的脚本语言&#xff0c;其代码在浏览器环境中通过内置的 JavaScript 引擎被动态地一行接一行地解释执行。这一特性赋予了开发者极高的灵活性和效率&#xff0c;因为代码修改后能立即生效&#xff0c;无需经历编译过程&#xff0c;从而加速了开发周期和…...

遗传算法与深度学习实战——生命模拟与进化论

遗传算法与深度学习实战——生命模拟与进化论 0. 前言1. 模拟进化1.1 代码实现1.2 代码改进 2. 达尔文进化论3. 自然选择和适者生存3.1 适者生存3.2 进化计算中的生物学 小结系列链接 0. 前言 生命模拟通过计算机模拟生物体的基本特征、遗传机制、环境互动等&#xff0c;试图模…...

rt-thread H7 使用fdcan没有外接设备时或发送错误时线程被挂起的解决方案

一、问题查找 使用的开发版是硬石的H7芯片型号STM32H743IIT6&#xff0c;测试时发现如果外面没有连接CAN设备&#xff0c;程序调用CAN发送时会一直等待发送反馈&#xff0c;导致相关线程挂起。 在线仿真时发现是卡在can.c文件的168行_can_int_tx函数&#xff1a;rt_co…...

exptern “C“的作用,在 C 和 CPP 中分别调用 openblas 中的 gemm 为例

openblas提供的sgemm有两种方式&#xff0c;一种是通过cblas&#xff0c;另一种是直接声明并调用 sgemm_ 其中&#xff0c;cblas方式是更正规调用方法&#xff1b; 1&#xff0c;调用openblas的 sgemm 的两种方式 1.1 c语言程序中使用 sgemm hello_sgemm.c #include <st…...

如何提前预防网络威胁

一、引言 随着信息技术的迅猛进步&#xff0c;网络安全议题愈发凸显&#xff0c;成为社会各界不可忽视的重大挑战。近年来&#xff0c;一系列网络安全事件的爆发&#xff0c;如同惊雷般震撼着个人、企业及国家的安全防线&#xff0c;揭示了信息安全保护的紧迫性与复杂性。每一…...

ProviderRpc发送服务二将远程调用来的信息反序列化后调用服务方的方法,并将服务方的结果返回给发送方

在Provider的实现中&#xff0c;OnMessage函数中&#xff0c;处理接收到的连接RPC请求。将接收到的RPC请求&#xff08;包含请求的对象&#xff0c;请求方法和 请求参数&#xff09;&#xff0c;接收到这些信息之后进行反序列化。得到这些参数之后我们即将要做的事情是去调用相…...

Io 35

FIleinputStream字节输入 package File.io;import java.io.*;public class io1 {public static void main(String[] args) throws IOException {// InputStream is new FileInputStream(new File("C:\\Users\\SUI\\Desktop\\Java1\\one\\src\\kaishi"));//简化Input…...

java基础概念11-方法

一、什么是方法 方法&#xff08;method&#xff09;是程序中最小的执行单元。 方法中的程序&#xff0c;要不然就是一起执行&#xff0c;要不然就是一起不执行&#xff01;&#xff01;&#xff01; 二、方法的定义 在Java中&#xff0c;方法定义的一般格式如下&#xff1a;…...

大模型应用中的思维树(Tree of Thought)是什么?

大模型应用中的思维树&#xff08;Tree of Thought&#xff09;是什么&#xff1f; 大模型&#xff0c;特别是基于GPT&#xff08;Generative Pre-trained Transformer&#xff09;架构的模型&#xff0c;在处理复杂任务时&#xff0c;通常需要依赖某种形式的推理和决策机制。…...

学习记录(11):训练图片分类的算法

文章目录 一、卷积神经网络&#xff08;CNN&#xff09;架构1. ResNet&#xff08;Residual Networks&#xff09;2. DenseNet&#xff08;Densely Connected Convolutional Networks&#xff09;3. EfficientNet4. MobileNet 二、变换器&#xff08;Transformer&#xff09;架…...

上网防泄密,这些雷区不要碰!九招教你如何防泄密

李明&#xff1a;“最近看到不少关于信息泄露的新闻&#xff0c;真是让人担忧。咱们在工作中&#xff0c;稍有不慎就可能触碰到泄密的雷区啊。” 王芳&#xff1a;“确实&#xff0c;网络安全无小事。尤其是我们这种经常需要处理敏感信息的岗位&#xff0c;更得小心谨慎。那你…...

数据库篇--八股文学习第十五天| 一条SQL查询语句是如何执行的?,事务的四大特性有哪些?,数据库的事务隔离级别有哪些?

1、一条SQL查询语句是如何执行的&#xff1f; 答&#xff1a; 连接器:连接器负责跟客户端建立连接、获取权限、维持和管理连接。查询缓存: MySQL 拿到一个查询请求后&#xff0c;会先到查询缓存看看&#xff0c;之前是不是执行过这条语句。之前执行过的语句及其结果可能会以…...

elk + filebeat + kafka实验和RSync同步

elk filebeat kafka实验和RSync同步 elk filebeat kafka实验 filebeatkafkaELK实验的操作步骤&#xff1a; #在装有nginx的主机上解压filebeat压缩包 [roottest4 opt]# tar -xf filebeat-6.7.2-linux-x86_64.tar.gz #将解压后的压缩包更改名字 [roottest4 opt]# mv file…...

子类到底能继承父类中的哪些内容?

...

【超详细公式】曝光值(EV)、光圈(AV)、快门(TV)、感光度(SV)、照度(Lux)

文章目录 术语 E V A V T V − S V EV AV TV - SV EVAVTV−SV L u x 2.5 2 E V Lux 2.5 \times 2^{EV} Lux2.52EV通常环境光照度参照表 术语 术语全称中文名EVExposure Value曝光值AVAperture Value光圈值TVTime Value快门值SVSensitive Value感光值BVBrightness Value…...