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

翻译环境

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>

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

关于GPT、AI绘画、AI提词器等AI技术的探讨

目前的AI潮流非常火热&#xff0c;CHATGPT可谓是目前大模型人工智能的代表&#xff0c;刚开始听说chatGPT可以写代码&#xff0c;写作&#xff0c;写方案&#xff0c;无所不能。还有AI绘画也很&#xff2e;&#xff22;作为一个程序员&#xff0c;为了体验这些&#xff21;&…

node基于express+mongodb项目的整体结构搭建和逻辑抽离

一、为什么需要逻辑抽离 这是我用express实现的一个缩减版的注册功能,如下&#xff1a; app.js const express require("express"); const app express();// 连接数据库 const mongoose require("mongoose"); // 连接数据库myTest mongoose.connect(…

【云原生】k8s之Ingress

1.Ingress的相关知识 1.1 Ingress的简介 service的作用体现在两个方面&#xff0c;对集群内部&#xff0c;它不断跟踪pod的变化&#xff0c;更新endpoint中对应pod的对象&#xff0c;提供了ip不断变化的pod的服务发现机制&#xff1b;对集群外部&#xff0c;他类似负载均衡器…

【剧前爆米花--web】HTTP协议格式详解以及构造

作者&#xff1a;困了电视剧 专栏&#xff1a;《JavaEE初阶》 文章分布&#xff1a;这是一篇关于HTTP协议的文章&#xff0c;在这篇文章中我会说明HTTP协议格式以及相关的构造&#xff0c;希望对你有所帮助&#xff01; 目录 HTTP协议 HTTP协议格式 HTTP请求 HTTP响应详情…

前端基本功 用 React Hooks + Antd 实现一个 Todo-List

背景 使用 React Hooks 以及组件库 Antd 来实现一个可以 增删 标记是否完成 的 todo-list 思路 要实现一个 todo-list 首先想到用 useState 维护一个状态数组来保存当前 list &#xff0c;还要用一个状态维护添加框中的内容 const [todos, setTodos] useState(initialValu…

如何用双指针法解决力扣“反转单词前缀”问题

本篇博客会讲解力扣“2000. 反转单词前缀”的解题思路&#xff0c;这是题目链接。 本题的思路是&#xff1a;先调用strchr函数&#xff0c;在字符串word中查找字符ch&#xff0c;若找到了&#xff0c;则会返回一个非空指针p&#xff0c;指向ch在word中的位置。为了反转从word到…

【UE4 塔防游戏系列】10-防御塔升级

目录 效果 步骤 一、根据防御塔等级修改子弹伤害 二、根据防御塔等级修改子弹速度 三、根据防御塔等级修改检测半径 四、根据防御塔等级修改子弹颜色 五、根据防御塔等级修改换弹时间 效果 步骤 一、根据防御塔等级修改子弹伤害 1. 打开“TowerBaseBullet_Child”&…

搭建Redis分片集群

说明&#xff1a;单体Redis有许多问题&#xff0c;可通过Redis数据持久化、搭建主从集群、哨兵和Redis分片集群解决单体Redis数据丢失、高并发、数据恢复和海量数据存储的问题。前三个参考http://t.csdn.cn/6pp2F、http://t.csdn.cn/o9u0S&#xff0c;本问介绍如何创建Redis分片…

http连接处理(下)(四)

1.结合代码分析请求报文响应 下面我们将介绍服务器如何响应请求报文&#xff0c;并将该报文发送给浏览器端。首先介绍一些基础API&#xff0c;然后结合流程图和代码对服务器响应请求报文进行详解。 基础API部分&#xff0c;介绍stat、mmap、iovec、writev。 流程图部分&…

数据结构--时间复杂度与空间复杂度

数据结构–时间复杂度与空间复杂度 文章目录 数据结构--时间复杂度与空间复杂度时间复杂度一、什么是时间复杂度二、具体实例1.大O的渐进表示法2.二分查找的时间复杂度 空间复杂度一、什么是空间复杂度二、具体实例总结 时间复杂度 一、什么是时间复杂度 在计算机科学中&…

微信小程序-地图上的图标计算旋转值朝向经纬度计算

废话不多说&#xff0c;开整 // 参数为寄件人经纬度和收件人经纬度 // 根据寄收件人经纬度弧度π进行rotate旋转计算 const getRotate (po1, po2) > {if (!(po1 && po2)) return 0const lng_a po1.longitudeconst lat_a po1.latitudeconst lng_b po2.longitud…

238. 除自身以外数组的乘积

题目描述&#xff1a; 主要思路&#xff1a; 正逆各扫一遍&#xff0c;利用数组存储当前数左边和右边的乘积。 class Solution { public:vector<int> productExceptSelf(vector<int>& nums) {int nnums.size();vector<int> ans;int l[n1],r[n1];l[0]1,…

Stable Diffusion - 图像反推 (Interrogate) 提示词算法 (BLIP DeepBooru)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/131817599 图像反推 (Interrogate) 功能&#xff0c;是指根据给定的图像生成一个或多个文本提示&#xff0c;这些提示可以描述图像的内容、风格、…

【C++杂货铺】构造函数和析构函数

文章目录 一、类的六个默认成员函数二、构造函数三、析构函数 一、类的六个默认成员函数 &#x1f4d6;默认成员函数 用户没有显式实现&#xff0c;编译器会自动生成的成员函数&#xff0c;称为默认成员函数。 构造函数&#xff1a;完成对象的初始化工作。析构函数&#xff…

前端笔记_OAuth规则机制下实现个人站点接入qq三方登录

文章目录 ⭐前言⭐qq三方登录流程&#x1f496;qq互联中心创建网页应用&#x1f496;配置回调地址redirect_uri&#x1f496;流程分析 ⭐思路分解⭐技术选型实现&#x1f496;技术选型&#xff1a;&#x1f496;实现 ⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本…

Vue2计算属性如何传参

Vue2官网并没解释计算属性应该怎么传值&#xff0c;但是呢&#xff0c;通过闭包的方式(使用箭头表达式)实际上是可以给计算属性传参的&#xff08;当然&#xff0c;多个参数也是可行的&#xff09;。 以下是本人项目开发使用自己基于ElementUI封装的集信息采集与文件上传功能的…

微信小程序基于Promise封装发起网络请求

1.创建一个request.js // 相当于域名 const baseURL ***************; // 暴露一个request函数 export function request(parms) {// 路径拼接const url baseURL parms.url;// 请求体&#xff0c;默认为{}const data parms.data || {};// 请求方式&#xff0c;默认为GETco…

攻防世界-web-easytornado

题目描述&#xff1a;Tornado 框架。打开链接是一个简单的界面 1. 思路分析 看到有个/flag.txt&#xff0c;我们点击进去看下 发现传入了两个参数&#xff0c;一个是filename&#xff0c;还有一个是filehash 看到里面的内容&#xff0c;提示我们真正的flag在 /flllllllllllla…

curl操作

下载路径&#xff1a;https://curl.se/windows/ 参考&#xff1a;https://blog.csdn.net/weixin_45191386/article/details/130652821 操作&#xff1a; curl http://localhost:8085/api/v1/aaa/bbbb/?ccc 652781344055627776

docker在arm64架构ubuntu系统的安装

卸载可能存在的旧版本 sudo apt remove docker docker-engine docker-ce docker-io安装依赖使apt可通过HTTPS下载包 sudo apt update && apt install -y apt-tranport-https ca-certificates curl software-properties-commonapt-transport-https用于支持通过HTTPS协…