【C语言】程序运行环境及预处理指令

news2024/11/17 15:37:39

文章目录

    • 程序的翻译环境:
    • 程序的运行环境:
    • C语言预定义符号
    • #define定义标识符
    • #define定义宏
      • 具有副作用的宏参数
    • #与##
      • #的使用
      • ##的使用
    • 宏和函数对比
    • #undef
    • 命令行定义
    • 条件编译
      • 常见的条件编译指令🌞
    • 文件包含指令
      • 嵌套文件包含
    • 其他预处理指令
  • 撒花

程序的翻译环境:

整个翻译环境很复杂,简单形容:

我们运行一个c语言代码,首先会将代码进行转化,将c语言代码转化为汇编代码,再将汇编代码转化为电脑看的懂的二进制语言,项目下的所有文件都会进行上述操作,之后进行链接,将文件链接到一起。

在这里插入图片描述

程序的运行环境:

程序执行过程:

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

C语言预定义符号

这些预定义符号可以直接进行使用

__ FILE __ //进行编译的文件路径

__ LINE __ //此文件内当前行号

__ DATE __ //编译时日期

__ TIME __ //编译时时间

__ STDC __ //如果编译器遵循ANSI C ,值为1,否则未定义

代码示例

#include <stdio.h>
int main()
{
	printf("当前文件路径:%s\n",__FILE__);	
	printf("当前行号:%d\n",__LINE__);
}

在这里插入图片描述


#define定义标识符

#define name stuff

程序在预处理的时候会将stuff替换回来,那为什么还要使用define?

  • 方便修改,如果有一个值我们经常会用到,那么如果需要修改时需要一个一个进行修改,使用#define后每次只要修改define后面的值即可
  • 如果某个关键字或者程序语法太长可以使用#define将其改成一个非常简短的名字

stuff位置可以写成任何东西,只要符合语法即可⭐

#define	MAX 100
#define do_forever for(;;)
#define ll	long long

定义#define的时候尽量不要加上;从语法上来说并没有错误,不过容易引起错误。

代码示例

#define MAX 100;
int main()
{
	printf("%d",MAX);   
    //由于定义的时候100后面加了分号,替换过来就变成↓
    //printf("%d",MAX;);    所有不要这么写
}

#define定义宏

#define name(参数) 参数操作

当程序进行到预处理阶段的时候就会将name(参数)部分替换回”参数操作部分“;

示例

#define ADD(a,b) a+b
int main()
{
    int a = 8;
    int b = 10;
    printf("%d",ADD(a,b));
}

//预处理过后
int main()
{
    int a = 8;
    int b = 10;
    printf("%d",a+b);		//18
}

定义宏的时候一定要注意宏是直接替换到程序中的,容易引发一些小问题

#include<stdio.h>
#define ADD(a,b)	a+b
int main()
{
	int a = 9;
	int b = 5;
	printf("%d", 10 * ADD(a, b));
}

//这段程序我们的理想结果是50,可是运气起来结果却是23
//预处理过后。。。。。
#include<stdio.h>
#define ADD(a,b)	a+b
int main()
{
	int a = 9;
	int b = 5;
	printf("%d", 10 * a+b);
}

可以看到直接将宏替换过来后先进行10与a的运算再+b

为了避免这种情况的发生,可以将宏内的运算加上括号
#define ADD(a,b) (a+b)。
这样就可以有效避免问题的发生🤓

还有这种情况:

#include<stdio.h>
#define SQUARE(x)	x*x
int main()
{
	int a = 8;
    printf("%d",SQUARE(a+1));
}

乍一看觉得这代码输出为81,实际上结果为17

替换过后。。。。。

int a = 8;

printf(“%d”,a+1*a+1);

很显然结果为17.。。

要避免这种情况也可以将表达式加括号

#define SQUARE(x)	((x)*(x))

具有副作用的宏参数

使用宏的时候要避免 ‘’ ++ ‘’ “ - - ”这种操作符会引起不可预测的结果,要尽量是用x+1,x-1着这种形式。⭐

#与##

#的作用是将一个宏参数变成字符串

  • 使用方法:#参数

##的作用是将##两边的参数进行连接形成一个新的标识符(此标识符必须得存在)

  • 使用方法:参数##参数

#的使用

#include <stdio.h>
int main()
{
	printf("好好学习,""天天向上");	//字符串是可以自动连接的,”好好学习,天天向上“
}
#include <stdio.h>
#define print(VALUE)	\
	printf("边长为"#VALUE"的正方形周长为%d",VALUE * VALUE)
int main()
{
	print(3);		//输出:边长为3的正方形周长为9
}

##的使用

int CLASS = 100;
#define ADD_TO_SUM(num,value) num##value;

int main()
{
	ADD_TO_SUM(CL, ASS);	//CLASS
}

##连接的两个参数必须组成一个合法标识符否则就是未定义的

宏和函数对比

/函数获胜
速度更快存在调用和返回,比宏要慢
可调式性宏不可调试函数可调式函数
类型宏不区分类型区分类型宏/函数(各有各的好)
代码长度每次使用时,宏代码都会被插入到程序中。除了非常 小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每 次使用这个函数时,都调用那个 地方的同一份代码函数
递归不可递归可递归函数
具有副作用的参数参数可能被带到宏内各各位置,会产生不可预料的结果函数的参数在传参时求值一次,更容易控制结果函数

一般来说函数用得较多,当然宏也有自己的优势,在适合的场景可以使用宏。

命名:为了区分宏与函数,一般宏名我们全部大写,函数名不要全部大写

#undef

#undef的作用是移除宏定义

int main()
{
	#define MAX 100
    printf("%d",MAX);
    #undef			//为防止后面标识符冲突,进行移除
    int MAX = 101;
    printf("%d",MAX);
}

命令行定义

我们可以在命令行中进行编译的时候给代码内某一标识符进行赋值

条件编译

在编译一条语句的时候我们可以进行判断某个值或宏有没有在上面定义,如果有定义执行,没有定义则不执行相当于注释掉。

#define MAX(a,b) a+b

int main()
{
	int a = 2;
	int b = 3;
#ifdef MAX
	printf("%d\n", MAX(a, b));
#endif

	return 0;
}

常见的条件编译指令🌞

  • #if 常量表达式
    ​ ………
    #endif

  • #if 常量表达式
    ​ ………
    ​ #elif 常量表达式
    ​ ………
    ​ #else
    ​ ………
    ​ #endif

  • #if define(标识符)
    #ifdef 标识符

  • #if !define(标识符)
    ​ #ifndef 标识符

以上条件编译指令也可以进行嵌套使用

文件包含指令

#include 可以对其他文件进行包含,从而使用其他文件内的函数或全局变量

  • 文件包含的两种方式

    • 本地文件包含 #include ”filename“
    • 库文件包含 #include < filename >

    两种不同的包含方式在查找文件的时候有所不同,本地文件包含,先到源文件所在目录下查找如果未找到,到查找库文件的位置查找,库文件包含就直接到查找库文件的位置进行查找

显然本地文件包含无论比较万能,但那也不代表所有的包含都要用本地文件包含,为了效率考虑还是看实际文件是不是库函数,是库函数就用库文件包含,不是的话就用本地文件包含。

嵌套文件包含

在文件开头写 #pragma once即可解决

其他预处理指令

#error
#pragma
#line
.........	

撒花

这就是文件操作的全部内容了,创作不易,还请各位小伙伴多多点赞👍关注收藏⭐,以后也会更新各种小游戏还有关于c语言的博客,撒花!

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

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

相关文章

【C++】对数组指针的理解,例如 int (*p)[3]

目录 简介思考理解结语 简介 Hello&#xff01; 非常感谢您阅读海轰的文章&#xff0c;倘若文中有错误的地方&#xff0c;欢迎您指出&#xff5e; ଘ(੭ˊᵕˋ)੭ 昵称&#xff1a;海轰 标签&#xff1a;程序猿&#xff5c;C选手&#xff5c;学生 简介&#xff1a;因C语言结识…

Win7 无法安装 VMware Tools 解决方法

文章目录 1.下载kb4474419补丁2.虚拟机 > 设置 > 软盘&#xff0c;选中“使用物理驱动器”3.解决IE浏览器只能访问百度4.下载windows iso的正确方式 win7版本&#xff1a;cn_windows_7_ultimate_with_sp1_x64_dvd_u_677408_2&#xff08;iso大小3.18 GB&#xff09; vmwa…

C++内联函数(编译器角度,汇编角度),auto关键字,范围for语法糖,nullprt与NULL区别等

TIPS 在C当中有一个东西可以打印类型&#xff1f;typeid(变量名).name()数组一旦从参数进入到函数里面&#xff0c;它就已经是个指针了&#xff0c;再也不是一整个数组了 内联函数&#xff08;正常函数定义前加个inline修饰&#xff09; 在实际当中&#xff0c;有时候去调用…

13、拦截器

文章目录 1、HandlerInterceptor 接口2、配置拦截器3、拦截器原理 【尚硅谷】SpringBoot2零基础入门教程-讲师&#xff1a;雷丰阳 笔记 路还在继续&#xff0c;梦还在期许 1、HandlerInterceptor 接口 /*** 登录检查* 1、配置好拦截器要拦截哪些请求* 2、把这些配置放在容器中…

爬虫——肯德基

import requests #UA伪装&#xff1a;将对应的User-Agent封装到一个字典中 headers{User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.48 } #指定url url http://www.kfc.com.c…

【jvm系列-09】垃圾回收底层原理和算法以及JProfiler的基本使用

JVM系列整体栏目 内容链接地址【一】初识虚拟机与java虚拟机https://blog.csdn.net/zhenghuishengq/article/details/129544460【二】jvm的类加载子系统以及jclasslib的基本使用https://blog.csdn.net/zhenghuishengq/article/details/129610963【三】运行时私有区域之虚拟机栈…

一图看懂 xlrd 模块:读写 Excel 文件的数据和格式信息, 资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 xlrd 模块&#xff1a;读写 Excel 文件的数据和格式信息, 资料整理笔记&#xff08;大全&#xff09; 摘要模块图类关系图模块全展开【xlrd】统计常量intdict 模块26 os27 …

【蓝桥杯省赛真题19】python完数及个数 青少年组蓝桥杯python编程省赛真题解析

目录 python完数及个数 一、题目要求 1、编程实现 2、输入输出 二、解题思路

itop-3568开发板驱动学习笔记(22)设备树(一)设备树基础

《【北京迅为】itop-3568开发板驱动开发指南.pdf》 学习笔记 文章目录 设备树简介设备树编译设备树语法设备根节点设备子节点节点名称reg 属性#address-cell 和 #size-cells 属性model 属性status 属性compatible 属性aliases 节点chosen 节点device_type 属性自定义属性 设备树…

使用Docker创建并运行Jenkins详细步骤

文章目录 一、使用Docker搭建Jenkins二、为Jenkins配置执行节点1、进入管理页面2、新建节点3、配置节点信息4、子节点连接master节点5、在子节点机器上运行上面复制下来的命令6、查看子节点是否在线 三、创建一个简单的job1、进入创建页面2、设置job名称3、配置job相关信息4、构…

eBay变现方式有哪些?如何利用好测评自养号?

近年来&#xff0c;越来越多的人选择在eBay开店&#xff0c;甚至很多其他平台的卖家也转型到了eBay。但很多卖家发现&#xff0c;在运营了一段时间后&#xff0c;过了对新账号的流量扶持期&#xff0c;店铺突然出现流量开始下滑的情况&#xff0c;针对这种情况卖家可以采取哪些…

计算机网络(数据链路层)部分习题

1. 通过传统以太网发送中文“华南师范大学计算机学院”&#xff0c;封装成以太网帧&#xff0c;请问该帧的数据字段有效字节是多少&#xff1f;需要填充多少个字节&#xff1f; 答&#xff1a;一个字两个字节&#xff0c;“华南师范大学计算机学院”共11个字&#xff0c;有效字…

Visual Studio容器工具要求在构建,调试或运行容器化项目之前运行Docker

出现此提示&#xff0c;是因为电脑未安装Docker所致&#xff0c;接下来就教大家如何安装Docker。 第一步&#xff1a;下载 地址&#xff1a;Install Docker Desktop on Windows | Docker Documentation 第二步&#xff1a;安装 1、双击Docker Desktop Installer.exe运行安装程…

电子束与材料相互作用Matlab代码

标题 1 题目2 实验原理2.1 蒙特卡洛模拟的基本思想2.2 电子散射的基本概念 3 代码 1 题目 扫描透射电镜(STEM)的基本原理是用极细的扫描电子束透射样品,透射电子直接被具有一定张角的接收器所接收&#xff0c;透射电流的强度直接反应了样品的质量厚度。 对于一定厚度的样品&am…

面试官:一千万的数据,你是怎么查询的?

面试官&#xff1a;一千万的数据&#xff0c;你是怎么查询的&#xff1f; 1 先给结论 对于1千万的数据查询&#xff0c;主要关注分页查询过程中的性能 针对偏移量大导致查询速度慢&#xff1a; 先对查询的字段创建唯一索引 根据业务需求&#xff0c;先定位查询范围&#xff08…

23Java面试专题 八股文面试全套真题(含大厂高频面试真题)

准备篇-01-企业简历筛选规则 准备篇-02-简历注意事项 等写简历的时候看02和04... 准备篇-03-应届生该如何找到合适的练手项目 &#xff01;准备篇-04-Java程序员的面试过程 Redis篇-01-redis开篇 感觉有点难 Redis篇-02-redis使用场景-缓存-缓存穿透 Redis篇-03-redis使用场景-…

一日一题:第十题---并查集(集合合并)and 二叉树遍历

​作者&#xff1a;小妮无语 专栏&#xff1a;一日一题 &#x1f6b6;‍♀️✌️道阻且长&#xff0c;不要放弃✌️&#x1f3c3;‍♀️ 今天来更前几天做的&#xff0c;怕忘记了hh 目录 并查集题目描述(集合合并)代码对路径压缩的解释 二叉树遍历题目描述代码 并查集 ​​​…

【JavaEE】死锁是什么?如何避免死锁(保姆级讲解)

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE初阶 本篇文章将介绍什么是死锁&#xff0c;死锁的四大必要条件&#xff0c;如何去避免死锁~~~ 目录 一、死锁是什么&#xff1f; 二、关于死锁的情况 2.1 一个线程的情况 2.2 两个线程的情…

javaweb家具购物商城的电商设计与实现

目 录 1 绪论 1 1.1 项目背景 1 1.2 研究意义 1 1.3 本系统概述 2 2系统分析 3 2.1 系统需求分析 3 2.1.1 功能需求 3 2.1.2 性能需求 4 2.2 系统可行性分析 4 2.2.1 技术及开发方法可行性 4 2.2.2 管理可行性 4 2.2.3 经济可行性 5 2.3 业…

CPU的一、二、三级缓存

在Java并发编程中&#xff0c;我们经常会遇到共享变量的读写问题&#xff0c;关于这类问题我们经常会说到原子性、可见性、有序性这三大特性&#xff0c;再进一步会了解到总线和CPU的一、二、三级缓存。关于这三个级别的缓存网上文章介绍比较多&#xff0c;今天我们这篇文章&am…