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

简介: 程序的翻译环境:整个翻译环境很复杂,简单形容:我们运行一个c语言代码,首先会将代码进行转化,将c语言代码转化为汇编代码,再将汇编代码转化为电脑看的懂的二进制语言,项目下的所有文件都会进行上述操作,之后进行链接,将文件链接到一起。

程序的翻译环境:

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

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

d1e9dfce2f114a14b0ebea2d29c21e1b.png

程序的运行环境:

程序执行过程:

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__);
}

9a6e8d5e4b534caba079867d2a69b768.png

#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语言的博客,撒花!
























相关文章
|
1月前
|
IDE 编译器 开发工具
【C语言】全面系统讲解 `#pragma` 指令:从基本用法到高级应用
在本文中,我们系统地讲解了常见的 `#pragma` 指令,包括其基本用法、编译器支持情况、示例代码以及与传统方法的对比。`#pragma` 指令是一个强大的工具,可以帮助开发者精细控制编译器的行为,优化代码性能,避免错误,并确保跨平台兼容性。然而,使用这些指令时需要特别注意编译器的支持情况,因为并非所有的 `#pragma` 指令都能在所有编译器中得到支持。
121 41
【C语言】全面系统讲解 `#pragma` 指令:从基本用法到高级应用
|
2月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
89 5
|
2月前
|
C语言
C语言编程中,错误处理至关重要,能提升程序的健壮性和可靠性
C语言编程中,错误处理至关重要,能提升程序的健壮性和可靠性。本文探讨了C语言中的错误类型(如语法错误、运行时错误)、基本处理方法(如返回值、全局变量、自定义异常处理)、常见策略(如检查返回值、设置标志位、记录错误信息)及错误处理函数(如perror、strerror)。强调了不忽略错误、保持处理一致性及避免过度处理的重要性,并通过文件操作和网络编程实例展示了错误处理的应用。
84 4
|
2月前
|
程序员 编译器 C语言
C语言中的预处理器指令,涵盖其基本概念、常见指令(如`#define`、`#include`、条件编译指令等)、使用技巧及注意事项
本文深入解析C语言中的预处理器指令,涵盖其基本概念、常见指令(如`#define`、`#include`、条件编译指令等)、使用技巧及注意事项,并通过实际案例分析,展示预处理器指令在代码编写与处理中的重要性和灵活性。
84 2
|
2月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
83 1
|
2月前
|
网络协议 物联网 数据处理
C语言在网络通信程序实现中的应用,介绍了网络通信的基本概念、C语言的特点及其在网络通信中的优势
本文探讨了C语言在网络通信程序实现中的应用,介绍了网络通信的基本概念、C语言的特点及其在网络通信中的优势。文章详细讲解了使用C语言实现网络通信程序的基本步骤,包括TCP和UDP通信程序的实现,并讨论了关键技术、优化方法及未来发展趋势,旨在帮助读者掌握C语言在网络通信中的应用技巧。
67 2
|
2月前
|
程序员 C语言
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门
C语言中的指针既强大又具挑战性,它像一把钥匙,开启程序世界的隐秘之门。本文深入探讨了指针的基本概念、声明方式、动态内存分配、函数参数传递、指针运算及与数组和函数的关系,强调了正确使用指针的重要性,并鼓励读者通过实践掌握这一关键技能。
57 1
|
C语言 编译器
《C语言编程初学者指南》一1.6 使用指令
本节书摘来自华章出版社《C语言编程初学者指南》一书中的第1章,第1.6节,作者【美】Keith Davenport(达文波特) , M1ichael Vine(维恩),更多章节内容可以访问云栖社区“异步社区”公众号查看 1.6 使用指令 下面再看看本章一开始给出的示例程序。
1505 0
|
13天前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
52 23
|
13天前
|
算法 C语言
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
44 15

热门文章

最新文章