linux c奇淫技巧v1

14 篇文章 2 订阅

数组相关

零长数组:

struct b

{
    int a;
    int b[0]; //不占空间
}
/*
1、动态分配可变长度的数组
2、方便释放,原地及数据
3、提高访问速度
*/

为什么不用指针代替零长数组?

struct buffer1{
    int len;
    int a[0];
};
struct buffer2{
    int len;
    int *a;
};
int main(void)
{
    printf("buffer1: %d\n", sizeof(struct buffer1));
    printf("buffer2: %d\n", sizeof(struct buffer2));
    return 0;
}

结果为:

buffer1:4

buffer2:8

1、需要单独为这个指针分配空间,这个指针指向的是另外一片空间

2、造成结构体冗余,使用不方便

 

数组指定初始化:

int a[100] = {[1]=1, [50] = 1}; //a[1]=1,a[50] =1

int a[100] = {[10 ... 30] = 1, [60 ... 70] = 1};//范围初始化

同样的case语句中也可以使用:

case 1:

         break;

case 2 ... 5:

         break;

指定初始化结构体成员变量:

struct student{
    char name[20];
    int age;
};

int main(void)
{
    struct student stu1={ "wit",20 };
    printf("%s:%d\n",stu1.name,stu1.age);

    struct student stu2=
    {
        .name = "wanglitao",
        .age  = 28
    };
    printf("%s:%d\n",stu2.name,stu2.age);

    return 0;
}

语句表达式:

({ 表达式1; 表达式2; 表达式3; })

语句表达式的值为内嵌语句中最后一个表达式的值,即表达式3的值

int main(void)
{
    int sum = 0;
    sum = 
    ({
        int s = 0;
        for( int i = 0; i < 10; i++)
            s = s + i;
            s;
    });
    printf("sum = %d\n",sum);
    return 0;
}

/*
语句表达式的值等于最后一个表达式的值,所以在 for 循环的后面,我们要添加一个 s
*/

我们一般多在宏定义中使用语句表达式

举个例子:定义一个宏,求两个数的最大值 

很常见的一个宏,那么究竟有多少门道??

 

不及格:
#define  MAX(x,y)  x > y ? x : y  //运算符优先级导致结果不符合,例如max(1+1,2)

 

及格:
#define MAX(x,y) (x) > (y) ? (x) : (y)

/*
解决了上面的问题,还是存在漏洞,例如:
#define MAX(x,y) (x) > (y) ? (x) : (y)
int main(void)
{
    printf("max=%d",3 + MAX(1,2));
    return 0;
}
展开为:
3 + (1) > (2) ? (1) : (2);  还是运算符优先级问题,错误
*/

 

合格:现在一般的写法也是这样,但是还是有问题
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void)
{
    int i = 2;
    int j = 6;
    printf("max=%d",MAX(i++,j++));
    return 0;
}

/*
在程序中,我们定义两个变量 i 和 j,然后比较两个变量的大小,并作自增运算。
实际运行结果发现 max = 7,而不是预期结果 max = 6。这是因为变量 i 和 j 在宏展开后,
做了两次自增运算,导致打印出 i 的值为7
*/

 

良好:使用了语句表达式
#define MAX(x,y)({     \
    int _x = x;        \
    int _y = y;        \
    _x > _y ? _x : _y; \
})

int main(void)
{
    int i = 2;
    int j = 6;
    printf("max=%d",MAX(i++,j++));
    return 0;
}
/*
上面的宏定义的是int,还需要优化
*/

 

优秀:能这样写已经很棒了,但是还能不能继续优化?
#define MAX(type, x, y)({     \
    type _x = x;        \
    type _y = y;        \
    _x > _y ? _x : _y; \
})

int main(void)
{
    int i = 2;
    int j = 6;
    printf("max=%d\n",MAX(int,i++,j++));
    printf("max=%f\n",MAX(float,3.14,3.15));
    return 0;
}

完美:

完美:使用了typeof,避免多传递参数
#define MAX(x,y)({     \
    typeof(x) _x = x;        \
    typeof(x) _y = y;        \
    _x > _y ? _x : _y; \
})

int main(void)
{
    int i = 2;
    int j = 6;
    printf("max: %d\n", MAX(i, j));
    printf("max: %f\n", MAX(3.14, 3.15));
    return 0;
}

 

那么在linux内核中利用typeof特性还有一个写法?

#define max(x, y) ({                \
    typeof(x) _max1 = (x);          \
    typeof(y) _max2 = (y);          \
    (void) (&_max1 == &_max2);      \
    _max1 > _max2 ? _max1 : _max2; })


/*

(void) (&_max1 == &_max2);
这一句很有意思:看起来是一句废话,其实用得很巧妙!它主要是用来检测宏的两个参数 x 和 y 的数据类型
是否相同。如果不相同,编译器会给一个警告信息,提醒程序开发人员:
warning:comparison of distinct pointer types lacks a cast

让我们分析一下,它是怎么实现的:语句 &_max1 == &_max2 用来判断两个变量 _max1 和 _max2 的地址是
否相等,即比较两个指针是否相等。&_max1 和 &_max2分别表示两个不同变量的地址,怎么可能相等呢!既然
大家都知道,内存中两个不同的变量地址肯定不相等,那为什么还要在此多此一举呢?妙就妙在,当两个变量
类型不相同时,对应的地址,即指针类型也不相同。比如一个 int 型变量,一个 char 变量,对应的指针类型,
分别为 char * 和 int *,而两个指针比较,它们必须是同种类型的指针,否则编译器会有警告信息。所以,
通过这种“曲线救国”的方式,这行程序语句就实现了这样一个功能:当宏的两个参数类型不相同时,编译器会及
时给我们一个警告信息,提醒开发者。
*/

强符号和弱符号

GNU C 通过 __attribute__ 声明weak属性,可以将一个强符号转换为弱符号

void __attribute__((weak)) func(void);

int num __attribte__((weak);

  • 强符号:函数名、初始化的全局变量名;
  • 弱符号:未初始化的全局变量名。

强符号和弱符号在解决程序编译链接过程中,出现的多个同名变量、函数的冲突问题非常有用。一般我们遵循下面三个规则。

 

一般用途:

多个库联调的时候有时候函数实现在其他库中,此时直接调用可能会编译不过,这个时候可以在本库中实现一个弱函数,当其他库没有该函数的时候会自动调用这个弱函数。

 

内联函数

定义就不细说了

说一下啊内联函数与宏

 

 内建函数

内建函数,顾名思义,就是编译器内部实现的函数。这些函数跟关键字一样,可以直接使用,无须像标准库函数那样,要 #include 对应的头文件才能使用。通常以 __builtin 开头

 

  • 一山不容二虎
  • 强弱可以共处
  • 体积大者胜出
  • 参数类型检查。内联函数虽然具有宏的展开特性,但其本质仍是函数,编译过程中,编译器仍可以对其进行参数检查,而宏就不具备这个功能。
  • 便于调试。函数支持的调试功能有断点、单步……,内联函数也同样可以。
  • 返回值。内联函数有返回值,返回一个结果给调用者。这个优势是相对于 ANSI C 说的。不过现在宏也可以有返回值和类型了,比如前面我们使用语句表达式定义的宏。
  • 接口封装。有些内联函数可以用来封装一个接口,而宏不具备这个特性。

内建函数的主要用途如下。

 

 __builtinreturnaddress(LEVEL) 

  • 用来处理变长参数列表;
  • 用来处理程序运行异常;
  • 程序的编译优化、性能优化;
  • 查看函数运行中的底层信息、堆栈信息等;
  • C 标准库函数的内建版本。

这个函数用来返回当前函数或调用者的返回地址。函数的参数 LEVEl 表示函数调用链中的不同层次的函数,各个值代表的意义如下。

 

  • 0:返回当前函数的返回地址;
  • 1:返回当前函数调用者的返回地址;
  • 2:返回当前函数调用者的调用者的返回地址;
  • ……

__builtinframeaddress(LEVEL)

我们可以通过内建函数 __builtinframeaddress(LEVEL),查看函数的栈帧地址。

  • 0:查看当前函数的栈帧地址
  • 1:查看当前函数调用者的栈帧地址
  • ……

__builtinconstantp(n)

该函数主要用来判断参数 n 在编译时是否为常量,是常量的话,函数返回1;否则函数返回0。该函数常用于宏定义中,用于编译优化。

__builtin_expect

这个函数的意义主要就是告诉编译器:参数 exp 的值为 c 的可能性很大。然后编译器可能就会根据这个提示信息,做一些分支预测上的代码优化

linux内核中的likely和unlickly就是通过这个内建函数实现的

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值