数组相关
零长数组:
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就是通过这个内建函数实现的