C中的预处理,宏

这篇具有很好参考价值的文章主要介绍了C中的预处理,宏。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数,C初学者入门训练,题解C,C的使用文章,「初学」C++

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

🐰宏的缺点

🐰用宏实现动态开辟的技巧

🐰命名约定

🐰#undef

🐰条件编译

🌸#ifdef

🌸#ifndef

🌸#if defined(symbol)

🌸#if !defined(symbol)

🐰常见的条件编译指令

🌸#if

          🌸多分支的条件编译

🐰文件包含

🐰atoi

🐰offsetof

🐰模拟实现offsetof

🐰有关宏的习题

🌸写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。


🐰宏的缺点

1.每次使用宏的时候,会将宏定义的代码插入到程序当中,增加了程序的长度

2.宏没有办法调式

3.宏由于类型无关,也不够严谨

4.宏可能会带来运算符优先级的问题,导致程序容易出错。(可以适当添加圆括号来解决)

#include<stdio.h>
#define MAX(x,y) ((x)>(y)?(x):(y))
int Max(int x,int y)
{
    return x>y?x:y;
}
int main()
{
    int a=2,b=3;
    //函数
    printf("%d\n",Max(a, b));
    //宏
    printf("%d\n",MAX(a, b));
    return 0;
}

🐰用宏实现动态开辟的技巧

#include<stdio.h>
#include<stdlib.h>
#define MALLOC(x,type) (type*)malloc(x*sizeof(type))
int main()
{
    int *p=(int*)malloc(10*sizeof(int));
    //宏
    //这样使得动态开辟空间更加方便
    int* p2=MALLOC(10, int);
    //只需传递开辟空间的大小,和类型
    return 0;
}

🐰命名约定

标准并没有规定宏名必须全部大写,函数名不能全部大写,只是一种俗称的习惯,这样更容易区分宏和函数

把宏名全部大写

函数名不全部大写

🐰#undef

#undef用于移除一个宏定义

例如:

#undef用于移除一个宏定义
#include<stdio.h>
#define M 100
int main()
{
    printf("%d\n",M);
#undef M//M就被移除了
    printf("%d\n",M);
    return 0;
}

🐰条件编译

在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令

调试性的代码,删除可惜,保留碍事,我们选择性编译

🌸#ifdef

#ifdef symbol//如果symbol定义,则执行#ifdef symbol和endif的语句

...

endif

例如:

#include<stdio.h>
#define __DEBUG__ 10
int main()
{
    int arr[10];
    for(int i=0;i<10;i++)
    {
        arr[i]=i;
#ifdef __DEBUG__//如果没有定义__DEBUG__,则后面#endif之前的语句不编译
        printf("%d ",arr[i]);
#endif
    }
    return 0;
}
结果:
0 1 2 3 4 5 6 7 8 9
因为这里的__DEBUG__ 被定义了,所以会执行#ifdef和#endif之间的语句

🌸#ifndef

#ifndef symbol //如果symbol没定义,则执行#ifdef symbol和endif的语句

...

#endif
#endif
#include<stdio.h>
#define __DEBUG__ 10
int main()
{
    int arr[10];
    for(int i=0;i<10;i++)
    {
        arr[i]=i;
#ifndef __DEBUG__
        printf("%d ",arr[i]);
#endif
    }
    return 0;
}
没有输出结果,因为__DEBUG__ 定义了,所以不会执行#ifndef和#endif之间的语句。这个和#ifdef和#endif执行的情况相反

🌸#if defined(symbol)

#if defined(symbol)//如果symbol定义,则执行#ifdef symbol和endif的语句

...

endif
#include<stdio.h>
#define __DEBUG__ 10
int main()
{
    int arr[10];
    for(int i=0;i<10;i++)
    {
        arr[i]=i;
#if defined(__DEBUG__)
        printf("%d ",arr[i]);
#endif
    }
    return 0;
}
结果:0 1 2 3 4 5 6 7 8 9
因为定义了__DEBUG__,所以会执行#if defined(__DEBUG__)和#endif之间的代码,这个和#ifdef和endif使用情况一样

🌸#if !defined(symbol)

#if !defined(symbol)//如果symbol没定义,则执行#ifdef symbol和endif的语句

...

endif
#include<stdio.h>
#define __DEBUG__ 10
int main()
{
    int arr[10];
    for(int i=0;i<10;i++)
    {
        arr[i]=i;
#if !defined(__DEBUG__)
        printf("%d ",arr[i]);
#endif
    }
    return 0;
}

🐰常见的条件编译指令

🌸#if

#if 常量表达式

...

#endif

如果常量表达式的值为真,则执行#if和#endif之间的代码 

#include<stdio.h>
int main()
{
#if 1==1//如果常量表达式的值为真,则执行#if和#endif之间的代码
    printf("hehe\n");
#endif
    return 0;
}
结果:
hehe
因为1==1表达式的值为真

🌸多分支的条件编译

#if 常量表达式

...

#elif 常量表达式

...

#elif 常量表达式

...

endif
#include<stdio.h>
int main()
{
#if 1==1//这里可以与if 和else if的联系,只会执行一个(一组)语句
    printf("hehe1\n");
#elif 1==1//
    printf("hehe2\n");
#elif 1==1//
    printf("hehe3\n");
#endif
    return 0;
}
结果:hehe1
因为第一个常量表达式1==1的值为真,所以执行printf("hehe2\n");,不会执行以下的分支语句了,这里与if和else if执行情况差不多

🐰文件包含

头文件的包含的方式

1.本地文件的包含

#include"test.h"

查找策略:先找在源文件所在的目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件

如果找不到就提示编译错误

2.库文件的包含

#include<stdio.h>

查找策略:直接去标准位置查找头文件,如果找不到就提示编译错

如果找不到就提示编译错误

注意:虽然库函数的头文件可以用双引号查找,但是进行了两次查找,降低了代码的速率

头文件多重包含(C++和类的多重继承类造成成员数据的重复)

解决方法:

C中的预处理,宏添加#pragma once,可以防止头文件多刺被包含

 

🐰atoi

用于将字符串转换成整形

atoi的原型:

int atoi (const char * str);

const char * str:字符串的首地址

#include<stdio.h>
#include<stdlib.h>
int my_atoi(char* ptr)
{
    int sum=0;
    int flag=1;
    if(*ptr=='+')
    {
        flag=1;
        ptr++;
    }
    if(*ptr=='-')
    {
        flag=-1;
        ptr++;
    }
    while(*ptr)
    {
        sum=sum*10+(*ptr-'0');
        ptr++;
    }
    sum*=flag;
    return sum;
}
int main()
{
    int ret=atoi("-1234");
    int rat=my_atoi("-1234");//模拟实现atoi
    printf("%d\n",ret);
    printf("%d\n",rat);
    return 0;
}
结果:
-1234
-1234

🐰offsetof

offsetof宏用于与求偏移量(结构体,联合体...)

offsetof的原型:

offsetof (type,member)

type:类型(结构体,联合体...)

member:成员(结构体成员...)

#include<stdio.h>
#include<stddef.h>
typedef struct S
{
    int a;//0-3
    int b;//4-7
    char c;//8
    double d;//16-23
}S;
int main()
{
    printf("%d\n",offsetof(S, a));
    printf("%d\n",offsetof(S, b));
    printf("%d\n",offsetof(S, c));
    printf("%d\n",offsetof(S, d));
    return 0;
}
结果:
0
4
8
16
分别对应着a,b,c,d的偏移量

🐰模拟实现offsetof

#include<stdio.h>
#include<stddef.h>
typedef struct S
{
    int a;//0-3
    int b;//4-7
    char c;//8
    double d;//16-23
}S;
#define OFFSETOF(t,m) (int)(&((S*)0)->m)//把0地址处强制转化为结构体指针,然后取出成员的地址,在以整形的形式输出,这样就可以得到偏移量了,(换句话说,就是结构体的成员假设从0地址处进行存储)
//注意:强制类型转化,不会真的原来的类型转化了,而是把原来的类型进行临时拷贝,然后再转化为自己想要的类型,所以没有真正把系统0地址转化为结构体指针,而是把0地址的拷贝转化为了结构体指针类型。
int main()
{
    printf("%d\n",OFFSETOF(S,a));
    printf("%d\n",OFFSETOF(S,b));
    printf("%d\n",OFFSETOF(S,c));
    printf("%d\n",OFFSETOF(S,d));
    return 0;
}
结果:
0
4
8
16

🐰有关宏的习题

🌸写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。

解题思路:

首先保存奇数位

保存的方法:例如:10(00000000000000000000000000001010)

10:            00000000000000000000000000001010

0x55555555     01010101010101010101010101010101

只要让10按位与上0x55555555就能保留奇数位(相同为相同的,不同则为0)

奇数位结果:00000000000000000000000000000000

保存偶数位

保存的方法:例如:10(00000000000000000000000000001010)

10:            00000000000000000000000000001010

0xaaaaaaaa     10101010101010101010101010101010

只要让10按位与上0xaaaaaaaa就能保留奇数位(相同为相同的,不同则为0)

偶数位结果:00000000000000000000000000001010

让奇数位左移一位

00000000000000000000000000000000<<1得到00000000000000000000000000000000


让偶数位右一位

00000000000000000000000000001010>>1得到00000000000000000000000000000101

然后移位后的奇数位和偶数位相加00000000000000000000000000000000+00000000000000000000000000000101

最后的到00000000000000000000000000000101为5
#include<stdio.h>
#define SWAP(n) n=(((n&0x55555555)<<1)+((n&0xaaaaaaaa)>>1))
int main()
{
    int a=10;
    SWAP(a);
    printf("%d\n",a);
    return 0;
}
结果:
5
10的二进制补码:00000000000000000000000000001010
转化后的补码:00000000000000000000000000000101
所以为5

🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸   文章来源地址https://www.toymoban.com/news/detail-475014.html

到了这里,关于C中的预处理,宏的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 计算机视觉任务图像预处理之去除图像中的背景区域-------使用连通域分析算法(包含完整代码)

    通过连通域分析算法能够找到最大的连通域,即图片的主体部分,然后保存该连通域的最小外接矩阵,即可去除掉无关的背景区域 更多图像预处理操作工具集包含在这个github仓库中

    2024年02月06日
    浏览(56)
  • 【C语言】程序环境和预处理|预处理详解|定义宏(下)

    主页:114514的代码大冒 qq:2188956112(欢迎小伙伴呀hi✿(。◕ᴗ◕。)✿ ) Gitee:庄嘉豪 (zhuang-jiahaoxxx) - Gitee.com 文章目录 目录 文章目录 前言 2.5带副作用的宏参数 2.6宏和函数的对比 3#undef ​编辑 4 命令行定义 5 条件编译 6 文件包含 总结 咱们书接上回 2.5带副作用的宏参数 先来

    2024年01月17日
    浏览(59)
  • 数据采集与预处理01: 项目1 数据采集与预处理准备

    数据采集:足够的数据量是企业大数据战略建设的基础,因此数据采集成为大数据分析的前站。数据采集是大数据价值挖掘中重要的一环,其后的分析挖掘都建立在数据采集的基础上。大数据技术的意义确实不在于掌握规模庞大的数据信息,而在于对这些数据进行智能处理,

    2024年01月25日
    浏览(65)
  • C语言——程序环境和预处理(再也不用担心会忘记预处理的知识)

    先简单了解一下程序环境,然后详细总结翻译环境里的编译和链接,然后在总结编译预处理。 在 ANSI C 的任何一种实现中,存在两个不同的环境 翻译环境:这个环境中源代码被转换为可执行的机器指令。 执行环境:执行二进制代码。 计算机如何执行二进制指令? 我们写的C语

    2024年02月09日
    浏览(57)
  • 数据预处理matlab matlab数据的获取、预处理、统计、可视化、降维

    1.1 从Excel中获取 使用readtable() 例1: 使用 spreadsheetImportOptions(Name,Value) 初步确定导入信息, 再用 opts.Name=Value 的格式添加。 例2: 先初始化 spreadsheetImportOptions 对象, 再用 opts.Name=Value 的格式逐个添加。 例3: 将导入信息存到变量里, 再使用 spreadsheetImportOptions(Name,Value)

    2024年02月15日
    浏览(56)
  • 图像预处理算法————灰度化处理

    图像预处理算法适合在FPGA上完成,原理简单且需要快速处理,通常有灰度化、中值、均值滤波等,以及颜色空间转换算法。 灰度图像是一种特殊的彩色图像(R=G=B的彩色图像) 只有一种颜色分量,单通道的0-255 方法:一般有分量法、最大值法、平均值法、加权平均法四种方

    2024年01月17日
    浏览(51)
  • 昇腾CANN DVPP硬件加速训练数据预处理,友好解决Host CPU预处理瓶

    本文分享自华为云社区《昇腾CANN 7.0 黑科技:DVPP硬件加速训练数据预处理,友好解决Host CPU预处理瓶颈》,作者: 昇腾CANN 。 随着人工智能的快速发展,越来越多的应用场景需要使用机器学习和深度学习模型。AI网络模型的训练一般分成两个关键部分,一个是训练数据预处理

    2024年02月05日
    浏览(52)
  • 大数据采集技术与预处理学习一:大数据概念、数据预处理、网络数据采集

    目录 大数据概念: 1.数据采集过程中会采集哪些类型的数据? 2.非结构化数据采集的特点是什么? 3.请阐述传统的数据采集与大数据采集的区别? ​​​​​​​ ​​​​​​​4.大数据采集的数据源有哪些?针对不同的数据源,我们可以采用哪些不同的方法和工具? 数据

    2024年01月25日
    浏览(54)
  • 编译预处理:#if

    #if expression … #elif … #end expression 是整数常量比较的表达式,例如: defined表达式,例如 defined AAA, 或者 defined(AAA), 如果AAA是一个宏定义,return true,否则,return false; 单个整数,例如:1/10/100/0, 非零为true,零为false; 整数比较,例如:1 == 1为true, 0 == 0为ture, 1 2为false; 单个

    2023年04月14日
    浏览(72)
  • 图像预处理方法

    两个基本的形态学操作是腐 和膨胀。他们 的变体构成了开运算 ,闭运算, 梯度等。 根据卷积核的大小前景的所有像素会腐 掉 变为 0 ,所以前景物体会变小整幅图像的白色区域会减少。 对于去除白噪声很有用 也可以用来断开两个 在一块的物体等。 函数原型: ⚫src: 输入原

    2023年04月11日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包