注意:一下函数在windows下可能不能通过编译,在linux下可以。 (GNU C )
一.结构体赋值:
1、对成员赋值
例如结构体
struct st1 {
int a;
int b;
int c;
}
1.1用 {}形式
struct st1 st1 = {1,2,3);
1.2 linux kernel风格
struct st1 st1 = {
.a = 1,
.b = 2,
.c =3,
};
注此风格(即在成员变量之前加点“.”),可以不按成员变量的顺序进行赋值。如可以为
struct st1 st1 = {
.c = 3,
.a = 1,
.b = 2,
};
2对整体赋值.
struct st1 a, b;
b = a;
3结构体作为函数返回值对另一个结构体赋值.
struct st1 func1();
struct st1 a = func1();
2、
内联函数:内联扩展是用来消除函数调用时的时间开销。它通常用于频繁执行的函数。 一个小内存空间的函数非常受益。
在c中,为了解决一些频繁调用的小函数而大量消耗栈空间或者是叫栈内存的问题,特别的引入了inline修饰符,表示为内联函数。内联函数使用inline关键字定义,并且函数体和声明必须结合在一起,否则编译器将他作为普通函数对待。inline函数一般放在头文件中。
inline void function(int x); //仅仅是声明函数,没有任何效果
inline void function(int x) //正确
{
return x;
}
使用内联函数的时候要注意:
1.递归函数不能定义为内联函数
2.内联函数一般适合于不存在while和switch等复杂的结构且只有1~5条语句的小函数上,否则编译系统将该函数视为普通函数。
3.内联函数只能先定义后使用,否则编译系统也会把它认为是普通函数。
4.对内联函数不能进行异常的接口声明。
内联函数的功能和预处理宏的功能相似。相信大家都用过预处理宏,我们会经常定义一些宏,如
#define TABLE_COMP(x) ((x)>0?(x):0)
就定义了一个宏。
为什么要使用宏呢?因为函数的调用必须要将程序执行的顺序转移到函数所存放在内存中的某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方。这种转移操作要求在转去执行前要保存现场并记忆执行的地址,转回后要恢复现场,并按原来保存地址继续执行。因此, 函数调用
要
有一定的时间和空间方面的开销
,于是将影响其效率。而宏只是在预处理的地方把代码展开,不需要额外的空间和时间方面的开销,所以调用一个宏比调用一个函数更有效率。内联函数在调用时,是将调用
表达式用内联函数体来替换
但是宏也有很多的不尽人意的地方。
1、.宏不能访问对象的私有成员。
2、.宏的定义很容易产生二义性。
内联函数和宏的区别在于,宏是由 预处理器对宏进行替代,而内联函数是通过 编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,
内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。
3、
三.typeof用法
1.关键字typeof用于获取表达式的数据类型
1>char *chptr;
typeof (*chptr) ch;//等价于char ch
typeof (ch) *chptr1;//等价于char *chptr1
typeof (chptr1) array[5]; //char *array[5]
chptr1的数据类型为char *
2>typeof常用在linux内核中
#define min(x,y) ({ \
typeof(x) __min1 = (x); \
typeof(y) __min2 =(y); \
(void) (& __min1 == & __min2); \
__min1 <__min2 __min1 :min2>
通过typeof获得x和y的数据,然后定义两个临时变量,并把x和y的分别赋给两个临时变量最后进行比较。
另外,宏定义中(void)(& __min1 = &__min2)语句的作用是用来警告x和y不能属于不同的数据类型。
此处定义了一个安全的求最小值的宏,在标准C中,通常定义为:
#define min(x,y) ((x) < (y) ? (x) : (y))
这个定义计算 x 和 y 分别两次,当参数有副作用时,将产生不正确的结果,使用语句表达式只计算参数一次,避免了可能的错误。语句表达式通常用于宏定义
4、
.GNU C之表达式中的复合语句
1>标准C中:表达式指的是运算符和操作数的组合,而复合语句指的是由一个或多个被括在花括号里的语句构成的代码块。在标准C中,不允许将复合语句用于表达式中。
2>在GNU C中,允许用小括号括起来的复合语句出现在一个表达式中。这个表达式的类型为复合语句中以分号结尾的最后一个子语句表达式的类型,其值也为最后子表达式的值。
这种特性常用于在Linux内核中常被用于宏的定义中 如上求较小数的宏
示例:
#include
main()
{
int a = ({
int b =4;
int c =3;
b+c;
b+c-2;
});
printf("a = %d\n",a);
return 0;
}
输出结果为:
a = 5
说明:
a的数值是复合语句中最后一个语句的值,并且它的数据类型与最后一个语句的数据类型相匹配。
五.GNU C之标号元素
标准 C要求数组或结构变量的初使化值必须以固定的顺序出现,在 GNU C 中,通过指定索引或结构域名,允许初始化值以任意顺序出现。指定数组索引的方法是在初始化值前写 '[INDEX] =',要指定一个范围使用'[FIRST ... LAST] =' 的形式。
1.在数组中的应用
在数组的初始化列表中使用“[index]=value”这样的形式即可以实现对指定(通过index指定)的某个元素进行初始化。
例如:
#include
int main(void)
{
int i;
int arr[6] ={[3] =10,11,[0]=5,6};
for(i=0;i<6;i++)
printf("a[%d]=%d\n",i,arr[i]);
return 0;
}
执行结果为:
a[0]=5
a[1]=6
a[2]=0
a[3]=10
a[4]=11
a[5]=0
说明:
1>如果在一个指定初始化项目后跟有不至一个值,如[3]=10,11。则这些多余的数值将用来对后续的数组元素进行初始化,即数值18用来初始化arr[4].
2>对C语言数组来说,初始化一个或多个元素后,其中未经初始化的元素将被自动地初始化为0后者NULL(针对指针变量而言)。未经过任何初始化的数组,其所有元素的值都将是不确定的。
2.GNU C还支持”[first…last]=value”的形式,即以个范围内的几个元素被初始化为同一值。
例:
1 #include
2 int main()
3 {
4 int i;
5 int arr[]={ [0 ... 3] =1,[4 ... 5]=2,[6... 9] =3};
6 for(i=0;i
7 printf("arr[%d]:%d\n",i,arr[i]);
8 return 0;
9 }
执行结果为:
jibo@jibo-VirtualBox:~/cv_work/work/GNUC/initializer $ ./main1
arr[0]:1
arr[1]:1
arr[2]:1
arr[3]:1
arr[4]:2
arr[5]:2
arr[6]:3
arr[7]:3
arr[8]:3
arr[9]:3
六.GNU C之匿名联合或结构体
在GNU C中,可以在结构体中声明某个联合体(或结构体)而不用指出它的名字,这样之后,就可以像使用结构体成员一样直接使用其中联合体(或结构体)的成员。
Eg:
1 #include
2
3 struct test_struct {
4 char * name;
5 union {
6 char gender;
7 int id;
8 };
9 int num;
10 }; //sizeof(struct test_struct) = 12char * name 4字节
11 int main(void)
12 {
13 struct test_structtest_struct={"zhou",'F',28}; //无论字符串"zhou"多长,siezof依然为12
//也不用malloc给char *name分配内存
14
15 printf("test_struct.gender=%c,test_struct.id=%d\n",test_struct.gender);
//.id为一个随机整数
16
17 return 0;
18 }
八.零长的数组
在标准C中长度为零的数组是被禁止的,在标准C中,要求数组最少长度为1个字节。但是GCC允许定义零长数组。那么为什么GNU C中要支持零长度数组,它的好处是什么,它如何使使用,以及零长度数组主要用在什么场合:
1.GNU C中允许使用这种用法,目的是为了访问不定长结构体时节省空间和便利性。
2.用法:在一个结构体的最后,申明一个长度为0的数组,就可以使得这个结构体是可变长的。对于 编译器来说,此时长度为0的数组并不占用空间,因为数组名本身不占空间,它只是一个偏移量, 数组名这个符号本身代表了一个不可修改的地址常量 (注意:数组名永远都不会是指针!),但对于这个数组的大小,我们可以进行动态分配。
Eg:
struct demo
{
int a;
char b[256];
char follow[0];
};
现在程序中要分配一个struct demo结构体,并紧邻其后分配长度为LEN个字节空间,则可以使用如下方法得到:
struct demo *demo=(struct demo*)malloc(sizeof(struct demo)+LEN);
这样就可以用demo->fellow来访问结构体demo随后的空间数据,非常方便。当然也可以使用指针来达到这样的目的。
Eg:
struct demo{
int a;
char b[256];
char *follow;
};
struct demo *demo=(struct demo *)malloc(sizeof(structdemo)+LEN);
同样可以达到零长度数组的效果,但是却多分配了一个char指针。如果分配额外数据空间还好
否则就是白白浪费了空间。
注:
1>在某一结构末尾如定义类似 charbytes[0]的零长数组,表示该结构不定长,可通过数组的方式进行扩展。结构中必包含一个长度信息。结构本身类似于一个信息头。同时,此结构只能通过堆方式分配内存。
2>优点:比起在结构体中声明一个指针变量、再进行动态分配的办法,这种方法效率要高。因为在访问数组内容时,不需要间接访问,避免了两次访存。
3.示例:
#include
#include
struct test{
int count;
int reverse[0];
};
int main()
{
int i;
struct test *ptest = (struct test *)malloc(sizeof(struct test)+sizeof(int)*10);
for(i=0;i<10;i++){
ptest->reverse[i]=i+1;
}
for(i=0;i<10;i++){
printf("reverse[%d]=%d \n",i,ptest->reverse[i]);
}
printf("sizeof(struct test) =%d\n",sizeof(struct test));
int a =*(&ptest->count+1); //优先级-> > & 注意不是+4 若是加4,则a = 4
printf("a=%d\n",a);
return 0;
}
执行结果为:
reverse[0]=1
reverse[1]=2
。。。
reverse[9]=10
sizeof(struct test) =4
a=1
分析:
可以看到test结构体中reverse数组并不占用空间。Sizeof struct test占用内存空间为4.并且可以看到结果体conunt变量之后就是零长度数组的内容。
#include
int main()
{
int a[5] = {1,2,3,4,5};
int *p = a;
printf("%d\n",*(&a[0]+1));
}
输出:2
九.范围标记
1.GCC还扩充了范围标记,通过它可以表示一段数值范围。这可以用在C程序的很多地方。最常用的莫过于 switch/case 语句中。
Eg:
static int sd_major(int major_idx)
{
switch (major_idx) {
case 0:
returnSCSI_DISK0_MAJOR;
case 1...7:
return SCSI_DISK1_MAJOR+ major_idx - 1;
case 8 ... 15:
returnSCSI_DISK8_MAJOR + major_idx - 8;
default:
BUG();
return 0; /* shut up gcc */
}
}
2.范围标记还可以用来初始化数组里面的一些连续元素。如五中所介绍的数组。