【C语言】那些优秀代码里的骚操作(持续更新…)

news/2025/1/16 1:01:46/

【C语言】那些优秀代码里的骚操作(持续更新…)

    • 1、联合体`union`的妙用
    • 2、`#include`的本质是什么?
    • 3、脱裤子放屁的`do{ }while(0)`
    • 4、一个成熟的代码要学会自己写函数
    • 5、……

语言这个东西,其实没有奇技淫巧,凡是可以写出来的,能被编译器识别的,都是常规操作,这里所谓的骚操作,其实是想说那些看起来不合理,但是自有道理的写法。这些写法大都是对C语言本质理解不透彻导致的惊奇。然而,我们应该保持惊奇, 纪德说:“智者,乃是对一切都发生惊奇的人。”
很多奇怪的写法,大多是我看一些SDK、库、框架时见到过的,这些操作也许你见到过,也许会说:哦,就这?那么恭喜你,这个逼我不装了,让你来。
这篇文章,想起来就就会来更新一下……

1、联合体union的妙用

假设有以下字节流数据date(共6Byte长度):

字节1Byte1Byte1Byte1Byte1Byte1Byte
含义

如果我们要分别解析出每一字节,我们一般会采用移位操作,但代码复杂

如果我们这样写:

typedef struct  time_s {char year;char mouth;char day;char hour;char min;char sec;
}time_t;union time {time_t t;char date[6];
};void get_time() {union time time_u;//这个例子有点像银行存钱时 整存零取 的模式memcpy(&time_u.date, "12345",6);//整存printf("the hour is:%c", time_u.t.hour);//零取
}

则将输出

the hour is:4

这就充分利用了联合体共享内存的特性。

2、#include的本质是什么?

看到这个题目,有人会说:当然是包含头文件
不,这只是一个现象,不是本质,它的本质是展开所包含文件的代码。

这是有一天看LWIP源码恍然大悟的:

typedef enum
{
#include LWIP_MEMPOOL(name,num,size,desc)	MEMP_##name,
#include "lwip/priv/memp_std.h"MEMP_MAX
} memp_t;

这段代码经过编译器处理后是这样的:

typedef enum
{	MEMP_RAW_PCB,MEMP_UDP_PCB,MEMP_TCP_PCB,MEMP_MAX
}

memp_std.h中放了什么呢?

LWIP_MEMPOOL(RAW, MEMP_NUM_RAW_PCB ,sizeof(struct raw_pcb),  "raw_pcb")
LWIP_MEMPOOL(UDP, MEMP_NUM_UDP_PCB ,sizeof(struct udp_pcb),  "udp_pcb")
LWIP_MEMPOOL(TCP, MEMP_NUM_TCP_PCB ,sizeof(struct tcp_pcb),  "tcp_pcb")

所以,头文件里放的东西不一定是一些变量、函数的声明,只要把它展开,放在被引用处的上下文能编译通过,那就可以随便你写什么!那么我们倒过来想:是不是我们也可以把原来.c中的一段代码截取出来放到.h中,然后在此处用#inlcude ".h"取而代之?当然可以!那么这么做有什么好处呢?比如说,我们想把.c的一段代码单独拎出来提供给其他层使用,当然了,大部分时候,这些内容是那些我们常见的函数和变量,但是记住:那些.h中未声明的东西我们依然可以在.h中使用,因为它们可能在.c中展开之后就语义通顺了。

另外注意,#include不是.h的专属,也可以是txt。

3、脱裤子放屁的do{ }while(0)

上一次看到do{ }while(0)还是在上一次,它的一个主要作用就是限制作用域,这也是C语言中{}的作用。有些人喜欢拿宏展开举例子,来证明do{ }while(0)可以限制代码的作用域,避免宏展开后与上下文结合产生新的语义,但这{}也可以做到。

①先来说说这个限制作用域的问题:

void print()
{cout<<"print: "<<endl;
}
void send()
{cout <<"send: "<<endl;
}#define LOG print();send();int main(){if (false)LOGsystem("pause");return 0;
}

这种情况这样写:#define LOG { print();send(); },也可以解决啊,do{ }while(0)纯粹是脱裤子放屁

但是如果这样调用宏:

if (false)LOG;
else{
}

使用{}来限制作用域,展开后则为:

if (false)
{print();send();
};
else{
}

便会报错。
使用do{ }while(0)来限制作用域,展开后则为:

if (false)
do{print();send();
}while(0);
else{
}

这才是do{ }while(0)的优势。

②还有种说法,do{ }while(0)还可以和break搭配使用,以代替goto语句,优点就是break即使漏写,也会跳出执行后面的语句;goto END;漏写便执行不到END:后的语句,我认为是无稽之谈,这也漏写,那也漏写,别编程了。这和①情况完全不同,①中的宏可能是由其他层提供的,层间存在不确定性,goto不存在这个问题。可以这样写,但别说什么代替goto,goto没惹你,告诉你特性了,你自己不注意是你的问题。这仅代表我此时码字的心情,明天我可能就不这么说了,不喜勿喷。

4、一个成熟的代码要学会自己写函数

这是一段来自某厂商SDK的shell交互代码,用宏来生成函数,有C基础的人一看便知:

#define CLI_CMD(name, help, func) \int _cli_inter_##func (char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString) \{ \int argc = 0; \char *argv[CLI_MAX_ARGS]; \argc = CLITokenizeCommand(pcCommandString, argv); \return func(argc, argv); \} \__attribute__((__used__)) const CLI_Command_Definition_t _cli_cmd_##func __SECTION(".commands") = \{ name, help, _cli_inter_##func }

但不要忘了使用__attribute__((__used__)),向编译器说明这段代码有用,即使在没有用到的情况下编译器也不会警告!

5、……


http://www.ppmy.cn/news/334599.html

相关文章

算法17: 删除排序链表中的重复元素(升序链表去重)

一、需求 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,1,2] 输出&#xff1a;[1,2] 示例 2&#xff1a; 输入&#xff1a;head [1,1,2,3,3] 输出…

Vue中如何进行数据筛选与搜索功能实现

Vue中如何进行数据筛选与搜索功能实现 在Vue应用中&#xff0c;数据筛选和搜索是常见的需求。本文将介绍如何在Vue中进行数据筛选和搜索功能的实现&#xff0c;包括基于原生JavaScript的筛选和搜索、基于Lodash库的筛选和搜索、以及基于Vue插件的筛选和搜索。 基于原生JavaScr…

Vue中如何进行图表绘制

Vue中如何进行图表绘制 数据可视化是Web应用中非常重要的一部分&#xff0c;其中图表绘制是其中的重要环节。Vue作为一款流行的前端框架&#xff0c;提供了很多优秀的图表库&#xff0c;以满足不同业务场景下的需求。本文将介绍如何在Vue中进行图表绘制&#xff0c;包括使用Vu…

【Deno】denon deno热重载框架

官网 https://deno.land/x/denon2.5.0 简介 denon是 deno 中 nodemon 的替代品。denon会监视文件的改变&#xff0c;并自动重新启动程序&#xff0c;重新编译更改的源代码。 安装 ⚠️ Make sure you are using deno version ^1.6.0 to install this executable. You can u…

离散数学编程作业:输出n元集合的所有划分或幂集元素

编程题目&#xff08;二选一&#xff09;&#xff1a; 打印输出n元&#xff08;n1,2,3,4,5,6&#xff09;集合的所有划分。打印输出n元&#xff08;n1,2,3,4,5,6&#xff09;集合的幂集中的所有元素。 编程内容及要求&#xff08;二选一&#xff09;&#xff1a; 编写程序&a…

【0基础自研记录】ESP32-CAM自制个人网络监控

目的&#xff1a;实现一个小型家庭监控 一、前期准备 1.硬件准备 esp32-acm烧录板烧录线 2.软件准备 Arduion IDE CH340串口驱动 下载地址如下 Arduion IDE:https://www.arduino.cc/en/software CH340串口驱动 链接&#xff1a;https://pan.baidu.com/s/1ri8dK7wW6KFz8rOPs…

win10笔记本电脑键盘没反应是哪个键锁了

原因一、部分字母打出来是数字&#xff0c;导致打不出 1 FnNUMLOCK切换法 我们先按住【Fn键】&#xff08;Fn键一般在键盘的左下角&#xff09;&#xff0c;再按【Num Lk】&#xff08;Num Lk一般在右上角&#xff0c;F11键的上面&#xff0c;当然不同的笔记本所在位置有所不同…

联想拯救者y7000键盘有几个按键失灵_y7000p键盘失灵

以联想拯救者y7000p为例&#xff0c;键盘失灵是系统有问题&#xff0c;联想笔记本都带着一键还原功能&#xff0c;只要没有重新安装过系统。一键还原按钮在笔记本左侧或者右侧&#xff0c;是一个很细的孔&#xff0c;进行还原系统即可。 键盘(Keyboard)是用于操作设备运行的一种…