C语言--程序环境和预处理

news2024/11/24 16:20:25

翻译环境

C语言的代码是文本信息,对于计算机来说无法直接理解,需要通过翻译环境进行翻译成二进制信息;
我们在写代码的时候,一般都会写在一个源文件中,这时候我们就使用我们的编译器(VS)将其转换为机器代码,我们可以通过命令行的命令进行编译,通过命令就会生成一个可执行文件,我们就可以直接运行;
下图是运行的逻辑图:
在这里插入图片描述
在这里插入图片描述

编译

对于编译来说,也分几个阶段
在这里插入图片描述

运行环境

程序执行的过程:

  1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
  2. 程序的执行便开始。接着便调用main函数。
  3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回
    地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
  4. 终止程序。正常终止main函数;也有可能是意外终止。

预处理详解

预定义符号

FILE //进行编译的源文件
LINE //文件当前的行号
DATE //文件被编译的日期
TIME //文件被编译的时间
STDC //如果编译器遵循ANSI C,其值为1,否则未定义

示例代码:

#include<stdio.h>
int main()
{
	printf("%s\n", __FILE__);
	printf("%d\n", __LINE__);
	printf("%s\n", __DATE__);
	printf("%s\n", __TIME__);
	printf("%d\n", __STDC__);


	return 0;
}

在这里插入图片描述

#define 定义标识符

语法:
#define name stuff

示例代码:

#define MAX 1000
#define reg register          //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;)     //用更形象的符号来替换一种实现
#define CASE break;case        //在写case语句的时候自动把 break写上。

// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
                          date:%s\ttime:%s\n" ,\
						__FILE__,__LINE__ 	, \
						__DATE__,__TIME__ ) 

其实就是把定义的内容进行互换,用更容易记住的名称来代替;
记住对于#define定义标识符,后面不要加;这样会导致你把分号也加入到stuff,也会算进去定义

define 定义宏

#define允许把参数代入到文本中,这种实现称为宏(macro);

宏的申明方式:

#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中

对于宏的申明方式,需要注意括号与name紧贴,不能有空格隔开,这样就会判定为是stuff的内容;

示例代码:

#include<stdio.h>
#define ADD(x,y) ((x)+(y))

int main()
{
    int a=ADD(3,5);
    printf("%d",a);
    //8
}

实际上这也是替换,ADD(3,5)在主程序中实际上就是3+5;对于宏的申明,对每个参数和最终值都要加上小括号,这样才比较严谨;
例如:

#define SQUARE( x ) x * x
int main()
{
    int a=5;
    //打印一个6*6的
printf("%d\n" ,SQUARE( a + 1) );
//实际上,5+1*5+1
}

对于宏来说,参数不会转换为一个最终值传过去,它只会照着你的参数形式原封不动的传过去,所以要加上小括号;

#define的替换规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤。

  1. 调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
  2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
  3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
    注意:
    1 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
    2 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

带副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
如:

x+1;//不带副作用
x++;//带有副作用

示例代码:

#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
int main()
{
    int x = 5;
    int y = 8;
    int z = MAX(x++, y++);
    printf("x=%d y=%d z=%d\n", x, y, z);
}

答案:x=6 y=10 z=9
由于后置加加的原因,所以对于这个宏来说,程序会进行先后顺序的执行;首先是5和8再比大小,由于8比较大,判断之后后置加加,返回值是9,x为5的后置加加就是6,而b判断之后又加了一次,所以是10;
在这里插入图片描述
所以不要使用带有副作用的参数;

#和##

如何把参数插入到字符串中?

nt main()
{
	printf("hello world\n");
	printf("hello ""world\n");

	return 0;
}

对于这段代码来说,打印结果都是一样的;字符串是有自动连接的特点;
看下面代码:

#define PRINT(FORMAT, VALUE) printf("the value is "FORMAT"\n", VALUE);
int main()
{
    PRINT("%d", 10);
}

结果:the value is 10

对于宏来说,把“%d"看作为字符串,将字符串内容进行插入;且只有当字符串作为宏参数的时候才可以把字符串放在字符串中。
而如果使用#,就会把宏的参数转换为对应的字符串;

#define PRINT(FORMAT, VALUE) printf("the value is "#FORMAT"\n", VALUE);
int main()
{
    PRINT("%d", 10);
}

结果:the value is “10”
多了一对双引号,表明把整个参数都带进去了;

示例代码:

#define PRINT(n, format) printf("the value of "#n" is " format "\n", n)


int main()
{
	int a = 20;
	//printf("the value of a is %d\n", a);
	PRINT(a, "%d");

	int b = 15;
	//printf("the value of b is %d\n", b);
	PRINT(b, "%d");

	float f = 4.5f;
	//printf("the value of f is %f\n", f);
	PRINT(f, "%f");
	return 0;
}

结果:
the value of a is 20
the value of b is 15
the value of f is 4.500000

如果在上面程序去掉#,程序将会报错;因为在字符串插入的并非是字符串,对于宏来说是错误的;

而##可以把位于它两边的符号合成一个符号。它允许宏定义从分离的文本片段创建标识符。

示例代码:

#define CAT(x,y) x##y

int main()
{
	int Class110 = 2024;
	printf("%d\n", CAT(Class, 110));
	printf("%d\n", Class110);
	return 0;
}

结果:
2024
2024

宏与函数对比

宏通常被应用于执行简单的运算。
如上面的简单的加法

#define ADD(x,y) ((x)+(y))

而不用函数的原因:

1, 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。
所以宏比函数在程序的规模和速度方面更胜一筹
2.更为重要的是函数的参数必须声明为特定的类型。
所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以
用于>来比较的类型。
宏是类型无关的

宏的缺点:当然和函数相比宏也有劣势的地方:

  1. 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序
    的长度。
  2. 宏是没法调试的
  3. 宏由于类型无关,也就不够严谨
  4. 宏可能会带来运算符优先级的问题,导致程容易出现错。

而对于函数和宏的命名,宏的命名一般都用大写字母,而函数一般是大小写字母结合;

#undef

如果对于已经下定义的名字已经被重复使用,或者不想用了,就用#undef

#undef NAME

条件编译

在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令;
1:

#if 常量表达式
//…
#endif

通过判断常量表达式的真假来判断这段代码要不要编译
示例代码:

#define __DEBUG__ 1
#if __DEBUG__ //真
 //..
#endif

#if 常量表达式
//…
#elif 常量表达式
//…
#else
//…
#endif

与if条件判断一样,用通过判断哪个条件成立,其他条件就会被省略;
3.

#ifdef symbol
//…
#endif
#ifndef symbol
//…
#endif

判断是否被定义;
4.嵌套指令

#if defined(OS_UNIX)
#ifdef OPTION1
unix_version_option1();
#endif
#ifdef OPTION2
unix_version_option2();
#endif
#elif defined(OS_MSDOS)
#ifdef OPTION2
msdos_version_option2();
#endif
#endif

文件包含

#include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的地方一样。
预处理器会对先对预指令进行删除,用文件中的内容进行替换;一个源文件如果被包含5次,那么就编译5次;

对于头文件的包含有两种形式;
第一种:

#include “filename”

本地文件包含:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件;
找不到就是没有该头文件,程序报错;

第二种:

#include <filename.h>

库文件包含:查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。
对于库文件包含于头文件包含的关系,头文件包含实际上包括着库文件包含;

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

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

相关文章

汉诺塔问题(Hanoi Tower)--递归典型问题--Java版(图文详解)

目录 概述问题来源汉诺塔问题的规则 实现解题思路一个盘子两个盘子三个盘子n个盘子 递归概念递归特性递归的时间复杂度汉诺塔中的递归 代码 总结 概述 问题来源 汉诺塔&#xff08;Tower of Hanoi&#xff09;&#xff0c;又称河内塔&#xff0c;是一个源于印度古老传说的益智…

2023无监督摘要顶会论文合集

2023无监督摘要顶会论文合集 写在最前面ACL-2023Aspect-aware Unsupervised Extractive Opinion Summarization 面向的无监督意见摘要&#xff08;没找到&#xff09;Unsupervised Extractive Summarization of Emotion Triggers *情绪触发(原因)的 *无监督 *抽取式 摘要&#…

Python Request get post 代理 基本使用

Python Request get post 代理 常用示例 文章目录 Python Request get post 代理 常用示例一、Pip install requests二、Requests 请求时携带的常用参数1、参数说明2、headers3、requests 常用参数:url、headers、proxies、verify、timeout 三、Requests Get Post1、Get2、Post…

【Kotlin】基础速览(1):操作符 | 内建类型 | 类型转换 | 字符串模板 | 可变 var 和不可变 val

&#x1f4dc; 本章目录&#xff1a; 0x00 操作符&#xff08;operators&#xff09; 0x01 内建类型&#xff08;Build-in&#xff09; 0x02 类型转换&#xff1a;显式类型转换 0x03 在较长数字中使用下划线 0x04 字符串&#xff08;String&#xff09; 0x05 字符串模板&…

grpc中间件之链路追踪(otel+jaeger)

参考文档 https://github.com/grpc-ecosystem/go-grpc-middleware/blob/main/examples/client/main.go https://github.com/grpc-ecosystem/go-grpc-middleware/blob/main/examples/server/main.go https://github.com/open-telemetry/opentelemetry-go/blob/main/example/jaeg…

基于深度学习的高精度道路瑕疵检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度道路瑕疵&#xff08;裂纹&#xff08;Crack&#xff09;、检查井&#xff08;Manhole&#xff09;、网&#xff08;Net&#xff09;、裂纹块&#xff08;Patch-Crack&#xff09;、网块&#xff08;Patch-Net&#xff09;、坑洼块&#x…

销售易的12年与七个瞬间

导读&#xff1a;企业级没有捷径 12年对一家企业意味着什么&#xff1f; 在消费互联网领域&#xff0c;12年足够长&#xff0c;短短几年内上市的故事过去屡见不鲜。在企业服务的toB领域&#xff0c;产业成熟和企业发展的时间维度被拉长&#xff0c;但故事同样精彩。 2023年7月1…

漫谈大数据时代的个人信息安全(三)——“点赞之交”

大数据时代的个人信息安全系列三&#xff1a;“点赞之交” 1. 点赞之交2. 点赞诈骗3. 个人信息保护小贴士 互联网就像公路&#xff0c;用户使用它&#xff0c;就会留下脚印。 每个人都在无时不刻的产生数据&#xff0c;在消费数据的同时&#xff0c;也在被数据消费。 近日&am…

传智教育成功入选教育部2023年产学合作协同育人项目

传智教育成功入选教育部2023年产学合作协同育人项目 近日&#xff0c;教育部产学合作协同育人项目专家组发布《关于公布教育部产学合作协同育人项目指南通过企业名单&#xff08;2023年5月&#xff09;的通知》&#xff0c;传智教育申报的“教学内容和课程体系改革项目 、师资…

zygote forkSystemServer及systemServer启动

###zygote forkSystemServer方法 通过上一篇文章我们了解到zygote 在ZygoteInit.java类的main方法中调用forkSystemServer方法 UnsupportedAppUsagepublic static void main(String[] argv) {ZygoteServer zygoteServer null;....省略部分代码//根据环境变量(LocalServerSocke…

向量检索增强chatglm生成

背景&#xff1a; 基于chatglm构建agnet&#xff1a;chatglm实现Agent控制 - 知乎 前面一篇文章已经介绍了如何去搭建LLM Agent控制系统&#xff0c;也简单介绍了如何去构建Toolset和构建Action。但是在上篇文章中Toolset其实是基于搜索api构建的&#xff0c;从这篇文章开始后…

C++ stack和queue 模拟实现

stack和queue 模拟实现 模拟栈实现模拟队实现 模拟栈实现 1 栈是一种容器适配器&#xff0c;专门设计用于后进先出的后进先出环境&#xff0c;在这种环境中&#xff0c;元素只从容器的一端插入和提取。 2 栈是作为容器适配器实现的&#xff0c;这些适配器是使用特定容器类的封装…

获取gitlab上项目最近更新时间

获取gitlab上项目列表过程及脚本_xiaodaiwang的博客-CSDN博客使用Python及shell&#xff0c;获取gitlab上项目列表过程及脚本https://blog.csdn.net/xiaodaiwang/article/details/131781316?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rI…

【字符串编码解码问题】

字符串中编码解码问题 1.编码 byte[] getBytes()&#xff1a;使用平台的默认字符集将该String编码为一系列字节&#xff0c;将结果存储到新的字节数组中byte[] getBytes(String charsetName)&#xff1a;使用指定的字符集将该String编码为一系列字节&#xff0c;将结果存储到…

Minecraft 1.20.x Forge模组开发 02.物品栏+方块+物品

我们本次在1.20中添加一个属于自己模组的物品栏、物品和方块。 效果演示 效果演示 效果演示 1.在项目中新建一个int包,用于存放所有注册类,在init包中新建ItemTabInit类: ItemTabInit.java package com.joy187.re8joymod.init

【半监督医学图像分割 2023 CVPR】PatchCL

文章目录 【半监督医学图像分割 2023 CVPR】PatchCL摘要1. 简介2. 相关工作2.1 半监督学习2.2 对比学习 3. 方法3.1 类感知补丁采样3.2 伪标记引导对比损失3.3 总体学习目标3.4 伪标号生成与求精 4. 实验5. 结果 【半监督医学图像分割 2023 CVPR】PatchCL 论文题目&#xff1a;…

行为型模式 - 模板方法模式

概述 在面向对象程序设计过程中&#xff0c;程序员常常会遇到这种情况&#xff1a;设计一个系统时知道了算法所需的关键步骤&#xff0c;而且确定了这些步骤的执行顺序&#xff0c;但某些步骤的具体实现还未知&#xff0c;或者说某些步骤的实现与具体的环境相关。 例如&#…

JAVA ---- 经典排序算法

目录 一. 插入排序 1. 直接插入排序 代码演示 2.希尔排序( 缩小增量排序 ) 二. 选择排序 1.直接选择排序 代码&#xff1a; 2. 堆排序 代码 三. 交换排序 1. 冒泡排序 代码 2. 快速排序 代码&#xff08;有注释&#xff09;&#xff1a; 动图来自网…

Mysql教程(四):DML学习

Mysql教程&#xff08;四&#xff09;&#xff1a;DML学习 前言 DML-介绍 DML英文全称是Data Manipulation Language数据库操作语言&#xff0c;用来对数据库中表的数据记录进行增删改查。 添加数据&#xff08;INSERT&#xff09;修改数据&#xff08;UPDATE&#xff09;删除…

Java 串口通讯 Demo

为什么写这篇文章 之前职业生涯中遇到的都是通过tcp协议与其他设备进行通讯&#xff0c;而这个是通过串口与其他设备进行通讯&#xff0c;意识到这里是承重板的连接&#xff0c;但实际上比如拉力、压力等模拟信号转换成数字信号的设备应该是有相当一大部分是通过这种方式通讯的…