由浅到深认识C语言(7)

该文章Github地址:https://github.com/AntonyCheng/c-notes

在此介绍一下作者开源的SpringBoot项目初始化模板(Github仓库地址:https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址:https://blog.csdn.net/AntonyCheng/article/details/136555245),该模板集成了最常见的开发组件,同时基于修改配置文件实现组件的装载,除了这些,模板中还有非常丰富的整合示例,同时单体架构也非常适合SpringBoot框架入门,如果觉得有意义或者有帮助,欢迎Star & Issues & PR!

上一章:由浅到深认识C语言(6)

6.预处理

6.1.C语言编译过程

  1. 预处理:头文件包含、宏替换、条件编译、删除注释,不做语法检查;

    #include<stdio.h>
    
  2. 编译:将预处理后的文件生成汇编文件,并且进行语法检查;

  3. 汇编:将汇编文件编译成二进制文件;

  4. 链接:将“众多的二进制文件+库函数+启动代码”生成可执行文件(.exe等);

6.2.头文件包含

  • <>用于包含系统的头文件
#include<hello.h>

上面代码表示在系统指定的目录下寻找 hello.h

  • ""用于包含用户自定义的头文件
#include"hello.h"

上面代码表示先从源文件所在的目录寻找 hello.h ,如果找不到再到系统指定的目录下寻找;

  • include只能包含头文件.h,不要去包含源文件.c

6.3.define 宏

定义:在源代码中允许一个标识符(宏名)来表示一个语言符号的字符串,用特定的符号来代表指定的信息;

优点:提高代码的可读性和移植性;

#include<stdio.h>
#define N 10
static void test() {int i = 0;for (i = 0; i < N; i++) {printf("%d ", i);}for (i = 0; i < N; i++) {printf("%d ", i);}for (i = 0; i < N; i++) {printf("%d ", i);}for (i = 0; i < N; i++) {printf("%d ", i);}for (i = 0; i < N; i++) {printf("%d ", i);}for (i = 0; i < N; i++) {printf("%d ", i);}for (i = 0; i < N; i++) {printf("%d ", i);}return;
}
int main(int argc, char *argv[]) {test();return 0;
}

打印效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

过程:上例的过程是将代码中的 10 转变成了 N ,如果需要修改 10 这一个数值的话,仅需改变宏,该过程被称之为“宏展开”;

注意:宏的后方不能加分号,因为加了分号,编译器就会把分号给编进宏内,宏一般为大写,以便和普通变量区分开来;

分类

  1. 无参数的宏:

    #define 宏名 代替内容

    例如:#define PI 3.14f

    例如:

    #include<stdio.h>
    #define N 10
    static void test() {int i = 0;for (i = 0; i < N; i++) {printf("%d ", i);}for (i = 0; i < N; i++) {printf("%d ", i);}for (i = 0; i < N; i++) {printf("%d ", i);}for (i = 0; i < N; i++) {printf("%d ", i);}for (i = 0; i < N; i++) {printf("%d ", i);}
    #undef Nfor (i = 0; i < N; i++) {printf("%d ", i);}for (i = 0; i < N; i++) {printf("%d ", i);}return;
    }
    int main(int argc, char *argv[]) {test();return 0;
    }
    

    注意

    • 宏只在当前源文件有效,当需要终止宏的作用范围时需要 #undef 宏名 ,例如上;
    • 这种方法能使用户能以一个简单的名字代替一个长的内容(数字,字符,字符串)
  2. 带参数的宏(宏函数):

    #define 宏名(形参表) 被替代内容;

    例如:#define N(a,b) a+b;

    注意:参数表中的参数不能写类型,即 N(int a,int b) 是错的;

    例如:

    #define MY_ADD(a,b) a+b
    //调用宏
    MY_ADD(10,20); //这里等价于 10 + 20;
    

    注意:千万不要擅自加括号,比如求 MY_ADD(10+10,20+20); 那么求得的过程就是 10+10*20+20

    案例一:两数相加;

    #include<stdio.h>
    #define MY_ADD(a,b) a+b
    static void test() {int num = 0;num = MY_ADD(10 , 20);printf("num = %d\n", num);return;
    }
    int main(int argc, char *argv[]) {test();return 0;
    }
    

    打印效果如下:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    案例二:两数相乘;

    #include<stdio.h>
    #define MY_MUL(a,b) a*b
    static void test() {int num = 0;num = MY_ADD(10 , 20);printf("num = %d\n", num);return;
    }
    int main(int argc, char *argv[]) {test();return 0;
    }
    

    打印效果如下:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  3. 宏函数和普通函数的区别

    宏函数调用多少次,就会宏展开多少次,执行代码的时候没有函数调用的过程,也不需要函数的出入栈,所以带参数的宏会浪费空间,节省了时间;

    普通函数代码只有一份,存在代码段,调用的时候去代码段读取函数指令,调用的时候要压栈(保存调用函数前的相关信息),调用完之后会出栈(恢复调用前的相关信息),所以函数浪费了时间,节省了空间;

  4. 案例:求出下例printf

    #include<stdio.h>
    #define MY_MUL(a,b) a*b
    #define MY_ADD(a,b) a+b
    static void test() {printf("%d\n", MY_MUL(MY_ADD(10 + 10, 20 + 20), MY_MUL(10 + 10, 20 + 20)));//列算式出来就是 10 + 10 + 20 + 20 * 10 + 10 * 20 +20 = 460return;
    }
    int main(int argc, char *argv[]) {test();return 0;
    }
    

    打印效果如下:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

6.4.条件编译

一般情况下,源程序中所有行都参加编译,但有时希望对部分源程序行只在满足一定条件时才编译,即对这部分源程序行指定编译条件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意:以下案例在 Linux下测试;

案例一:测试不存在;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

测试预处理结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

案例二:测试存在;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

测试预处理结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

案例三:判断表达式;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

变蓝表示不编译

综合案例:通过条件编译来控制大小写的转换;

#include<stdio.h>
int main(int argc, char *argv[]) {char str[128] = { "" };int i = 0;printf("请输入一个字符串:");//fgets 会获取换行符“\n”fgets(str, sizeof(str), stdin);//去掉换行符,strlen返回的是字符串的实际长度,不包括“\0”str[strlen(str) - 1] = 0;  //strlen[str] 是换行符的下标位置
#if 1//大写变小写while (str[i] != '\0') {if (str[i] >= 'A' && str[i] <= 'Z') {str[i] = str[i] + 32;}i++;}
#else//小写变大写while (str[i] != '\0') {if (str[i] >= 'a' && str[i] <= 'z') {str[i] -= 32;}i++;}
#endifprintf("str = %s\n", str);return 0;
}
由于条件变量为 1 ,即编译大写变小写;

6.5.防止头文件重复包含

  • 方法一:编译器层面 #pragma once

    在所有头文件上方写上 #pragma once ,例如下:

    #pragma once
    #include"a.h"
    #include"b.h"
    #include"c.h"
    
  • 方法二:C++层面 #ifndef 宏……#define 宏……头文件具体内容……#endif

    例如下:

    main.c

    #include<stdio.h>
    #include"a.h"
    #include "b.h"
    int main(int argc,char *argv[]){printf("num = %d\n",num);return 0;
    }
    

    a.h

    #ifndef __A_H__
    #define __A_H__
    #include "b.h"
    #endif
    

    b.h

    #ifndef __B_H__
    #define __B_H__
    int num = 10;
    #endif
    
  • 总结:

    #pragma once 编译器决定,强调的文件名;

    #ifndef 是C/C++标准制定,强调的是宏而不是文件;

7.二进制 原/反/补码

无符号数和正数负数
概念10概念-10
原码数据的二进制形式0000 1010原码数据的二进制形式1000 1010
反码和原码相同0000 1010反码原码的符号位不变,其他取反1111 0101
补码和原码相同0000 1010补码反码 + 11111 0110

注意

  1. 无符号数和正数——原码==反码==补码
  2. 负数——
    • 反码==原码的符号位不变,其他位取反
    • 补码=反码 + 1
  3. 任何数据在计算机中都是以补码形式存储

计算机为什么要补码?

补码将减法运算转换成加法运算:

计算下式:
6 - 10 == -4
6 + (-10) == -4
如果没有补码:0000 0110
+ 1000 1010
-------------1001 0000 == -16(错误)
如果有补码:0000 0110
+ 1111 0110
-------------1111 1100 --> 1000 0011 --> 1000 0100 == -4

补码统一了零的编码:

有符号数:1111 1111 ~ 1000 0000 ~ 0000 0000 ~ 0111 1111-127    ~    -0     ~    +0     ~    +127计算机为了扩大数据的表示范围,故意将 -0 看成了 -128所以范围是 -128 ~ 127
无符号数:0000 0000 ~ 1111 1111范围是 0 ~ 255
即:
+0 == 0000 0000(原) == 0000 0000(反) == 0000 0000(补)
-0 == 1000 0000(原) == 1111 1111(反) == 0000 0000(补)

补码意义:将减法运算变加法运算,同时统一了零的编码。

下一章:由浅到深认识C语言(8)

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

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

相关文章

云仓酒庄东莞分公司2024年日常沙龙:葡萄酒文化与品鉴之旅

原标题&#xff1a;云仓酒庄东莞分公司日常沙龙&#xff1a;葡萄酒文化与品鉴之旅&#xff0c;招商新机遇共融 在东莞这座充满活力的城市&#xff0c;云仓酒庄分公司近日举办了一场别开生面的日常沙龙活动。此次活动以葡萄酒文化与品鉴为主题&#xff0c;旨在让参与者深入体验…

Typecho CMS 反序列化漏洞(CVE-2018-18753)复现

1.环境搭建 项目地址&#xff1a;Release Typecho 1.0(14.10.10) typecho/typecho GitHub 安装&#xff1a; 创建数据库typecho create database typecho; 再进入安装程序&#xff0c;输入数据库密码&#xff0c;设置登录密码即可 直接使用即可 2.漏洞分析 install.php文…

[善用佳软]推荐掌握小工具:Json解析的命令行工具jq

前言&#xff1a; 我们在各种生产环境或者开发测试环境中&#xff0c;一定遇到有很多信息都是使用JSON串或者文本文件作为输入的。在没有JQ命令行工具之前&#xff0c;我们要从中获取真正的输入&#xff0c;大都把它复制到文本里头&#xff0c;然后使用文本编辑器进行加工整理…

PytorchAPI的使用及在GPU的使用和优化

API 调用API&#xff1a;和手动实现的思路是一样的。#1&#xff0c;#2这两个步骤是通用的步骤&#xff0c;相当于建立一个模型&#xff0c;之后你具体的数据直接丢进去就行了。只需要按着这样的样式打代码就行&#xff0c;死的东西&#xff0c;不需要你自己创造。 import torc…

【博士每天一篇文献-综述】Communication dynamics in complex brain networks

阅读时间&#xff1a;2023-11-30 1 介绍 年份&#xff1a;2018 作者&#xff1a;Andrea Avena-Koenigsberger&#xff0c;印第安纳大学心理与脑科学系&#xff1b;Bratislav Misic 蒙特利尔神经学研究所&#xff0c;麦吉尔大学 期刊&#xff1a; Nature reviews neuroscience…

【Linux进阶之路】HTTPS = HTTP + S

文章目录 一、概念铺垫1.Session ID2.明文与密文3.公钥与私钥4.HTTPS结构 二、加密方式1. 对称加密2.非对称加密3.CA证书 总结尾序 一、概念铺垫 1.Session ID Session ID&#xff0c;即会话ID&#xff0c;用于标识客户端与服务端的唯一特定会话的标识符。会话&#xff0c;即客…

基于DataX迁移MySQL到OceanBase集群

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

go和rust使用protobuf通信

先下载protoc 首先下载proc程序以生成代码 https://github.com/protocolbuffers/protobuf/releases 解压&#xff0c;然后把bin目录的位置放到环境变量 测试 rust作为server&#xff0c;rpc使用tonic框架 官方教程 go作为service&#xff0c;使用grpc go语言使用grpc 效…

Xilinx LVDS ISERDESE2

ISERDESE2 7 系列 FPGA 是一款专用的串行到并行转换器,具有特定的时钟和逻辑功能,旨在促进高速源同步应用的实现。该ISERDESE2避免了在FPGA架构中设计解串器时遇到的额外时序复杂性. ISERDESE2功能包括: 1,专用解串器/串行转换器 ISERDESE2解串器可实现高速数据传输,而无需…

FPGA静态时序分析与约束(四)、时序约束

系列文章目录 FPGA静态时序分析与约束&#xff08;一&#xff09;、理解亚稳态 FPGA静态时序分析与约束&#xff08;二&#xff09;、时序分析 FPGA静态时序分析与约束&#xff08;三&#xff09;、读懂vivado时序报告 文章目录 系列文章目录前言一、什么是时序约束&#xff1…

【JavaEE -- 多线程3 - 多线程案例】

多线程案例 1.单例模式1.1 饿汉模式的实现方法1.2 懒汉模式的实现方法 2. 阻塞队列2.1 引入生产消费者模型的意义&#xff1a;2.2 阻塞队列put方法和take方法2.3 实现阻塞队列--重点 3.定时器3.1 定时器的使用3.2 实现定时器 4 线程池4.1 线程池的使用4.2 实现一个简单的线程池…

【XR806开发板试用】基于WEBSOCKET实现人机交互(控制开关灯)以及开发问题记录

一、开发板编译、功能介绍 根据官方文档编译烧录成功后&#xff0c;我们修改下官方例子&#xff0c;进行开发来实现websocket。 整体流程&#xff1a;开发板先自动寻找指定的wifi并且连接&#xff0c;连接成功后&#xff0c;通过websocket来与服务端连接&#xff0c;连接成功后…

【SVG】前端-不依靠第三方包怎么画连线???

如何用SVG实现连线功能 在Web开发中&#xff0c;我们经常会遇到需要在页面上绘制图形或者实现一些图形交互的场景。SVG&#xff08;Scalable Vector Graphics&#xff09;作为一种用于描述二维图形的XML标记语言&#xff0c;在这方面提供了极大的便利。本文将以一个具体的例子…

蜡烛图K线图采用PictureBox控件绘制是实现量化交易的第一步非python量化

用vb6.0开发的量化交易软件 VB6量化交易软件的演示视频演示如上 股票软件中的蜡烛图是非常重要的一个东西&#xff0c;这里用VB6.0自带的Picture1控件的Line方法就可以实现绘制。 关于PictureBox 中的line 用法 msdn 上的说明为如下所示 object.Line [Step] …

LabVIEW电液伺服作动器

LabVIEW电液伺服作动器 随着工业自动化技术的快速发展&#xff0c;电液伺服作动器在各类精密控制领域得到了广泛应用。基于CRIO架构&#xff0c;利用LabVIEW软件开发了一套电液伺服作动器测控系统&#xff0c;实现了高精度的位移同步控制与测量&#xff0c;有效提高了系统的控…

3.Gen<I>Cam文件配置

Gen<I>Cam踩坑指南 我使用的是大恒usb相机&#xff0c;第一步到其官网下载大恒软件安装包,安装完成后图标如图所示&#xff0c;之后连接相机&#xff0c;打开软件&#xff0c;相机显示一切正常。之后查看软件的安装目录如图&#xff0c;发现有GenICam和GenTL两个文件&am…

uniapp无感登录封装

全局请求封装 https://blog.csdn.net/qq_42618566/article/details/109308690 无感登录封装 import {http} from "./index.js" let requestsQueue []; // 请求队列// 记录请求队列 export function recordRequests(path, params, loading, method) {requestsQueu…

JVM高频面试点(一):Java类加载过程

1.概述 在 Java 中&#xff0c;类加载过程是指将 Java 类的字节码加载到内存中&#xff0c;并转换为 Java 虚拟机能够识别和执行的数据结构的过程。类加载是 Java 虚拟机执行 Java 程序的必要步骤之一&#xff0c;它负责加载程序中用到的类和接口。下图所示是 ClassLoader 加载…

鸿蒙车载原生开发,拓展新版图

一天内连发“五弹”、HiCar 4.0首次上车 华为鸿蒙狂扩“汽车朋友圈”-上游新闻 汇聚向上的力量 3月15日&#xff0c;在“华为云&华为终端云服务创新峰会2024”上&#xff0c;华为首批汽车行业伙伴广汽传祺、岚图汽车、零跑汽车、凯翼汽车加入鸿蒙生态合作&#xff0c;华为…

FPGA高端项目:FPGA基于GS2971+GS2972架构的SDI视频收发+OSD动态字符叠加,提供1套工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案的SDI接收发送本方案的SDI接收图像缩放应用本方案的SDI接收纯verilog图像缩放纯verilog多路视频拼接应用本方案的SDI接收HLS图像缩放HLS多路视频拼接应用本方案的SDI接收HLS多路视频融合叠加应用本方案的S…