美文网首页C语言C语言&嵌入式
C语言结构体史上最详细的讲解

C语言结构体史上最详细的讲解

作者: duyuanchao | 来源:发表于2018-12-18 11:03 被阅读0次

    原文链接

    https://github.com/shellhub/blog/issues/40

    struct结构体数据类型

    前言

    我们知道,在C语言中有一些基本的数据类型,如

    • char
    • int
    • float
    • long
    • double
    • string(c99)

    等等数据类型,他们可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,这时候C提供了一种自定义数据类型,他可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct,可以使用struct关键词声明结构体

    结构体的声明

    结构体的声明语法如下

    struct [structure tag] /*结构体的标签*/{
    
       member definition; /*零个或多个成员变量的定义*/
       member definition;
       ...
       member definition;
    } [one or more structure variables];  /*一个或多个结构体变量的定义*/
    

    结构体标签(structure tag)是可选的,但是推荐还是写上,这样使得代码更加规范清晰,成员变量的定义一般为基本数据类型,如 int age; char name[10]等,成员变量之间使用;隔开,最后一个成员变量后面的;可选, 如下面定义一个图书信息的结构体变量

    struct Books {
       char  title[50];
       char  author[50];
       char  subject[100];
       int   book_id;
    } book;  
    

    如下所示

    struct Books {
       char  title[50];
       char  author[50];
       char  subject[100];
       int   book_id
    } book;
    

    我省略了最后一个成员变量后面的分号;代码可以正常运行,但是当我使用gcc编译的时候,出现了下面信息

     gcc struct.c
    

    output

    struct.c:8:1: warning: no semicolon at end of struct or union
     } book;
     ^
    

    这是警告提示,提示我们需要在structunion数据类型定义的后面加上分号;,这样的好处就是当我们需要再添加一个成员变量的时候,只需写上该成员变量的定义,而无需先敲;,我太机智了,手动滑稽...

    没有成员变量的结构体

    我们也可以定义一个空的结构体,有时候我们需要某一个结构体数据类型,但是暂时又不知道如何填充里面的成员变量,我们可以有如下定义

    struct Books {
      //TODO
    } book;
    

    访问结构体成员

    定义完结构体积后接下来就是去访问它并给他赋值,为了访问一个结构体成员变量,我们可以使用成员操作符(.) 成员访问运算符被编码为结构变量名称和我们希望访问的结构成员之间的句点(.)如下所示的完整代码

    struct.c

    #include <stdio.h>
    #include <string.h>
    
    struct Books {
       char  title[50];
       char  author[50];
       char  subject[100];
       int   book_id;
    };
    
    int main( ) {
    
       struct Books Book1;        /* Declare Book1 of type Book */
       struct Books Book2;        /* Declare Book2 of type Book */
    
       /* book 1 specification */
       strcpy( Book1.title, "C Programming");
       strcpy( Book1.author, "Nuha Ali");
       strcpy( Book1.subject, "C Programming Tutorial");
       Book1.book_id = 6495407;
    
       /* book 2 specification */
       strcpy( Book2.title, "Telecom Billing");
       strcpy( Book2.author, "Zara Ali");
       strcpy( Book2.subject, "Telecom Billing Tutorial");
       Book2.book_id = 6495700;
    
       /* print Book1 info */
       printf( "Book 1 title : %s\n", Book1.title);
       printf( "Book 1 author : %s\n", Book1.author);
       printf( "Book 1 subject : %s\n", Book1.subject);
       printf( "Book 1 book_id : %d\n", Book1.book_id);
    
       /* print Book2 info */
       printf( "Book 2 title : %s\n", Book2.title);
       printf( "Book 2 author : %s\n", Book2.author);
       printf( "Book 2 subject : %s\n", Book2.subject);
       printf( "Book 2 book_id : %d\n", Book2.book_id);
    
       return 0;
    }
    

    编译并执行

    gcc struct.c && ./a.out
    

    输出

    Book 1 title : C Programming
    Book 1 author : Nuha Ali
    Book 1 subject : C Programming Tutorial
    Book 1 book_id : 6495407
    Book 2 title : Telecom Billing
    Book 2 author : Zara Ali
    Book 2 subject : Telecom Billing Tutorial
    Book 2 book_id : 6495700
    

    结构作为函数参数

    同样的,我们也可以像基本数据类型一样,把结构体作为函数的参数,如下所示我们定义一个打印结构体的函数

    #include <stdio.h>
    #include <string.h>
    
    struct Books {
       char  title[50];
       char  author[50];
       char  subject[100];
       int   book_id;
    };
    
    /* function declaration */
    void printBook( struct Books book );
    
    int main( ) {
    
       struct Books Book1;        /* Declare Book1 of type Book */
       struct Books Book2;        /* Declare Book2 of type Book */
    
       /* book 1 specification */
       strcpy( Book1.title, "C Programming");
       strcpy( Book1.author, "Nuha Ali");
       strcpy( Book1.subject, "C Programming Tutorial");
       Book1.book_id = 6495407;
    
       /* book 2 specification */
       strcpy( Book2.title, "Telecom Billing");
       strcpy( Book2.author, "Zara Ali");
       strcpy( Book2.subject, "Telecom Billing Tutorial");
       Book2.book_id = 6495700;
    
       /* print Book1 info */
       printBook( Book1 );
    
       /* Print Book2 info */
       printBook( Book2 );
    
       return 0;
    }
    
    void printBook( struct Books book ) {
    
       printf( "Book title : %s\n", book.title);
       printf( "Book author : %s\n", book.author);
       printf( "Book subject : %s\n", book.subject);
       printf( "Book book_id : %d\n", book.book_id);
    }
    

    编译运行

    gcc struct.c && ./a.out
    

    输出

    Book 1 title : C Programming
    Book 1 author : Nuha Ali
    Book 1 subject : C Programming Tutorial
    Book 1 book_id : 6495407
    Book 2 title : Telecom Billing
    Book 2 author : Zara Ali
    Book 2 subject : Telecom Billing Tutorial
    Book 2 book_id : 6495700
    

    结构体的指针

    我们也可以定义结构体指针,像这样

    struct Books *struct_pointer;
    

    现在你可以存放结构体变量的地址在结构体变量指针中.和基本数据类型的变量一样,我们使用&操作符取一个变量的地址

    struct_pointer = &Book1;
    

    接下来就是使用结构体指针去访问成员变量了,访问的操作符我们由原来的.变为->,没错,这个是不是很形象呢?完整代码如下

    #include <stdio.h>
    #include <string.h>
    
    struct Books {
       char  title[50];
       char  author[50];
       char  subject[100];
       int   book_id;
    };
    
    /* function declaration */
    void printBook( struct Books *book );
    int main( ) {
    
       struct Books Book1;        /* Declare Book1 of type Book */
       struct Books Book2;        /* Declare Book2 of type Book */
    
       /* book 1 specification */
       strcpy( Book1.title, "C Programming");
       strcpy( Book1.author, "Nuha Ali");
       strcpy( Book1.subject, "C Programming Tutorial");
       Book1.book_id = 6495407;
    
       /* book 2 specification */
       strcpy( Book2.title, "Telecom Billing");
       strcpy( Book2.author, "Zara Ali");
       strcpy( Book2.subject, "Telecom Billing Tutorial");
       Book2.book_id = 6495700;
    
       /* print Book1 info by passing address of Book1 */
       printBook( &Book1 );
    
       /* print Book2 info by passing address of Book2 */
       printBook( &Book2 );
    
       return 0;
    }
    
    void printBook( struct Books *book ) {
    
       printf( "Book title : %s\n", book->title);
       printf( "Book author : %s\n", book->author);
       printf( "Book subject : %s\n", book->subject);
       printf( "Book book_id : %d\n", book->book_id);
    }
    

    编译运行

    gcc struct.c && ./a.out
    

    输出

    Book 1 title : C Programming
    Book 1 author : Nuha Ali
    Book 1 subject : C Programming Tutorial
    Book 1 book_id : 6495407
    Book 2 title : Telecom Billing
    Book 2 author : Zara Ali
    Book 2 subject : Telecom Billing Tutorial
    Book 2 book_id : 6495700
    

    结构体数组

    #include <stdio.h>
    #include <string.h>
    
    struct Books {
        char  title[50];
        char  author[50];
        char  subject[100];
        int   book_id;
    };
    
    /* function declaration */
    void printBook( struct Books *book );
    int main( ) {
    
        struct Books books[2];
    
        /* book 1 specification */
        strcpy( books[0].title, "C Programming");
        strcpy( books[0].author, "Nuha Ali");
        strcpy( books[0].subject, "C Programming Tutorial");
        books[0].book_id = 6495407;
    
        /* book 2 specification */
        strcpy( books[1].title, "Telecom Billing");
        strcpy( books[1].author, "Zara Ali");
        strcpy( books[1].subject, "Telecom Billing Tutorial");
        books[1].book_id = 6495700;
    
        /* print Book1 info by passing address of Book1 */
        printBook( &books[0] );
    
        /* print Book2 info by passing address of Book2 */
        printBook( &books[1] );
    
        return 0;
    }
    
    void printBook( struct Books *book ) {
    
        printf( "Book title : %s\n", book->title);
        printf( "Book author : %s\n", book->author);
        printf( "Book subject : %s\n", book->subject);
        printf( "Book book_id : %d\n", book->book_id);
    }
    

    编译运行

    gcc struct.c && ./a.out
    

    输出

    Book 1 title : C Programming
    Book 1 author : Nuha Ali
    Book 1 subject : C Programming Tutorial
    Book 1 book_id : 6495407
    Book 2 title : Telecom Billing
    Book 2 author : Zara Ali
    Book 2 subject : Telecom Billing Tutorial
    Book 2 book_id : 6495700
    

    结构体的内存计算

    没错,估计你已经知道了,结构体变量的所占用内存空间的大小为各成员变量所占空间之和,如下所示的结构体占用内存大小在注释里面

    #include <stdio.h>
    #include <string.h>
    
    struct Books {
    };
    
    int main( ) {
        printf("%d\n", (int) sizeof(struct Books)); /*0*/
        return 0;
    }
    
    #include <stdio.h>
    #include <string.h>
    
    struct Books {
        char title[50];
        char author[50];
        char subject[100];
        int book_id;
    };
    
    int main() {
        printf("%d\n", (int) sizeof(struct Books)); /*204*/
        return 0;
    }
    

    位域

    有时候我们内存紧张的时候,我们可以使用位域定义结构体成员变量,比如当我们需要定义一个表示truefalse的时候,如果想这样定义

    int isOpen;
    

    明显很浪费空间,因为一个真假值只需要一个字位表示,所以我们可以这样定义

    unsigned int isOpen:1;
    

    但是如果你直接写在函数中是会报错的,我们应该写在结构体中

    int main() {
        unsigned int isOpen:1; /*编译无法通过*/
        return 0;
    }
    

    正确姿势

    struct packed_struct {
       unsigned int f1:1;
       unsigned int f2:1;
       unsigned int f3:1;
       unsigned int f4:1;
       unsigned int type:4;
       unsigned int my_int:9;
    } pack;
    

    C尽可能紧凑地自动打包上述位字段,前提是字段的最大长度小于或等于计算机的整数字长。如果不是这种情况,那么一些编译器可能允许字段存储器重叠,而其他编译器会将下一个字段存储在下一个字中。

    #include <stdio.h>
    #include <string.h>
    
    struct packed_struct {
        unsigned int f1:1;
        unsigned int f2:1;
        unsigned int f3:1;
        unsigned int f4:1;
        unsigned int type:4;
        unsigned int my_int:9;
    } pack;
    int main() {
        printf("%d\n", (int) sizeof(struct packed_struct));
        return 0;
    }
    

    输出结果
    8

    相关文章

      网友评论

        本文标题:C语言结构体史上最详细的讲解

        本文链接:https://www.haomeiwen.com/subject/qnpkkqtx.html