【【萌新的c语言学习-15】】

文章介绍了C语言中的预处理概念,包括宏定义的使用,如函数式宏与条件宏,以及函数与宏的效率对比。还讨论了条件编译的作用,如#ifdef和#if指令,用于控制代码的编译。最后,文章展示了如何利用宏模拟offsetof函数来计算结构体成员的偏移量。
摘要由CSDN通过智能技术生成

萌新的c语言学习15

C语言的预处理2

这大概是萌新c学习最后一篇了,后面更新c语言的小项目练习了,感恩 我的文章更多的像是笔记
实用价值其实有点低。后面更新解析全面,更加方便阅读的版本
宏在书写的时候会有一些带有副作用的参数影响

int main()
{
    int a = 10;
    int b = a + 1;
    int b = ++a;
    //相对来说第二个a它会自增

    return 0;
}

下面是举例

int main()
{
    int a = 10;
    int b = a + 1;
    int b = ++a;
    //相对来说第二个a它会自增

    return 0;
}

其实函数和宏的方法各有各的好处
//函数-1

int Max(int x, int y)
{
    return (x > y ? x : y);
}
//宏-2
#define MAX(x,y) (x)>(y)?(x):(y)
int main()
{
    int a = 10;
    int b = 2;
    //万一是浮点型的float呢
    //那么函数形式就不对了



    int max = Max(a, b);
    printf("max=%d\n", max);
    max = MAX(a, b);
    printf("max=%d\n", max);
    return 0;
}

函数比较慢但是直观吧, 宏定义就是直接替换 各有各的好处
函数在调用时,会有函数调用和返回的开销
因为define 在预处理阶段就完成了替换
宏的效率更高
宏用法与类型无关 常常用在执行简单的运算
宏的劣势
可能会大幅增加代码的长度
宏是无法调试的 函数我可以一步一步进去试探,宏只是简单的替换 押韵押韵
宏传递什么都可以
甚至宏可以传递一个定义
sizeof(arr)这种也可以传递
inline 内联函数
#undef
条件编译
在编译一个程序的时候,如果要将一条语句编译或者不参与编译 ,其实很方便
这就是条件编译 顾名思义 按照对应的条件来编译

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int i = 0;
    for (i = 0;i < 10;i++)
    {
        arr[i] = 0;
#ifdef DEBUG
        printf("%d ", arr[i]);
#endif
        //如果DEBUG定义过 这句话就参与编译
        //否则不参与编译

    }

    return 0;
}

定义后的版本

#define DEBUG
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int i = 0;
    for (i = 0;i < 10;i++)
    {
        arr[i] = 0;
#ifdef DEBUG
        printf("%d ", arr[i]);
#endif
        //如果DEBUG定义过 这句话就参与编译
        //否则不参与编译

    }

    return 0;
}

#if 还可以跟其他语句
#if 1 表示恒真参与编译
记住#if 和#endif 是一对的
还有else if 的表达式

int main()
{
#if 1==1
    printf("haha\n");
#elif 2==1
    printf("haha\n");
#else
    printf("heihei\n");
#endif
    return 0;
}

3.判断是否被定义
#ifdef symbol
就是如果定义了就参与编译
这个形式相当于

#if defined(symbol)

还有

#ifndef    (if no define)
#define DEBUG 1
int main()
{
#if defined(DEBUG)
    printf("hehe\n");
#endif
    return 0;
}

还有

int main()
{
#ifndef DEBUG
    printf("hehe\n");
#endif
    return 0;
}

4.嵌套指令

预处理 文件包含
我们在使用自己的定义的库函数要用#include
如果包含本地文件就用双引号
库函数用<>


因为我们每次使用都要去访问这些不管是库函数还是自己定义的函数 那么非常冗余 有重复包含的现象
所以我们在写头文件时候带上
#ifdef
#endif
这就像是单片机的.h文件一样 为什么写这个就是防止重复嵌套

一般来说就是这样子的形式

#ifdnef  __TEST_H__
#define __TEST_H__
int Add(int x , int y);
#endif

就是我们每次进去都会扫描一下 如果没有定义 那就去定义一下 ,如果下次读入判读一下有没有定义过 这样就可以防止一段代码被重复包含多次
上面这种写法是比较古老
也可以这么写

#pragma once //比较新颖

小题
请编写宏 计算结构体中某变量相对于首地址的偏移,并给出说明

#include<stddef.h>
struct s
{
    char c1;
    int a;
    char c2;
};
int main()
{
    //struct s s;
    printf("%d\n", offsetof(struct s, c1));
    printf("%d\n", offsetof(struct s, a));
    printf("%d\n", offsetof(struct s, c2));
    return 0;
}
// 0 4 8

//我们想要模拟实现offsetof的功能
我们假设在0地址放了一成员 那么随后的地址 就是他的偏移量

struct s
{
    char c1;
    int a;
    char c2;
};
#define OFFSETOF(struct_name , member_name)  &((struct_name*)0)->member_name
int main()
{
    //struct s s;
    printf("%d\n", OFFSETOF(struct s, c1));
    printf("%d\n", OFFSETOF(struct s, a));
    printf("%d\n", OFFSETOF(struct s, c2));
    return 0;
}

&((struct_name*)0)->member_name

就是说0是一个地址 强制类型转化成
struct s* 这是类型 (struct s*) 然后这个指针指向了c1 ((struct s*)0)->c1
这其实指的是c1的内容再取地址 不就是c1的地址 也是偏移量 再转化成整型

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值