为了方便后面的介绍,先定义一个struct类型:
struct User
{
int id; //id
char name[100]; //user name
char *home; //home directory
int passwd; //password
};
1 初始化
struct数据有3中初始化方法:顺序,C风格及C++风格的乱序。
1)顺序
这种方法很常见,在一般的介绍C的书中都有介绍。
顺序初始化的特点是: 按照成员定义的顺序,从前到后逐个初始化;允许只初始化部分成员;在被初始化的成员之前,不能有未初始化的成员;未显示初始化的自动设为0。
这种赋值方式也是最常见的,每个学C语言的人都懂的。
eg:
struct User oneUser = {10, "Lucy", "/home/Lucy"};
初始化之后,oneUser各个成员的值为:
oneUser.id = 10;
oneUser.name = "Lucy";
oneUser.home = "/home/Lucy";
oneUser.passwd = 0;
2)乱序(C风格) // 这个一般的教材上好像没有介绍,而且gcc不支持后缀名为cpp的文件使用这种方式!!!!
顺序的缺陷是必须按成员定义的顺序逐个初始化,不能间隔。而乱序的方式则很好的解决了这个问题,因为这种方式是按照成员名进行。
eg:
struct User oneUser = {
.name = "Lucy",
.id = 10,
.home = "/home/Lucy"
}; // 这个时候才回想起来,在内核代码里确实看到过类似的代码。。。。
3)乱序(C++风格) // 这个地方注意了,gcc不支持后缀名为cpp的文件使用这种方式!!!!
C++风格的乱序初始化方式跟C风格的一样,只是它更常用在C++代码里。
eg:
struct User oneUser = {
name:"Lucy",
id:10,
home:"/home/Lucy"
};
乱序这种方式在gcc/g++中,后缀名位.c可以支持;但是后缀名为.cpp就不支持,提示
sorry, unimplemented: non-trivial designated initializers not supported
2 拷贝
struct有两种拷贝方式,一是直接赋值(=),另一种是用memcpy等库函数实行内存拷贝。
eg:
struct Temp a, b;
//Set value to members of b
a = b;
memcpy(&a, &b, sizeof(a));
不管是哪种拷贝方式,都是将以&b开始的,大小为sizeof(struct Temp)的内存区域中的数据,简单地复制到以&a开始的,同样大小的内存区域。所以,这两种方式与按成员赋值是等价的:
a.id = b.id;
a.name = b.name;
a.home = b.home;
a.passwd = b.passwd;
由此,我们不难看出,上面两种拷贝方式都属于浅拷贝。
3 指针成员的两种使用技巧
1) 为多个指针成员同时分配内存
如果一个struct中有多个指针类型的成员,我们通常需要为每个指针逐个成员分配内存空间,并在使用完时释放它们;这样频繁调用malloc/free,难免让人生厌。如果在分配内存之前,每个指针所指向内存区域的大小是确定的,那么,我们可以为所有指针一次性分配内存区域;并在使用完后,一次性释放。
eg:
struct Inode
{
int id;
char *file;
int fie_len;
char *path;
int path_len;
char *user;
int user_len;
};
struct Inode data = {
.file_len = X,
.path_len = Y,
.user_len = X
};
//Allocate memory
data.file = (char *)malloc(data.file_len + data.path_len + data.user_len); // 一下子分配所有内存,只适用于知道内存大小
data.path = data.file + data.file_len;
data.user = data.path + data.path_len;
//User
...
//Free memory
free(data.file);
2)变长数组的另类实现
将下面的定义
struct File
{
TypeA dataA;
......
char *data;
TypeN dataN;
};
改成:
struct File
{
TypeA dataA;
......
TypeN dataN;
char data[0]; // 定义大小为0的一维数组,且放在最后,实际上是利用数组越界访问!!!
};
即将指针成员换成大小为0的一维数组, 作为struct的最后一个成员(数据结构的可变部分必须作为最后一个成员),有两个优点:
(1) 在紧邻struct处为data分配内存区域,这样在分配内存后无须为data赋值;
(2) 利用数组的特性,以指针的方式通过越界访问data数组外的内存区域。
eg:
struct File *pVar = (struct File *)malloc(sizeof(struct File) + DATA_LEN);
strncpy(pVar->data, "Source data", DATA_LEN);
为了方便后面的介绍,先定义一个struct类型:
struct User
{
int id; //id
char name[100]; //user name
char *home; //home directory
int passwd; //password
};
1 初始化
struct数据有3中初始化方法:顺序,C风格及C++风格的乱序。
1)顺序
这种方法很常见,在一般的介绍C的书中都有介绍。 顺序初始化的特点是: 按照成员定义的顺序,从前到后逐个初始化;允许只初始化部分成员;在被初始化的成员之前,不能有未初始化的成员;未显示初始化的自动设为0。
这种赋值方式也是最常见的,每个学C语言的人都懂的。
struct User
{
int id; //id
char name[100]; //user name
char *home; //home directory
int passwd; //password
};
1 初始化
struct数据有3中初始化方法:顺序,C风格及C++风格的乱序。
1)顺序
这种方法很常见,在一般的介绍C的书中都有介绍。 顺序初始化的特点是: 按照成员定义的顺序,从前到后逐个初始化;允许只初始化部分成员;在被初始化的成员之前,不能有未初始化的成员;未显示初始化的自动设为0。
这种赋值方式也是最常见的,每个学C语言的人都懂的。
eg:
struct User oneUser = {10, "Lucy", "/home/Lucy"};
初始化之后,oneUser各个成员的值为:
oneUser.id = 10;
oneUser.name = "Lucy";
oneUser.home = "/home/Lucy";
oneUser.passwd = 0;
2)乱序(C风格) // 这个一般的教材上好像没有介绍,而且gcc不支持后缀名为cpp的文件使用这种方式!!!!
顺序的缺陷是必须按成员定义的顺序逐个初始化,不能间隔。而乱序的方式则很好的解决了这个问题,因为这种方式是按照成员名进行。
eg:
struct User oneUser = {
.name = "Lucy",
.id = 10,
.home = "/home/Lucy"
}; // 这个时候才回想起来,在内核代码里确实看到过类似的代码。。。。
3)乱序(C++风格) // 这个地方注意了,gcc不支持后缀名为cpp的文件使用这种方式!!!!
C++风格的乱序初始化方式跟C风格的一样,只是它更常用在C++代码里。
eg:
struct User oneUser = {
name:"Lucy",
id:10,
home:"/home/Lucy"
};
乱序这种方式在gcc/g++中,后缀名位.c可以支持;但是后缀名为.cpp就不支持,提示
struct User oneUser = {10, "Lucy", "/home/Lucy"};
初始化之后,oneUser各个成员的值为:
oneUser.id = 10;
oneUser.name = "Lucy";
oneUser.home = "/home/Lucy";
oneUser.passwd = 0;
2)乱序(C风格) // 这个一般的教材上好像没有介绍,而且gcc不支持后缀名为cpp的文件使用这种方式!!!!
顺序的缺陷是必须按成员定义的顺序逐个初始化,不能间隔。而乱序的方式则很好的解决了这个问题,因为这种方式是按照成员名进行。
eg:
struct User oneUser = {
.name = "Lucy",
.id = 10,
.home = "/home/Lucy"
}; // 这个时候才回想起来,在内核代码里确实看到过类似的代码。。。。
3)乱序(C++风格) // 这个地方注意了,gcc不支持后缀名为cpp的文件使用这种方式!!!!
C++风格的乱序初始化方式跟C风格的一样,只是它更常用在C++代码里。
eg:
struct User oneUser = {
name:"Lucy",
id:10,
home:"/home/Lucy"
};
乱序这种方式在gcc/g++中,后缀名位.c可以支持;但是后缀名为.cpp就不支持,提示
sorry, unimplemented: non-trivial designated initializers not supported
2 拷贝
struct有两种拷贝方式,一是直接赋值(=),另一种是用memcpy等库函数实行内存拷贝。
eg:
struct Temp a, b;
//Set value to members of b
a = b;
memcpy(&a, &b, sizeof(a));
不管是哪种拷贝方式,都是将以&b开始的,大小为sizeof(struct Temp)的内存区域中的数据,简单地复制到以&a开始的,同样大小的内存区域。所以,这两种方式与按成员赋值是等价的:
a.id = b.id;
a.name = b.name;
a.home = b.home;
a.passwd = b.passwd;
由此,我们不难看出,上面两种拷贝方式都属于浅拷贝。
3 指针成员的两种使用技巧
1) 为多个指针成员同时分配内存
如果一个struct中有多个指针类型的成员,我们通常需要为每个指针逐个成员分配内存空间,并在使用完时释放它们;这样频繁调用malloc/free,难免让人生厌。如果在分配内存之前,每个指针所指向内存区域的大小是确定的,那么,我们可以为所有指针一次性分配内存区域;并在使用完后,一次性释放。
eg:
struct Inode
{
int id;
char *file;
int fie_len;
char *path;
int path_len;
char *user;
int user_len;
};
struct Inode data = {
.file_len = X,
.path_len = Y,
.user_len = X
};
//Allocate memory
data.file = (char *)malloc(data.file_len + data.path_len + data.user_len); // 一下子分配所有内存,只适用于知道内存大小
data.path = data.file + data.file_len;
data.user = data.path + data.path_len;
//User
...
//Free memory
free(data.file);
2)变长数组的另类实现
将下面的定义
struct File
{
TypeA dataA;
......
char *data;
TypeN dataN;
};
改成:
struct File
{
TypeA dataA;
......
TypeN dataN;
char data[0]; // 定义大小为0的一维数组,且放在最后,实际上是利用数组越界访问!!!
};
即将指针成员换成大小为0的一维数组, 作为struct的最后一个成员(数据结构的可变部分必须作为最后一个成员),有两个优点:
(1) 在紧邻struct处为data分配内存区域,这样在分配内存后无须为data赋值;
(2) 利用数组的特性,以指针的方式通过越界访问data数组外的内存区域。
eg:
struct File *pVar = (struct File *)malloc(sizeof(struct File) + DATA_LEN);
strncpy(pVar->data, "Source data", DATA_LEN);
2 拷贝
struct有两种拷贝方式,一是直接赋值(=),另一种是用memcpy等库函数实行内存拷贝。
eg:
struct Temp a, b;
//Set value to members of b
a = b;
memcpy(&a, &b, sizeof(a));
不管是哪种拷贝方式,都是将以&b开始的,大小为sizeof(struct Temp)的内存区域中的数据,简单地复制到以&a开始的,同样大小的内存区域。所以,这两种方式与按成员赋值是等价的:
a.id = b.id;
a.name = b.name;
a.home = b.home;
a.passwd = b.passwd;
由此,我们不难看出,上面两种拷贝方式都属于浅拷贝。
3 指针成员的两种使用技巧
1) 为多个指针成员同时分配内存
如果一个struct中有多个指针类型的成员,我们通常需要为每个指针逐个成员分配内存空间,并在使用完时释放它们;这样频繁调用malloc/free,难免让人生厌。如果在分配内存之前,每个指针所指向内存区域的大小是确定的,那么,我们可以为所有指针一次性分配内存区域;并在使用完后,一次性释放。
eg:
struct Inode
{
int id;
char *file;
int fie_len;
char *path;
int path_len;
char *user;
int user_len;
};
struct Inode data = {
.file_len = X,
.path_len = Y,
.user_len = X
};
//Allocate memory
data.file = (char *)malloc(data.file_len + data.path_len + data.user_len); // 一下子分配所有内存,只适用于知道内存大小
data.path = data.file + data.file_len;
data.user = data.path + data.path_len;
//User
...
//Free memory
free(data.file);
2)变长数组的另类实现
将下面的定义
struct File
{
TypeA dataA;
......
char *data;
TypeN dataN;
};
改成:
struct File
{
TypeA dataA;
......
TypeN dataN;
char data[0]; // 定义大小为0的一维数组,且放在最后,实际上是利用数组越界访问!!!
};
即将指针成员换成大小为0的一维数组, 作为struct的最后一个成员(数据结构的可变部分必须作为最后一个成员),有两个优点:
(1) 在紧邻struct处为data分配内存区域,这样在分配内存后无须为data赋值;
(2) 利用数组的特性,以指针的方式通过越界访问data数组外的内存区域。
eg:
struct File *pVar = (struct File *)malloc(sizeof(struct File) + DATA_LEN);
strncpy(pVar->data, "Source data", DATA_LEN);