找回密码
 register

QQ登录

只需一步,快速开始

查看: 125|回复: 0

[C语言] 第十八课(04.00-04.01):预处理

[复制链接]

[C语言] 第十八课(04.00-04.01):预处理

[复制链接]
  • 打卡等级:热心大叔
  • 打卡总天数:91
  • 打卡月天数:14
  • 打卡总奖励:91
  • 最近打卡:2025-01-15 02:59:34
Waylee

主题

0

回帖

1万

积分

仙帝

积分
11890
Waylee 2024-8-8 17:19 | 显示全部楼层 |阅读模式

马上注册,查看网站隐藏内容!!

您需要 登录 才可以下载或查看,没有账号?register

×
C语言编译过程
gcc -E hello.c -o hello.i        预处理
gcc -S hello.i -o hello.s        编译
gcc -c hello.s -o hello.o        汇编
gcc hello.o -o hello hello_elf 链接
#include<stdio.h>

预处理:头文件包含\宏替换\条件编译\删除注释\不做语法检查
编译:将预处理后的文件生成汇编文件 语法检查
汇编:将汇编文件 编译 二进制文件
链接:将众多的二进制文件+库+启动代码 生成 可执行文件
总结:一步到位 编译 gcc 源文件 -o 可执行文件
预处理
C编译器提供的预处理功能主要有以下四种:
  • 文件包含  #include
  • 宏定义     #define
  • 条件编译  #if #endif ..
  • 一些特殊作用的预定义宏
头文件包含<>,""  (了解)
#include<stdio.h>   表示从系统指定的目录下寻找stdio.h
#include"hehe.h"    表示先源文件所在的目录寻找,如果找不到再到系统指定的目录下寻找
1.jpg
宏定义 define
在源程序中,允许一个标识符(宏名)来表示一个语言符号字符串用指定的符号代替指定的信息
宏一般为 大写(将宏和普通变量区分开)
1.不带参数的宏
#define 宏名 字符串
例如:define PI 3.1415926

  • 在编译预处理时,将程序中在该语句以后出现的所有的PI都用3.1415926代替
  • 这种方法使用户能以一个简单的名字代替一个长的字符串
  • 在预编译时将宏名替换成字符串的过程称为"宏展开"
  • 宏定义,只在宏定义的文件中起作用
#include<stdio.h>
//宏 后面不要加分号
#define N "hehe"
void test01()
{
        //在预处理阶段 "hehe" 替换 代码中所有出现的N (宏展开)
        printf("%s\n",N);
}
int main(int argc,char *argv[])
{
        test01();
        return 0;
}

中止宏的作用范围: #undef 宏名
#include<stdio.h>
#define N "hehe"
void test01()
{
        printf("%s\n",N); //OK 识别的

        //中止宏N的作用
#undef N
        //printf("%s\n",N); err 不识别N
        return;
}
int main(int argc,char *argv[])
{
        test01();
        return 0;
}

2.带参数的宏(宏函数)
格式:#define 宏名(形参表) 字符串
调用:宏名(形参表)
宏展开:进行宏替换
#define 宏名(参数1,参数2,....) 字符串
//宏的参数 a b 不能类型
//#define MY_ADD(int a,int b) a+b //错误
#define MY_ADD(a,b) a+b
//调用 宏名(参数)
MY_ADD(10,20); // 等价 10+20
[b]案例:
[pre]#include<stdio.h>
//宏展开的本质 就是替换
#define MY_MUL1(a,b) a*b
#define MY_MUL2(a,b) ((a)*(b))
void test01()
{
        printf("%d\n",MY_MUL1(10,20)); // MY_MUL(10,20) = 10*20
        //MY_MUL1(10+10,20+20) == 10+10*20+20
        printf("%d\n",MY_MUL1(10+10,20+20));  //输出:230 ,不能保证完整性
        //#define MY_MUL2(a,b) ((a)*(b)) == ((10+10)*(20+20))
        printf("%d\n",MY_MUL2(10+10,20+20));  //输出:800 ,保证完整性
        return;
}
int main(int argc,char *argv[])
{
        test01();
        return 0;
}

3.带参数的宏(宏函数) 和普通函数有啥区别
带参数的宏(宏函数) 调用多少次 就会展开多少次,执行代码的时候 没有函数调用的过程,也不需要函数的出入栈的问题,所以带参数的宏 浪费空间 节省了时间。
带参的函数:代码只有一份,存在代码段,调用的时候去代码段读取函数指令,调用的时候要压栈(保存调用函数前的相关信息),调用完出栈(恢复调用函数前的相关信息),所以函数浪费了时间,节省了空间。
#include<stdio.h>
//宏展开的本质 就是替换
//宏函数
#define MY_MUL1(a,b) a*b
#define MY_MUL2(a,b) ((a)*(b))

//普通函数
int my_mul(int a,int b) //a = 10+10  b = 20+20 
{
        return a*b;
}
void test01()
{
        //预处理阶段完成 宏的替换
        printf("%d\n",MY_MUL1(10,20)); // MY_MUL(10,20) = 10*20
        //MY_MUL1(10+10,20+20) == 10+10*20+20
        printf("%d\n",MY_MUL1(10+10,20+20));  //输出:230 ,不能保证完整性
        //#define MY_MUL2(a,b) ((a)*(b)) == ((10+10)*(20+20))
        printf("%d\n",MY_MUL2(10+10,20+20));  //输出:800 ,保证完整性
        
        //执行
        printf("%d\n",my_mul(10+10,20+20));   //输出:800
        return;
}
int main(int argc,char *argv[])
{
        test01();
        return 0;
}

案例,最终输出结果是多少?:
#define MY_ADD(a,b) a+b
#define MY_MUL(a,b) a*b
printf("%d\n", MY_MUL(MY_ADD(10+10,20+20),MY_MUL(10+10,20+20)) );

解析:
#include<stdio.h>
#define MY_ADD(a,b) a+b
#define MY_MUL(a,b) a*b
int main(int argc,char *argv[])
{        
        //MY_MUL(MY_ADD(10+10,20+20),MY_MUL(10+10,20+20))
        //MY_MUL(10+10+20+20,10+10*20+20)
        //10+10+20+20*10+10*20+20  = 460 
        printf("%d\n", MY_MUL(MY_ADD(10+10,20+20),MY_MUL(10+10,20+20)) );
        return 0;
}

条件编译:
一般情况下,源程序中所有的行都参加编译,但有时希望对部分程序行只在满足一定条件时才编译,技对着部分源程序行指定编译条件
测试存在:
#ifdef 标识符
    程序段1;
#else
    程序段2;
#endif

测试不存在:
#ifndef 标识符
    程序段1;
#else
    程序段2;
#endif

判断条件是否成立:
#if 标识符
    程序段1;
#else
    程序段2;
#endif

案例一:测试不存在
223.jpg
运行结果:
224.jpg
案例二:
#include<stdio.h>
int main(int argc,char *argv[])
{
#if 1
        printf("--001--\n");
#else
        printf("--002--\n");
#endif
        return 0;
}

综合案例:
通过条件编译 控制大小写的转换
#include<stdio.h>
#include<string.h>
int main(int argc,char *argv[])
{
        char buf[128] = "";
        int i = 0;
        printf("请输入字符串:");
        //fgets会获取换行符“\n”
        fgets(buf,sizeof(buf),stdin);
        //去掉换行符 strlen返回的是字符串的长度 不包含'\0'
        //strlen(buf)-1 这是换行符的下标位置
        buf[strlen(buf)-1] = 0;

        while(buf) //最后一个元素是'\0' == 0 == 假  循环进不去
                {#if 0
                        //大写变小写
                        if(buf>='a' && buf <= 'z')
                                buf =buf-32;
#else
                        //小写变大写
                        if(buf>='A' && buf <= 'Z')
                                buf =buf+32;
#endif
                        i++;
                }
        printf("buf = %s\n",buf);
        return 0;
}


您需要登录后才可以回帖 登录 | register

本版积分规则

雪舞知识库 | 浙ICP备15015590号-1 | 萌ICP备20232229号|浙公网安备33048102000118号 |天天打卡

GMT+8, 2025-1-15 13:19 , Processed in 0.118805 second(s), 9 queries , Redis On.

Powered by XueWu Licensed

Copyright © Tencent Cloud.

快速回复 返回顶部 返回列表