程序的翻译环境

程序的执行环境

详解C语言程序的编译+链接

预定义符号

预处理指令#define

宏和函数的对比

预处理操作符#和##的介绍

命令定义

预处理指令#include
 

程序的翻译环境

程序的翻译环境和执行环境
在任何c的实现中都存在两种不同的环境
1.翻译环境就是在这环境中源代码被转换为可执行的机器指令
2.执行环境就是实际执行代码

程序的执行环境

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

 详解C语言程序的编译+链接

预定义符号

__FILE__打印当前文件所在的路径

__LINE__打印当前代码所在函数

__DATE__打印当前日期

__TIME__打印当前时间

预处理指令#define

define定义符号,可以提升代码的可读性

不仅可以定义数字,也可以定义代码,关键字,或者符号,
#define m 100 #define ret register//用ret来替换registr #define do_forever for( ;
;)//死循环 #define x int* #include<stdio.h> int main() {     ret int b = 10;   
 do_forever;     int a = m;//100 x p=&a;//p为int*的指针变量     return 0; } #define x
int * typedef int * INT #include<stdio.h> int main() { x a,b; INT c,d;
//其中只有b不是指针 //因为define只完成代码的替换,替换成了int* a,b//a是指针,b不是指针 //而typedef是对类型的重命名,
return 0; }

宏和函数的对比

define 定义宏,同样是完成替换
#define机制包括一个规定,把常数替换到文本中,这种实现形式就叫做宏,或定义宏,
参数左括号必须与函数名紧邻,若中间又有空格,就会被解释为stuff中的成员,不可吝啬括号
#include<stdio.h> //不可以吝啬括号 #define square(x) x*x//建议把x加个括号(x),将其当做一个整体,括号很重要
#define double(x) (x)+(x)//将之当成一个整体才可以 #define doubl(x) ((x)+(x)) int main() {
printf("%d\n", square(3));//会被替换成printf("%d",3*3); printf("%d\n", square(3 +
1));//7!=16,会被替换,宏的参数是完成替换的,不是计算的,不经过任何计算直接传过去 //会被替换成printf("%d",3+1*3+1))//7
//加括号就变成了(3+1)*(3+1) printf("%d", 10 *
double(4));//这样写也是有问题的,会被替换成10*(4)+(4)=44 printf("%d", 10 * doubl(4));//这样就可以了;
return 0; }
再调用宏的时候,首先对参数进行检查,是否有define定义的符号
宏不能递归,而函数可以
当预处理时,字符串常量不能被替换,如printf(“”)里的不被替换

#define m 100 #include<stdio.h> int main() { int c = max(101, m);//宏替换,
printf("m=%d", m);//括号里面的m不被替换 return 0; }
 
预处理操作符#和##的介绍

#可以把参数插入到字符串中
#define print(x) printf("the value of " #x " is
%d\n",x)//#x会变成x对于的内容所对于的字符串"a" #include<stdio.h> int main() { printf("hello
world\n"); printf("hello""world\n");//结果是一样的, //写3个printf()有点冗余, int a = 10;
print(a); //the value of a is 10 int b = 20; print(b); //the value of b is 20
int c = 30; print(c); //the value of b is 30 printf(""); return 0; }
##可以把两个符号合成一个符号,两个符号连在一起
#define cd(x,y) x##y #include<stdio.h> int main() { int c = 100; printf("%d",
cd(c, 120));//替换成100120 return 0; }
带副作用的宏参数 

首先介绍一下什么是副作用

int a=1;
int b=a+1;//b=2,a=1,没有副作用
int b=++a//b=2,而a=2这里的a就是有副作用,
#include<stdio.h> #define max(x,y) (x)>(y)?(x):(y) int main() { int a = 5; int
b = 8; int c = max(a++, b++); //int c = (a++) > (b++) ? (a++) : (b++);替换
//5>8不成立,执行完判断语句,a和b就加1,a=6,b=9,则执行b++,但b++是先使用在++ //c=9,执行完后b=10 printf("%d",
c);//9 return 0; }
宏和函数的对比

1.宏比函数在程序的规模上和速度上更胜一筹
2.更为重要的是,函数在定义上有数据类型限制,若定义int型数据,则double型数据就无法传参,而宏与类型无关,

3.命名规定,宏名全部大写,函数名不全部大写(不成文的规定)

命令定义

#undef移除一个宏定义

#if 加常量表达式,为0为真往下面执行

#endif为#if的结束标志

#ifdef 如果定义了就执行下面的代码

#ifndef 如果没定义就执行下面的代码

#undef//移除一个宏定义 #define m 100 #include<stdio.h> int main() { int a = m; #undef
m//把定义的m取消掉 printf("%d", a);//a就没有值 return 0; } //#if
加常量表达式(非0未真就往下执行,直到#endif停止) //#endif为#if的结束标志 //#if 0//相当于把下面的代码给注释掉, #define
print 1 int main() { #if 1-2//1为真就执行,0就为假不执行,非0为真 #endif #if
print//print为1为真就往下执行下面的代码 printf("hehe "); #endif return 0; }
#include<stdio.h> int main() { #if
1==1//判断成立,就往下执行,从多分支只选择一个,选择完下面就不执行跳到#endif去 printf("hh"); #elif 1 == 2
printf("haha"); #else printf("hehe"); #endif return 0; } //判断是否被定义的写法
#include<stdio.h> int main() { #ifdef test//如果test被定义了,下面参与执行,给个0也可以
printf("test"); #endif #ifndef hh//如果hh不定义,下面参与编译 printf("hehe"); #endif return
0; }

预处理指令#include

文件包含
1.<>直接去库函数的头文件所在的目录下查找
2.""形式的包含文件,(1).在自己写的代码底下所在的目录去查找,(2).如果1找不到,就在库函数的头文件目录下查找

嵌套文件包含
一个头文件被重复包含两次,有点啰嗦
#pragma once//头文件只包含一次,不管自己写了多少此,加上这句都只用一次
头文件中的ifndef,define ,endif是用来防止文件被多次包含

技术
©2019-2020 Toolsou All rights reserved,
TypeScript:函数类型接口8道大厂指针笔试题让你秒杀指针!!!MySQL 日期时间加减mysql 查询条件之外的数据_mysql 查询符合条件的数据查linux的操作系统版本,如何查看Linux操作系统版本?将String类型转换成Map数据类型使用uuid做MySQL主键,被老板,爆怼一顿C语言中的字符串函数和字符函数linux服务器中毒排查--基础篇C# ASCII码字符转换