关于c语言,你必须了解的运行流程

流程图

 

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

在ANSIC任何一种实现下,都存在两种环境,程序的翻译环境和执行环境

翻译环境:将源代码转换成机器指令

执行环境:用于执行代码

 2.详解编译+链接

简单的说一个代码从编写到看到控制台的结果分为编译链接两步即可,接下来我们将详细解释编译链接中的步骤,帮助读者更好的理解c语言的执行步骤.

2.1 翻译环境

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

 2.2 编译

这里我们给定几段代码区体现它编译时候发生的事情,我们先给定结论,最后再一步一步解释

//sum.c
int g_val = 2016;
void print(const char *str)
{printf("%s\n", str);
}
//test.c
#include <stdio.h>
int main()
{extern void print(char *str);extern int g_val;printf("%d\n", g_val);print("hello bit.\n");return 0;
}

注:以上_sum等地址是我随意编撰的地址

在vs这种集成环境中,我们不好去测试,我们可以选择在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中。
 

2.3 运行环境 

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

3.预处理 

3.1 预定义符号

__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
int main()
{//__FILE__ //进行编译的源文件//__LINE__ //文件当前的行号//	__DATE__ //文件被编译的日期//	__TIME__ //文件被编译的时间//	__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义printf("%s\n", __FILE__);printf("%d\n",__LINE__ );printf("%s\n", __DATE__);printf("%s\n", __TIME__);return 0;
}

我们不妨打印出来看看 

3.2 #定义标识符

语法:
#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后面不需要加上分号,因为#定义的标识符会在预编译的时候被替换,到时候可能就会多个分号

3.2.2 #define 定义宏

#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。
声明方式举例 #define name( parament-list ) stuff

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

#define在进行简单运算的时候可以使用,但是复杂运算的时候不建议使用,可能由于优先级的原因发生错误

举例:

#define SQUARE( x ) x * xSQUARE( 5 ); // 答案是5*5//执行以下代码
int a = 5;
printf("%d\n" ,SQUARE( a + 1) );//答案是11而不是36 
因为替换的时候是 5+1*5+1 = 11

所以我们建议使用宏的时候加上括号

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

还有一个问题,当我们定义了这样一个宏的时候,传入这样的参数

#define DOUBLE(x) (x) + (x)int a = 5;
printf("%d\n" ,10 * DOUBLE(a));//替换后
printf ("%d\n",10 * (5) + (5));

所以我们建议在宏的表达式也加上括号

#define DOUBLE( x) ( ( x ) + ( x ) )

3.2.3 宏替换规则 

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

3.3 #和##的规则 

我们先给一串代码

char* p = "hello ""bit\n";
printf("hello"" bit\n");
printf("%s", p);

我们发现字符串是有自动拼接的效果的

于是我们可以写出以下的代码

#define PRINT(FORMAT, VALUE)\
printf("the value is "FORMAT"\n", VALUE);
...
PRINT("%d", 10);

这里只有当字符串作为宏参数的时候才可以把字符串放在字符串中。


1. 使用 # ,把一个宏参数变成对应的字符串
 

int i = 10;
#define PRINT(FORMAT, VALUE)\
printf("the value of " #VALUE "is "FORMAT "\n", VALUE);
...
PRINT("%d", i+3);//产生了什么效果?打印出
the value of i+3 is 13

2.## 的作用

##的作用是可以把##两边的符号合并,举例:

#define ADD_TO_SUM(num, value) \ 续行符
sum##num += value;
...
ADD_TO_SUM(5, 10);//作用是:给sum5增加10.

注:这样的连接必须产生一个合法的标识符,否则其结果就是未定义的.


 

3.带副作用的宏参数

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

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

#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);//输出的结果是什么?

预处理后产生替换
z = ( (x++) > (y++) ? (x++) : (y++));

结果是 6 10 9

4. 宏和函数的对比

宏通常被应用于执行简单的运算,比如在两个数中找出较大的一个.

为啥不用函数呢?原因有两个:

宏的优势


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

宏相比函数的劣势:

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

宏能做而函数做不了的事情

宏的参数可以是类型,比如int char...

#define MALLOC(num, type)\
(type *)malloc(num * sizeof(type))
...
//使用
MALLOC(10, int);//类型作为参数
//预处理器替换之后:
(int *)malloc(10 * sizeof(int));

5.命令行定义

许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。
例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一个机器内存大写,我们需要一个数组能够大写。)

#include <stdio.h>
int main()
{int array [ARRAY_SIZE];int i = 0;for(i = 0; i< ARRAY_SIZE; i ++){array[i] = i;}for(i = 0; i< ARRAY_SIZE; i ++){printf("%d " ,array[i]);}printf("\n" );return 0;
}

这里我们可以在linux等环境下进行操作

gcc -D ARRAY_SIZE=10 programe.c
 

 

6.#包含的头文件和自定义头文件的区别

""自定义包含的头文件会先去工程路径下寻找文件,找不到就去标准路径下寻找文件,找不到就报错.

<>头文件直接去标准路径下寻找头文件,找不到就报错.

 

 7.条件编译(类似于选择语句)

在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令,因为有些代码在不同的情况下运行不同的代码.
 

1.
#if 常量表达式
//...
#endif
//常量表达式由预处理器求值。
如:
#define __DEBUG__ 1
#if __DEBUG__
//..
#endif
2.多个分支的条件编译
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif
3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol
4.嵌套指令
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif

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

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

相关文章

FoLR:Focus on Local Regions for Query-based Object Detection论文学习笔记

论文地址&#xff1a;https://arxiv.org/abs/2310.06470 自从DETR问询式检测器首次亮相以来&#xff0c;基于查询的方法在目标检测中引起了广泛关注。然而&#xff0c;这些方法面临着收敛速度慢和性能亚优等挑战。值得注意的是&#xff0c;在目标检测中&#xff0c;自注意力机制…

Java8实战-总结44

Java8实战-总结44 CompletableFuture&#xff1a;组合式异步编程Future 接口Future 接口的局限性使用 CompletableFuture 构建异步应用 CompletableFuture&#xff1a;组合式异步编程 最近这些年&#xff0c;两种趋势不断地推动我们反思我们设计软件的方式。第一种趋势和应用运…

微信小程序设计之主体文件app-wxss/less

一、新建一个项目 首先&#xff0c;下载微信小程序开发工具&#xff0c;具体下载方式可以参考文章《微信小程序开发者工具下载》。 然后&#xff0c;注册小程序账号&#xff0c;具体注册方法&#xff0c;可以参考文章《微信小程序个人账号申请和配置详细教程》。 在得到了测…

STM32F4X之中断二

一、外部中断 外部中断&#xff1a;外部中断的中断是相对于外部中断控制器&#xff08;EXTI&#xff09;来说&#xff0c;如下图所示&#xff1a; EXTI掌管着23根中断线&#xff0c;具体分布图下&#xff1a; 16根连接GPIO口&#xff0c;如下图&#xff1a; 所有的0口连接到中…

餐饮外卖小程序商城的作用是什么

随着互联网及线上餐饮的发展趋势&#xff0c;行业洗牌正在加速&#xff0c;并且对餐饮连锁门店提出更高要求&#xff0c;餐饮数字化转型加快&#xff0c;积极发展线上经营是不少餐饮商家的首选。这其中&#xff0c;餐饮外卖商城成为很多餐饮品牌的线上经营品牌&#xff0c;也是…

VR全景应用广泛体现在哪里?有何优势?

VR全景作为一种新型营销方式&#xff0c;正在逐渐走进人们的视线&#xff0c;它区别于以往单一角度的照片和视频&#xff0c;VR全景制作显得更加直观、更加真实、更加生动。VR全景通过VR技术将所拍摄的图片变成720度可观看的场景模式&#xff0c;把产品的特色以及魅力整体呈现展…

对长度为n的顺序表L,编写一个时间复杂度为O(n),空间复杂度为O(1)的算法,该算法删除线性表中的所有值为x的数据元素

对长度为n的顺序表L&#xff0c;编写一个时间复杂度为O(n)&#xff0c;空间复杂度为O(1)的算法&#xff0c;该算法删除线性表中的所有值为x的数据元素 算法思路&#xff1a; 用count标记遇到x的次数&#xff0c;每次遇到x&#xff0c;count 遇到非x的元素&#xff0c;把它前移…

vue3后台管理系统之顶部tabbar组件搭建

1.1静态页面搭建 <template><div class"tabbar"><div class"tabbar_left"><!-- 面包屑 --><Breadcrumb /></div><div class"tabbar_right"><!-- 设置 --><Setting /></div></di…

自动驾驶的未来展望和挑战

自动驾驶技术是一项引人瞩目的创新&#xff0c;将在未来交通领域产生深远影响。然而&#xff0c;随着技术的不断演进&#xff0c;自动驾驶也面临着一系列挑战和障碍。本文将探讨自动驾驶的未来发展方向、技术面临的挑战&#xff0c;以及自动驾驶对社会和环境的潜在影响。 自动驾…

封装一个vue3 Toast组件,支持组件和api调用

先来看一段代码 components/toast/index.vue <template><div v-if"isShow" class"toast">{{msg}}</div> </template><script setup> import { ref, watch } from vue const props defineProps({show: {type: Boolean,def…

JVM 类的加载子系统

文章目录 类的加载过程加载阶段链接阶段初始化 类的加载器测试代码中获取对应的加载器获取加载器加载的路径不同类对应的加载器自定义加载器自定义加载器的方式 获取类的加载器的方式双亲委派机制双亲委派机制的好处 Java 的 SPI 机制1. 接口定义2. 具体实现3. 配置 META-INF/s…

Spring Cloud Gateway 路由构建器的源码分析

Spring Cloud Gateway 路由构建器的源码分析 文章目录 1. 路由构建器的入口2. 创建路由规则3. 设置路由规则和属性4. 路由过滤器的设置5. 构建和获取路由规则&#xff1a;6. 实例化路由构建器&#xff1a;8. 路由构建器的源码分析8.1 RouteLocator接口8.2 RouteLocatorBuilder…

springBoot与Vue共同搭建webSocket环境

欢迎使用Markdown编辑器 你好&#xff01; 这片文章将教会你从后端springCloud到前端VueEleementAdmin如何搭建Websocket 前端 1. 创建websocket的配置文件在utils文件夹下websocket.js // 暴露自定义websocket对象 export const socket {// 后台请求路径url: ,websocketCo…

Android前台服务和通知

前台服务 Android 13及以上系统需要动态获取通知权限。 //android 13及以上系统动态获取通知权限 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {checkPostNotificationPermission(); } private void checkPostNotificationPermission() {if (ActivityCompat.chec…

信息检索与数据挖掘 | 【实验】排名检索模型

文章目录 &#x1f4da;实验内容&#x1f4da;相关概念&#x1f4da;实验步骤&#x1f407;分词预处理&#x1f407;构建倒排索引表&#x1f407;计算query和各个文档的相似度&#x1f407;queries预处理及检索函数&#x1f525;对输入的文本进行词法分析和标准化处理&#x1f…

【Docker】Dockerfile使用技巧

开启Buildkit BuildKit是Docker官方社区推出的下一代镜像构建神器&#xff0c;可以更加快速&#xff0c;有效&#xff0c;安全地构建docker镜像。 尽管目前BuildKit不是Docker的默认构建工具&#xff0c;但是完全可以考虑将其作为Docker&#xff08;v18.09&#xff09;的首选…

软件工程与计算总结(二十三)软件工程职业基础

本系列最后一更&#xff0c;《软计》系列总结的大结局&#xff01;本栏目告一段落&#xff0c;之后会结合真题和练习题再发布新的总结~ 往期链接&#xff1a; 《软件工程与计算》总结 一.软件工程职业 1.行业的发展 20世纪50年代&#xff1a;计算机还是研究型机器&#xff…

C++中显示构造和隐式构造

截图来源于C primerplus 第六版。

tomcat的负载均衡、动静分离(nginx联动)

动静分离&#xff1a; 访问静态页面和动态页面分开 实现动态和静态页面负载均衡 实验5台虚拟机 一、动态负载均衡 3台虚拟机模拟&#xff1a; 代理服务器&#xff1a;30 tomcat动态页面&#xff1a;21、22 代理服务器&#xff1a; proxy_pass http://tomcat; proxy_set_h…

buuctf[HCTF 2018]WarmUp 1

题目环境&#xff1a; 发现除了表情包&#xff0c;再无其他F12试试发现source.php文件访问这个文件&#xff0c;格式如下&#xff1a;url/source.php回显如下&#xff1a;PHP代码审计&#xff1a; <?php highlight_file(__FILE__); class emmm {public static function ch…