50条C语言奇技淫巧,精品干货!_c 语言技巧大全,2024年最新Golang程序员必看

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注go)
img

正文

BUILD_BUG_ON((sizeof(struct mystruct) % 8) != 0);

除了这个,还有

#define BUILD_BUG_ON_ZERO(e) (sizeof(struct{int : -!!(e);}))
#define BUILD_BUG_ON_NULL(e) ((void*)sizeof(struct{int : -!!(e);}))
#define BUILD_BUG_ON(condition) ((void)BUILD_BUG_ON_ZERO(condition))
#define MAYBE_BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)]))

11. 用异或运算实现数据交换

交换俩变量数据,一般做法是:

// 方法1
temp = a;
a = b;
b = temp;

// 方法2
a=a+b;
b=a-b;
a=a-b;

方法1需要第三个变量,方法二存在数据溢出可能,可以尝试下以下方法:

a = a ^ b;
b = a ^ b;
a = a ^ b;

12. 判断语句中把const数值放在前面

通常条件语句写成

if(n == 0){ // }

但是,有可能手误写成

if(n = 0){ // }

这种错误只有机器在运行时候知道,而人不一定能发现这种bug。把数值放在前面就不怕了,==写成=,编译器就知道

if(0 == n){ // }

13. 用冒号表达式替代if...else...语句

这个用法应该很普遍了,不算什么特别的技巧了。

if(y < 0)
{
x = 10;
}
else
{
x = 20;
}

可以改成以下一行代码即可

x = (y < 0) ? 10 : 20;

14. 判断一个整数是否为2的幂

也许你会不断地将这个数除以2,除到底,然而Linux kernel有个巧妙的办法:

#define is_power_of_2(n) ((n) != 0 && ((n) & ((n) - 1)) == 0)

((n) & ((n) - 1)) == 0这个不理解?那先想想2的X次方的值的二进制是怎样的。

15. 静态链表

直接看代码

struct mylist {
int a;
struct mylist* next;
};
#define cons(x, y) (struct mylist[]){{x, y}}
struct mylist *list = cons(1, cons(2, cons(3, NULL)));
struct mylist *p = list;
while(p != 0) {
printf(“%d\n”, p->a);
p = p -> next;
};

16. 柔性数组

#include <stdlib.h>
#include <string.h>

struct line
{
int length;
char contents[0];
};
struct line *thisline = (struct line *) malloc (sizeof (struct line) + this_length);
thisline->length = this_length;

struct f1 { int x; int y[]; } f1 = { 1, { 2, 3, 4 } };
struct f2 { struct f1 f1; int data[3]; } f2 = { { 1 }, { 2, 3, 4 } };

详见6.18 Arrays of Length Zero

17. 数组之间直接赋值

int a[10] = {0,1,2,3,4,5,6,7,8,9};
int b[10] = {0};
b = a;

这样是非法的,但是你可以放数组穿个马甲:

typedef struct
{
int n[10];
}S;
S a = {{0,1,2,3,4,5,6,7,8,9}};
S b = {0};
b = a;

18. #include的不一定是要.h文件

#include后面跟的可以是任意后缀的,但文件内容一定要是合法的。例如

#include “test.fxxk”

19. 自动获取变量类型

#define var(left, right) typeof(right) left = (right)

var(s, 1LL); // 相当于 long long s = 1LL;

是不是有点像C++ 11的auto类型?

20. 宏定义函数MIN(x,y)的终极做法

#define MIN(x, y) x < y? x : y // 这样给0分

#define MIN(x, y) (x < y? x : y) // 这样给50分
// 不信你试试这个
int n = 3 * MIN(3, 4 < 5 ? 4 : 5);

#define MIN(x, y) ((x) < (y)? (x) : (y)) // 这个给90分
// 不信你试试这个
double xx = 1.0;
double yy = MIN(xx++, 1.5);
printf(“xx=%f, yy=%f\n”,xx,yy);

// 以下放大招了,看看GNU的
#define MIN(A,B) ({ typeof(A) __a = (A); typeof(B) __b = (B); __a < __b ? __a : __b; })
double xx = 1.0;
double yy = MIN(xx++, 1.5);
printf(“xx=%f, yy=%f\n”,xx,yy);

也许你知道用__LINE__可以输出行号,然而你试下这个:

#line 12345 “abcdefg.xxxxx”
printf(“%s line: %d\n”, FILE, LINE); printf(“%s line: %d\n”, FILE, LINE);

不单止行号被改了,文件名也被改了,是不是我们可以用这个干点啥……想想?

22. C和C++代码混合编译

在C的头文件上面

#ifdef __cplusplus
extern “C” {
#endif

然后再头文件下面

#ifdef __cplusplus
}
#endif

23. 用查表法实现hex2str

直接上代码

void hex2str(const unsigned char* hex, int size, char* str)
{
char char_arr[17] = “0123456789ABCDEF”;
for(int i = 0; i < size; i++)
{
str[3i] = char_arr[hex[i]>>4];
str[3
i+1] = char_arr[hex[i]&0x0F];
str[3*i+2] = ’ ';
}
}

24. 用sprintf实现hex2str

直接上代码

void hex2str(const unsigned char* hex, int size, char* str)
{
for(int i = 0; i < size; i++)
{
sprintf(&str[3*i], "%02X ", hex[i]);
}
}

25. 将变量名变字符串

如果想打印一个变量名和它的值,也许会这样:

unsigned int program_flag = 0xAABBCCDD;
printf(“program_flag: 0x%08X\n”, program_flag);

对于你有很多这样的变量要打印,建议你做个宏函数:

#define PRINT_HEX_VAR(var) printf(“%s: 0x%08X\n”, #var, var);
unsigned int program_flag = 0xAABBCCDD;
PRINT_HEX_VAR(program_flag);

26. 获取结构体元素的偏移

#define offsetof(type, member) ( (size_t)&((type*)0->menber) )

typedef struct
{
char a;
int b;
}S;
offsetof(S, b);

27. 根据结构体成员获取结构体变量指针

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE )0)->MEMBER)
/
*

  • container_of - cast a member of a structure out to the containing structure
  • @ptr: the pointer to the member.
  • @type: the type of the container struct this is embedded in.
  • @member: the name of the member within the struct.

*/
#define container_of(ptr, type, member) ({
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

这个怎么玩?看看链表:

struct list_head {
struct list_head *next;
struct list_head *prev;
};

struct ipstore{
unsigned long time;
__u32 addr[4];
struct list_head list;
};

container_of(ist1->list, struct ipstore, list)
scanf(“%[^,]”, a); // This doesn’t scrap the comma
scanf(“%[^,],”,a); // This one scraps the comma
scanf(“%[^\n]\n”, a); // It will read until you meet ‘\n’, then trashes the ‘\n’
scanf(“%*s %s”, last_name); // last_name is a variable

这是啥意思,正则表达式先了解下?然后自己试试,理解会更深入。

29. 两个数相加可以不用+号?

int Add(int x, int y)
{
if (y == 0)
return x;
else
return Add( x ^ y, (x & y) << 1);
}

30. 调试的时候打印数组

你是不是曾经为打印数组而烦恼,每次都要将元素一个个取出来?

#define ARR_SIZE(arr) (sizeof(arr)/sizeof(*arr))
#define PRINT_DIGIT_ARR(arr) do{
printf(“%s: “, #arr);
for(int i=0; i < ARR_SIZE(arr); i++)
printf(”%d “, arr[i]);
printf(”\n”);
}while(0)

int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
PRINT_DIGIT_ARR(arr);

31. 感受下这个0x5F3759DF

float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;

x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed

return y;
}

32. switch-case的特殊玩法

直接看代码:

void send(uint8* to, uint8 from, uint16 count)
{
uint16 n = (count + 7) / 8;
switch (count % 8)
{
case 0: do { *to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (–n > 0);
}

实际上它是:

void send(uint8* to, uint8 from, uint16 count)
{
do
{
*to = *from++;
} while (–count > 0);
}

使用最上面的switch-case的形式大大提高了运行效率。理解不了?汇编看看。还是理解不了?那就网上自行搜索“Duff’s Device”。

33. 防止头文件重复包含导致问题

这个用法很常见了,而且非常有用。

// xxx.h
#ifndef XXX_H
#define XXX_H

// Header file contents…

#endif

当然,如果你的编译器支持的话,也可以。

// xxx.h
#pragma once

// Header file contents…

不过,为了更好的兼容性,还是建议你用第一种方法。

34. 2的N次幂ROUNDUP

#define ROUNDUP(a, size) (((a) & ((size)-1)) ? (1+((a) | ((size)-1))) : (a))

其中,size是2的整数次幂,而:

  1. a & (2^n-1)检查a的低位是否有值;
  2. a | (2^n - 1) 将a的低n位赋值为1;
  3. 1 + a | (2^n -1) 为a最近的下一个2^n倍值。

ROUNDUP(10, 8); // 结果为16
ROUNDUP(10, 16); // 结果为16
ROUNDUP(10, 32); // 结果为32
ROUNDUP(16, 16); // 结果为16

有什么用?申请内存的时候可以按某字节对齐,减少内存碎片啊!

35. 某整数的ROUNDUP

#define VAL_ROUNDUP(size, val_size) (((size)+val_size-1)/val_size*val_size)

这个不是按2的次幂ROUNDUP的,而是按某个整数的倍数ROUNDUP,例如:

VAL_ROUNDUP(10, 8); // 结果为16
VAL_ROUNDUP(16, 8); // 结果为16
VAL_ROUNDUP(8, 10); // 结果为10
VAL_ROUNDUP(16, 16); // 结果为16

这个又有什么用?EEPROM或者Flash的page大小对齐的时候就非常有意义。

想想,memcpy函数为什么要用void*

void *memcpy(void *dest, const void *src, size_t n);

因为,它不关心你传什么类型的指针过来,我void统统都接纳。

无为而无不为。

再看看这个:

void *p1;
int *p2;

p1 = p2; // 这个没问题
p2 = p1; // 这个是错的

因为“空类型”可以包容“有类型”,而“有类型”则不能包容“空类型”。

所以,适可而止,不要滥用哦!

typedef struct
{
}StructNull;

sizeof(void);
sizeof(StructNull);

正所谓:

空即是色,色即是空。

在C语言上,sizeof(void); 的值为1;而sizeof(StructNull);为0。C++的情况请自行验证,别瞎猜,哈哈哈。

38. 布尔变量的判断

正确的做法:

if(bValue)

以下是瞎搞:

if(bValue == TRUE)

为啥?布尔类型中只有两个值:假和真。请问:假是什么,真又是什么?

假是0,而真是非0。那么非0是什么?-1,1,2,……除了0的一切。

所以再想想以下代码中的两个叹号是否可以去掉?

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

我们知道##是用来连接字符的,看看RTX怎么用,感受一下:

/// Create a Thread Definition with function, priority, and stack requirements.
/// \param name name of the thread function.
/// \param priority initial priority of the thread function.
/// \param instances number of possible thread instances.
/// \param stacksz stack size (in bytes) requirements for the thread function.
/// macro body is implementation specific in every CMSIS-RTOS.
// define the object
#define osThreadDef(name, priority, instances, stacksz)
const osThreadDef_t os_thread_def_##name =
{ (name), (priority), (instances), (stacksz) }

/// Access a Thread definition.
/// \param name name of the thread definition object.
/// macro body is implementation specific in every CMSIS-RTOS.
#define osThread(name)
&os_thread_def_##name

代码比较简单,我就不解释了,自行思考下。

40. 传值和传址

看两个例子:

void swap(int a; int b)
{
int t = 0;
t = a;
a = b;
b = t;
}

int x = 100;
int y = 200;
swap(x, y);

请问,这个swap可以交换x, y的值吗?

再看看一个常见的面试题:

void GetMemory(char *p)
{
p = (char *)malloc(100);
}

void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, “hello world”);
printf(str);
}

这个程序能输出“hello world”吗?

此处没有答案,为了加深理解,建议感兴趣的朋友请自行动手验证和思考。

41. 形参到底传值好还是传址好

接着上一条,我们从另一个角度看。函数定义一个结构体类型形参,传值好还是传址好?

void func1(struct tStructType param)
{
}
void func2(struct tStructType* param)
{
}

实际上两种方式都行,但你要明白形参实际上就是一个临时变量,不管传值还是传址都有一个复制给临时变量的过程。这个仿真汇编看看就知道了。很明显,如果tStructType这个类型占用空间很大,那么肯定用tStructType*比较合算。

42. bit翻转的几个方法

bit翻转是从MSB->LSB到LSB->MSB, 所有的Bit都必须反转。例如:

1010 0001 => 1000 0101

a. 运算实现32位bit翻转

unsigned int
reverse(register unsigned int x)
{
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
truct tStructType* param)
{
}

实际上两种方式都行,但你要明白形参实际上就是一个临时变量,不管传值还是传址都有一个复制给临时变量的过程。这个仿真汇编看看就知道了。很明显,如果tStructType这个类型占用空间很大,那么肯定用tStructType*比较合算。

42. bit翻转的几个方法

bit翻转是从MSB->LSB到LSB->MSB, 所有的Bit都必须反转。例如:

1010 0001 => 1000 0101

a. 运算实现32位bit翻转

unsigned int
reverse(register unsigned int x)
{
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-7QbcAgPo-1713112809103)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值