第六十一天学习记录:C语言进阶:C语言预处理1

这篇具有很好参考价值的文章主要介绍了第六十一天学习记录:C语言进阶:C语言预处理1。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

程序的翻译环境和执行环境

在ANSI C的任何一种实现中,存在两个不同的环境。
第一种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。第2种是执行环境,它用于实际执行代码。

详解编译+链接

翻译环境

第六十一天学习记录:C语言进阶:C语言预处理1

![在这里插入图片描述](https://img-blog.csdnimg.cn/04bd03e2cb554aa298fb6a8349722f89.png
第六十一天学习记录:C语言进阶:C语言预处理1

上图截取自比特科技免费课程

。组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。
。每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
。链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。

Linux环境方便观察编译过程:
1、预处理 选项gcc -E test.c -o test.i 预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。
2、编译 选项gcc -S test.c编译完成之后就停下来,结果保存在test.s中。
3、汇编 gcc -c test.c汇编完成之后就停下来,结果保存在test.o中。

比特科技推荐网站:
VIM学习资料
简明VIM练级攻略:
https://coolshell.cn/articles/5426.html
给程序员的VIM速查卡
https://coolshell.cn/articles/5479.html

运行环境

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

问:在编译过程中实现的内存堆栈和通过exe运行程序时进行的内存堆栈是不是一样的?
ChatAI答:
在编译过程中实现的内存堆栈和通过exe运行程序时进行的内存堆栈是不一样的。

在编译过程中,内存堆栈是由编译器实现的,用于在程序执行时为函数、变量等分配内存空间和管理内存使用情况。编译器会根据程序的代码框架和变量的作用域等因素,对内存空间进行分配和释放,并且对程序和函数的调用栈进行跟踪和管理。

在执行程序时,内存堆栈也存在,但实现方式是由操作系统和运行时环境决定的。当程序运行时,操作系统会为程序分配一定的内存空间,这些内存空间被划分为不同的存储区域,包括进程虚拟地址空间、栈、堆、全局/静态数据区等。程序使用内存空间时,根据内存空间的访问类型和权限进行对应的操作。

因此,编译器实现的内存堆栈和运行时环境实现的内存堆栈,并不是完全相同的。它们的实现方式、管理机制、内存分配和释放方式等都可能存在差异。这也是为什么在一些情况下,程序在编译时可以通过编译,但在执行时却会出现运行时错误的原因之一。

《程序员的自我修养》

预处理详解

预定义符号
__FILE__				//进行编译的源文件
__LINE__				//文件当前的行号
__DATE__				//文件被编译的日期
__TIME__				//文件被编译的时间
__STDC__				//如果编译器遵循ANSI C,其值为1,否则未定义(PS:VS2015无效)
__FUNCTION__     //打印函数名

这些预定义符号都是语言内置的。

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

int main()
{
	printf("file:%s line:%d\n",__FILE__,__LINE__);
	printf("date:%s time:%s\n",__DATE__,__TIME__);
	return 0;
}

第六十一天学习记录:C语言进阶:C语言预处理1
用于写日志文件:

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

int main()
{
	//写日志文件
	int i = 0;
	int arr[10] = { 0 };
	FILE* pf = fopen("log.txt", "w");
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
		fprintf(pf, "file:%s line:%d date:%s time:%s i=%d\n",
			__FILE__, __LINE__, __DATE__, __TIME__,i);
	}
	fclose(pf);
	pf = NULL;
	for (i = 0; i < 10; ++i)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

第六十一天学习记录:C语言进阶:C语言预处理1

预处理指令:
#define
#include
#pragma
#if
#endif
#ifdef
#line

#define

#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定义宏

#defne 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。
下面是宏的声明方式:

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

注意:参数列表的左括号必须与name紧邻。如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。
如:

#define SQUARE( x ) x*x

这个宏接收一个参数x,如果在上述声明之后,你把

SQUARE( 5 );

置于程序中,预处理器就会用下面这个表达式替换上面的表达式:

5*5
#include <stdio.h>
#define SQUARE(X) X*X

int main()
{
	int ret = SQUARE(5);
	printf("%d\n",ret);
	return 0;
}

输出:25

#include <stdio.h>
#define SQUARE(X) X*X

int main()
{
	int ret = SQUARE(5+1);
	printf("%d\n",ret);
	return 0;
}

输出:11
注意:宏是用来替换的而不是传参的。上面被替换成了:

5+1*5+1   =   11

建议优化写法:

#define SQUARE(X) ((X)*(X))
define替换规则

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

#和##

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

#include <stdio.h>
#define PRINT(X) printf("the value of "#X" is %d\n",X)

int main()
{
	int a = 10;
	int b = 20;
	PRINT(a);
	PRINT(b);
	return 0;
}

第六十一天学习记录:C语言进阶:C语言预处理1
另一个技巧是:使用#,把一个宏参数变成对应的字符串。比如:

#include <stdio.h>
#define PRINT(FORMAT,VALUE)\
	printf("the value of "#VALUE" is "FORMAT"\n",VALUE)

int main()
{
	int a = 10;
	int b = 20;
	PRINT("%d",a+8);
	PRINT("%d",b+3);
	return 0;
}

第六十一天学习记录:C语言进阶:C语言预处理1

##的作用

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


#include <stdio.h>

#define CAT(X,Y) X##Y

int main()
{
	int chung1991 = 2023;
	printf("%d\n", CAT(chung, 1991));
	return 0;
}

输出:2023
注:这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。文章来源地址https://www.toymoban.com/news/detail-467942.html

到了这里,关于第六十一天学习记录:C语言进阶:C语言预处理1的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C语言(第三十一天)

    6. 调试举例1 求1!+2!+3!+4!+...10!的和,请看下面的代码: 调试找一下问题。 7. 调试举例2 在VS2019、X86、Debug 的环境下,编译器不做任何优化的话,下面代码执行的结果是啥? 程序运行,死循环了,调试看看为什么? 调试可以上面程序的内存布局如下:  1. 栈区内存的使用习惯

    2024年02月11日
    浏览(30)
  • 从零开始的力扣刷题记录-第五十一天

    题目描述: 给你一棵二叉搜索树的 root ,请你 按中序遍历 将其重新排列为一棵递增顺序搜索树,使树中最左边的节点成为树的根节点,并且每个节点没有左子节点,只有一个右子节点。 题解: 中序遍历存储节点后按顺序连接即可 代码(Go): 题目描述: 小扣在秋日市集发

    2024年02月08日
    浏览(31)
  • 学习Android的第十一天

    目录 Android ProgressBar 进度条 ProgressBar ProgressBar 外观 ProgressBar 属性 ProgressBar 方法 Android ProgressBar 动图替代圆形进度条 范例 Android ProgressBar 自定义圆环进度条 例子: 参考文档 在Android中,ProgressBar(进度条)是用于显示任务进度的UI组件,通常用于耗时操作的过程中以及需要显

    2024年02月19日
    浏览(31)
  • 【80天学习完《深入理解计算机系统》】第十一天 3.5 过程(函数调用)

    专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录) 文章字体风格: 红色文字表示:重难点★✔ 蓝色文字表示:思路以及想法★✔ 如果大家觉得有帮助的话,感谢大家帮

    2024年02月11日
    浏览(31)
  • 【80天学习完《深入理解计算机系统》】第十一天 3.4 跳转指令

    专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录) 文章字体风格: 红色文字表示:重难点★✔ 蓝色文字表示:思路以及想法★✔ 如果大家觉得有帮助的话,感谢大家帮

    2024年02月11日
    浏览(30)
  • 第五十一天打卡

    中等 1.5K company 微软 Microsoft 给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。​ 设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票): 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。 注意

    2023年04月09日
    浏览(25)
  • node 第二十一天 webpack 初见

    为什么需要学习(了解)webpack webpack是前端工程化的基石,webpack又是基于node进行文件打包bundle,所以作为前端起手学习node服务端开发,同时学习webpack是很有必要的。 随着vite的出现,vue这一脉可能也许不再需要学习webpack了,但是需要知道的是, 打包一定是前端工程化绕不

    2024年01月16日
    浏览(32)
  • 代码随想录第二十一天

    题目链接 : 二叉搜索树的最小绝对差 自己的思路 :和验证二叉搜索树一样的思路!可以求每个相邻节点的差值的绝对值,然后和之前的差值的绝对值进行比较,取最小的为新的res;递归三部曲:1、传入参数:当前节点;2、终止条件:如果当前节点为空,直接返回;3、单层递

    2024年02月16日
    浏览(40)
  • 学C的第三十一天【通讯录的实现】

    ========================================================================= 相关代码gitee自取 :C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 学C的第三十天【自定义类型:结构体、枚举、联合】_高高的胖子的博客-CSDN博客  ==============

    2024年02月15日
    浏览(38)
  • 谷粒商城第十一天-完善商品分组(主要添上关联属性)

    目录 一、总述 二、前端部分 2.1 改良前端获取分组列表接口及其调用 2.2 添加关联的一整套逻辑 三、后端部分 四、总结 前端部分和之前的商品品牌添加分类差不多。 也是修改一下前端的分页获取列表的接口,还有就是加上关联的那一套逻辑,包括基本构件的引入、数据域的

    2024年02月13日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包