struct的赋值技巧

来源:互联网 发布:淘宝买东西店铺下架了 编辑:程序博客网 时间:2024/06/07 06:01

为了方便后面的介绍,先定义一个struct类型:

       struct User

       {

           int id;             //id

           char name[100];     //user name

           char *home;         //homedirectory

           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-trivialdesignated 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);

原创粉丝点击