C基础 那些年用过的奇巧淫技
引言 - 为寻一颗明星
为要寻一颗明星 徐志摩 1924年12月1日《晨报六周年纪念增刊》我骑著一匹拐腿的瞎马,向著黑夜里加鞭;——向著黑夜里加鞭,我跨著一匹拐腿的瞎马。// 我冲入这黑绵绵的昏夜,为要寻一颗明星;——为要寻一颗明星,我冲入这黑茫茫的荒野。// 累坏了,累坏了我胯下的牲口,那明星还不出现;——那明星还不出现,累坏了,累坏了马鞍上的身手。// 这回天上透出了水晶似的光明,荒野里倒著一只牲口,黑夜里躺著一具尸首。——这回天上透出了水晶似的光明!//
{ 风 : http://music.163.com/#/song?id=5276735 }
前言 - 有点扯
C基本是程序生涯的入门语言. 虽说简单, 但已经断层了. 估计是不合时宜吧.
工作中也就在网络层框架会看见部分C的影子. 自己用C开发久了, 发现C一个弊端是 当一个项目超过 2千行 x 10 时候用C协作
非常难受. C风格是个自由的英雄主义表现.
但是 真实的生活如dota, 我们不是 hero 而只是 那个小兵, 时来运转会成为超级兵. 哈哈.
但这不重要, 喜欢就好.
生活不止眼前的苟且 ... ...
好那我们开始,看看那些关于C基础的活化石. 真想问 <<C程序设计>> 这门课你真的学好了吗?
正文 - 有点难
1. int i = 0; ++i 一直继续会怎样?
我们先看这样的测试代码
#include <stdio.h> #include <stdlib.h>/** 测试 int 的最大值*/ int main(void) {int id = 0x7fffffff;printf("-1 = %x\n", -1);printf("id = %d\n", id);++id;printf("id = %d\n", id);id += 0x7fffffff;printf("id = %d\n", id);id += 0x7fffffff;printf("id = %d\n", id);system("pause");return 0; }
你能算明白测试结果吗, 如果可以说明你计算机组成原理学的很好. 运行截图如下
因而 我们得到 int i = 0; ++i 一直继续的 会是 0->INT_MAX->INT_MIN->0 这样循环的. 例如 skynet 存在这个使用错误
int id = __sync_add_and_fetch(&(ss->alloc_id), 1);if (id < 0) {id = __sync_and_and_fetch(&(ss->alloc_id), 0x7fffffff);}
原作者希望 再从 0开始 , 但却忘了
#define INT_MIN (-2147483647 - 1) // minimum (signed) int value #define INT_MAX 2147483647 // maximum (signed) int value
对于 signed MAX + MIN = -1 , 因为计算机中 正数从0开始, 负数从-1开始.
2. 添加双引号 的宏用法
看下面代码
// 添加双引号的宏 #ifdef _API_MEM # define STRINIFY_(S) #S # define STRINIFY(S) STRINIFY_(S) # include STRINIFY(_API_MEM) # undef STRINIFY # undef STRINIFY_ #endif
有些工程中使用上面代码, 来动态的导入头文件. 核心在于 STRINIFY_ 和 STRINIFY 两个宏使用. 我们测试一下
#include <stdio.h> #include <stdlib.h># define STRINIFY_(S) #S # define STRINIFY(S) STRINIFY_(S)#define _API_MEM api.h// 测试添加双引号宏 int main(void) {puts(STRINIFY_(_API_MEM));puts(STRINIFY(_API_MEM));system("pause");return 0; }
运行结果是
通过这个发现, 如果直接用 STRINIFY_ 不会将参数展开了. 这也是一个C行业淫荡的技巧了. 但是觉得大巧若拙
个人觉得 最好做法是
#define _API_MEM "api.h" #ifdef _API_MEM # include _API_MEM #endif
3. 除了sizeof, 其实还有 offsetof
直接看例子
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stddef.h> #include <stdbool.h>#define UDP_ADDRESS_SIZE 19 // ipv6 128bit + port 16 bit + 1 byte typestruct write_buffer {struct write_buffer* next;void* buffer;char* ptr;int sz;bool userobject;uint8_t udp_address[UDP_ADDRESS_SIZE]; };/** 测试 宏 offsetof*/ int main(int argc, char* argv[]) {printf("offsetof(struct write_buffer, udp_address[0]) = %d\n", offsetof(struct write_buffer, udp_address[0]));printf("offsetof(struct write_buffer, udp_address) = %d\n", offsetof(struct write_buffer, udp_address));system("pause");return 0; }
运行的结果如下
通过上面 可以知道 offsetof 其实计算的是结构体中字段的偏移量. 关于结构体的内存计算基础能力, 必须要掌握的. 洞悉内存结构很重要.
其实 offsetof 是 stddef.h 中定义的一个 宏 如下
#define offsetof(s,m) ((size_t)&(((s*)0)->m))
是不是很清爽. 就是这样, 没事简单的.
其实上面代码还隐含一个 关于 数组的 细节 . int a[10]; &a[0] == a == &a 地址是相同的.
4. 如何构造一个只能在堆上分配结构体?
//堆上 声明结构体 struct request_open {int id;int port;uintptr_t opaque;char host[]; };
就是上面那样, 加了[], 表示不完全类型. 只能在堆上分配内存. 使用方法.
struct request_open *open = malloc(sizeof(struct request_open) + sizeof(char) * 19);
这种结构一般在底层库会看见. 一些老的程序员喜欢这么写
//堆上 声明结构体 struct request_open {int id;int port;uintptr_t opaque;char host[0]; };
或
//堆上 声明结构体 struct request_open {int id;int port;uintptr_t opaque;char host[1]; };
因为老的编译器不支持 char host[]; 后面标准加了. 后来没改过习惯.
5. 如何构造一个在栈上初始化的指针变量
说的不好明白, 或者这么问, 下面定义的类型怎么解.
struct cstring_data {char* cstr; //保存字符串的内容uint32_t hash; //字符串hash,如果是栈上的保存大小uint16_t type; //主要看 _INT_STRING_* 宏,默认0表示临时串uint16_t ref; //引用的个数, 在 type == 0时候才有用 };typedef struct _cstring_buffer {struct cstring_data* str; } cstring_buffer[1]; //这个cstring_buffer是一个在栈上分配的的指针类型
上面也是底层库中会遇到一个技巧.
当声明cstring_buffer cb; 后.可以直接cb->str调用它,
当 cb 传入到 函数中. 仍然可以 cb->str. 可以理解为这个值是栈上的但是可以当指针变量用法去使用. 看下面也许好理解
typedef struct _jmp_buf { int _jb[_JBLEN + 1]; } jmp_buf[1];
这个是 setjmp.h 里的一行定义,把一个 struct 定义成一个数组。
这样,在声明 jmp_buf 的时候,可以把数据分配到堆栈上。但是作为参数传递的时候则作为一个指针.
扩展一下阅读理解可以看下面. 应该可以知道为什么这么搞.
//特殊的数组 声明结构体 #define _INT_STRING_ONSTACK (4) //标识 字符串分配在栈上//0 潜在 标识,这个字符串可以被回收,游离态#define _INT_ONSTACK (128) //栈上内存大小struct cstring_data {char* cstr; //保存字符串的内容uint32_t hash; //字符串hash,如果是栈上的保存大小uint16_t type; //主要看 _INT_STRING_* 宏,默认0表示临时串uint16_t ref; //引用的个数, 在 type == 0时候才有用 };typedef struct _cstring_buffer {struct cstring_data* str; } cstring_buffer[1]; //这个cstring_buffer是一个在栈上分配的的指针类型/** v : 是一个变量名** 构建一个 分配在栈上的字符串.* 对于 cstring_buffer 临时串,都需要用这个 宏声明创建声明,* 之后可以用 CSTRING_CLOSE 关闭和销毁这个变量,防止这个变量变成临时串*/ #define CSTRING_BUFFER(v) \char v##_cstring[_INT_ONSTACK] = { '\0' }; \struct cstring_data v##_cstring_data = { v##_cstring, 0, _INT_STRING_ONSTACK, 0 }; \cstring_buffer v; \v->str = &v##_cstring_data;
6. 那些年总有个align字段进行内存对齐
/*字节对齐的类型Align,为了优化CPU读取*/ typedef union {long l_dummy;double d_dummy;void *p_dummy; } Align;/*标志大小,默认是4字节*/ #define MARK_SIZE (4) /*内存块头结点,双向链表结点size,filename,line都是为了调试添加的调试信息.prev和next是双向链表的核心*/ typedef struct {int size;char *filename;int line;Header *prev;Header *next;unsigned char mark[MARK_SIZE]; } HeaderStruct;/*Align类型的字节大小*/ #define ALIGN_SIZE (sizeof(Align)) /*这是个不错的技巧,求最小的n使得n*ALIGN_SIZE>=val成立,n,val,ALIGN_SIZE都属于自然数*/ #define revalue_up_align(val) ((val) ? (((val) - 1) / ALIGN_SIZE + 1) : 0) /*将HeaderStruct按照Align划分,找到最小的n,使得n*ALIGN_SIZE>=sizeof(HeaderStruct),在自然数集中*/ #define HEADER_ALIGN_SIZE (revalue_up_align(sizeof(HeaderStruct)))/*实现了memory.h接口中Header不完全类型,Align是对齐用的,内存结构的头结点.链表链接的主要结点*/ union Header_tag {HeaderStruct s;Align u[HEADER_ALIGN_SIZE]; };
主要看 union Headr_tag 中 Align结构. 保证不同机器上内存是对齐的. 比较古老了. 特别底层的库会见到.
7. 可变参数宏, 那些事
同样直接看下面工程中用的示例
//4.0 控制台打印错误信息, fmt必须是双引号括起来的宏 #ifndef CERR #define CERR(fmt, ...) \fprintf(stderr,"[%s:%s:%d][error %d:%s]" fmt "\r\n",\__FILE__, __func__, __LINE__, errno, strerror(errno),##__VA_ARGS__) #endif/* !CERR *///4.1 控制台打印错误信息并退出, t同样fmt必须是 ""括起来的字符串常量 #ifndef CERR_EXIT #define CERR_EXIT(fmt,...) \CERR(fmt,##__VA_ARGS__),exit(EXIT_FAILURE) #endif/* !ERR */#ifndef IF_CERR /**4.2 if 的 代码检测** 举例:* IF_CERR(fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), "socket create error!");* 遇到问题打印日志直接退出,可以认为是一种简单模板* code : 要检测的代码 * fmt : 必须是""括起来的字符串宏* ... : 后面的参数,参照printf*/ #define IF_CERR(code, fmt, ...) \if((code) < 0) \CERR_EXIT(fmt, ##__VA_ARGS__) #endif //!IF_CERR#ifndef IF_CHECK /** 是上面IF_CERR 的简化版很好用*/ #define IF_CHECK(code) \if((code) < 0) \CERR_EXIT(#code) #endif // !IF_CHECK
那 传说中的 3颗痣, 就是可变参数宏的一切o(∩_∩)o
8. 简单的谢幕. 还是宏
一个数如何和0比较,真的是 == 吗. 其实好的思路是定义阀值.
//3.0 浮点数据判断宏帮助, __开头表示不希望你使用的宏 #define __DIFF(x, y) ((x)-(y)) //两个表达式做差宏 #define __IF_X(x, z) ((x)<z&&(x)>-z) //判断宏,z必须是宏常量 #define EQ(x, y, c) EQ_ZERO(__DIFF(x,y), c) //判断x和y是否在误差范围内相等//3.1 float判断定义的宏 #define _FLOAT_ZERO (0.000001f) //float 0的误差判断值 #define EQ_FLOAT_ZERO(x) __IF_X(x,_FLOAT_ZERO) //float 判断x是否为零是返回true #define EQ_FLOAT(x, y) EQ(x, y, _FLOAT_ZERO) //判断表达式x与y是否相等
谢幕吧 : [
老师布置一个作业, 问学生, 看见那个晾衣杆吗. 谁能帮我测试出高度来.
一个同学自告奋勇的把晾衣杆放倒了. 测试出长度 为 1.5m.
老师把他骂了一顿, 我要的是高度, 不是长度.
]
//5.0 获取数组长度,只能是数组类型或""字符串常量,后者包含'\0' #ifndef LEN #define LEN(arr) \(sizeof(arr)/sizeof(*(arr))) #endif/* !ARRLEN */
后记 - 认真做容易的
错误是难免的, 欢迎吐槽交流. ... 还有下半辈子的苟且. 哈哈, 但是在变化, 那会变得有意思, O(∩_∩)O哈哈~
转载于:https://www.cnblogs.com/life2refuel/p/5422778.html
C基础 那些年用过的奇巧淫技相关推荐
- 蓝桥杯算法竞赛系列第一章——位运算的奇巧淫技及其实战
遇见蓝桥遇见你,不负代码不负卿! 第二章"递归"已将更新咯,欢迎铁汁们点评!蓝桥杯算法竞赛系列第二章--深入理解重难点之递归(上)_安然无虞的博客-CSDN博客 目录 一.位运算符 ...
- 算法很美第一章 位运算的奇巧淫技
第一章 位运算的奇巧淫技 1.2 题解:如何找数组中唯一成对的那个数 知识点: A^A=0 A^0=A(一个数与0进行按位异或,结果是他自己) 对一个序列进行按位异或,可以消除掉序列中的重复项,比如: ...
- iOS 开发的一些奇巧淫技
iOS开发的一些奇巧淫技1&2 CGfloat和float的区别? 现在上架的app都要求支持64位系统,那么CGFloat和float的区别就在这里.command+左键点击CGFloat. ...
- 【奇巧淫技】python 助你每天早上八点自动发送天气预报邮件到QQ邮箱
此博客仅为我业余记录文章所用,发布到此,仅供网友阅读参考,如有侵权,请通知我,我会删掉. 补充 有不少杠精小婊贝留言说本文章没有用,因为天气预报直接打开手机就可以收到了,为何要多此一举发送到邮箱呢!! ...
- 位运算概览与奇巧淫技
文章目录 一.位运算概述 二.位运算概览 异或 二进制数的原码.反码.补码 左移 << 右移 >> 无符号右移 >>> 三.位运算的使用奇巧淫技 判断奇偶数 ...
- Django REST framework的一些奇巧淫技(干货!!!)
开始之前,假设你已经有Django和Django REST framework的一些基础了 mixins,ViewSet和routers配合使用 minxis的类有5种 CreateModelMixi ...
- iOS开发的一些奇巧淫技2
能不能只用一个pan手势来代替UISwipegesture的各个方向? 1 - (void)pan:(UIPanGestureRecognizer *)sender 2 { 3 4 typedef N ...
- iOS 【奇巧淫技】获取webView内容高度
针对获取webView高度问题之前写过一个方案--通过监听WebView的scrollView的变化来实时更新高度 附上链接: iOS[终极方案]精准获取webView内容高度,自适应高度 本文是给出 ...
- jane street market prediction 冠军方案 奇巧淫技与topline链接整理(3/3)
目录 前言 将income 作为loss(优化目标) 读取数据,定义优化目标 模型训练与微调 提交结果 多目标学习 Topline整理 AE+MLP (rank10) Current 17th sol ...
最新文章
- AI聚变:寻找2018最佳人工智能应用案例
- 驰骋工作流引擎-嵌入式表单的介绍
- php数字小数大小比较,PHP 常用的数学函数和数值处理函数
- python数据类型转换方法列表
- python 读取文件
- 为什么使用mq?具体的使用场景是什么?
- C#3.0 自动属性——只能在简单属性上偷懒
- 报错Cannot determine embedded database driver class for database type NONE解决方法
- xcode里面找不到头文件
- Linux命令大全(常用的命令解析)
- PDFObject的使用(转)
- Windows/Linux/Mac OS下IntelliJ IDEA快捷键中文大全(本人翻译自官方ReferenceCard.pdf)(PDF典藏版)
- 第21课: JSP语句 if判断语句 (JSP教程 JSP入门实战教程 黄菊华Java网站开发系列教程)
- 电路中容易混淆的三角形符号
- 老式十字锁自动碰锁,换锁芯
- 第三方登录数据库用户表结构设计
- SOLIDWORKS中钣金展开标注是英文怎么办?
- 一篇文教你使用python Turtle库画出“精美碎花小清新风格树”快来拿代码!
- 怎么改变ADS1.2的字体大小
- 如何查询MySQL表中的数据,这些操作你得明白!
热门文章
- 苹果iPhone 7存在六大短板 看完你还想买吗
- 计算机二进制转化教案及ppt,二进制和十进制之间的转换.ppt
- R:ggplot2分类别绘图的三种方式以及分组绘图的二种方式
- 基于入侵杂草算法的函数寻优算法
- NewStarCTF week5 web wp
- java junit 语句,java – Junit测试日志语句
- Stata Plus:连老师的 Stata 外部命令集
- AR宇航员互动体验软件:虚拟与现实叠加增强体验感
- React Native Android 应用层实战沦陷记
- 全志平台Android4.0 SOFTAP STATION共存调试记录2