C语言:结构体和共用体

这是很基础的教程,我只是写给自己看,作为一个学习笔记记录一下,如果正在阅读的你觉得简单,请不要批评,可以关掉选择离开

如何学好一门编程语言

  • 掌握基础知识,为将来进一步学习打下良好的基础。
  • 上机实践,通过大量的例题学习怎么设计算法,培养解题思路。
  • 养成良好的编码习惯,注释一定要写,要不然保你一周后自己写的代码都不认识了。

结构体

C语言中的结构 允许存储不同类型的数据,定义结构体 格式 如下:

struct 结构体名字{ 
    变量定义;
    变量定义;
    ...
} ;

例如

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

struct Books的作用就相当于 一般声明中的int或float。我们可以用该结构 定义 结构变量,结构变量可以是普通的变量,还可以是数组

struct Books library, li[10];

以下是结构体声明的简化:

struct Books {         // Books是 结构名
    char title[50];    // 结构体成员
    char author[50];
    char subject[100];
    int book_id;
} library;  // library是结构变量

如果只想使用一次该结构,还可以省略结构名,直接创建结构变量。

struct {    /*无结构名*/
    char title[50];    // 结构体成员
    char author[50];
    char subject[100];
    int book_id;
} library;  // library是结构变量

我们还可以用typedef创建新类型

typedef struct {
  int a;
  char b;
  double c;
} Simple2;
// 现在可以用Simple2作为类型 声明新的结构变量
Simple2 u1, u2[20], *u3;

结构体的成员可以包含其他结构体

  struct COMPLEX {
    char string[100];
    struct SIMPLE a;
  };
View Code

结构体也可以包含指向自己结构体类型的指针,而通常这种指针的应用是为了实现一些更高级的数据结构如链表和树等。例如:

struct NODE {
  char string[100];
  struct NODE *next_node;
};
View Code

如果两个结构体互相包含,则需要对其中一个结构体进行不完整声明,例如:

struct B;    //对结构体B进行不完整声明

//结构体A中包含指向结构体B的指针
struct A {
  struct B *partner;
  //other members;
};

//结构体B中包含指向结构体A的指针,在A声明完后,B也随之进行声明
struct B {
  struct A *partner;
  //other members;
};
View Code

结构体 初始化

可以在定义结构体时 指定初始值

struct Books{
    char title[50];
    char author[50];
    char subject[100];
    int book_id;
} book = {"C语言","never","编程语言",123456};

也可以在定义结构变量时初始化

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

struct Books book = {"C语言", "never", "编程语言", 123456};

还有一种访问式的初始化 方法

struct book gift = { .value = 25.99,
                     .author = "James Broadfool",
                     .title = "Rue for the Toad"};

访问结构成员

访问结构成员格式: 结构体变量.成员名 

结构作为函数参数

  当把结构作为函数参数时,传参方式与其他类型的变量或指针类似。

#include <stdio.h>
#include <string.h>

// 声明结构体类型Books
struct Books {
    char  title[50];
    char  author[50];
    char  subject[100];
    int   book_id;
};

void printBook(struct Books book);    /* 函数声明 */

int main() {
    struct Books Book1;        /* 声明类型为Books的结构体变量Book1 */
    struct Books Book2;        /* 声明类型为Books的结构体变量Book2 */

    /* Book1 详述 */
    strcpy(Book1.title, "C Programming");
    strcpy(Book1.author, "Nuha Ali");
    strcpy(Book1.subject, "C Programming Tutorial");
    Book1.book_id = 6495407;

    /* Book2 详述 */
    strcpy(Book2.title, "Telecom Billing");
    strcpy(Book2.author, "Zara Ali");
    strcpy(Book2.subject, "Telecom Billing Tutorial");
    Book2.book_id = 6495700;

    printBook(Book1);    /* 将结构变量Book1 作为参数传入函数*/
    printBook(Book2);    /* 将结构变量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);
}
View Code
Book title : C Programming
Book author : Nuha Ali
Book subject : C Programming Tutorial
Book book_id : 6495407
Book title : Telecom Billing
Book author : Zara Ali
Book subject : Telecom Billing Tutorial
Book book_id : 6495700
结果

结构体指针

  结构体指针 与 指向其他类型变量的指针相似,格式如下:

// 声明结构指针
struct
Books *struct_pointer;

结构指针 指向 结构变量Book1的地址  struct_pointer = &Book1; 

使用 结构体指针 访问 结构成员方式为: struct_pointer->title; ,如果访问成员变量的地址: &struct_pointer->title; ,->的优先级高于&。

#include <stdio.h>
#include <string.h>

// 定义一个结构类型
struct Books{
    char  title[50];
    char  author[50];
    char  subject[100];
    int   book_id;
};

void printBook(struct Books* book);    /* 函数声明 */

int main(){
    struct Books Book1;        /* 声明 Book1,类型为 Books */
    struct Books Book2;        /* 声明 Book2,类型为 Books */

    /* Book1 详述 */
    strcpy(Book1.title, "C Programming");
    strcpy(Book1.author, "Nuha Ali");
    strcpy(Book1.subject, "C Programming Tutorial");
    Book1.book_id = 6495407;

    /* Book2 详述 */
    strcpy(Book2.title, "Telecom Billing");
    strcpy(Book2.author, "Zara Ali");
    strcpy(Book2.subject, "Telecom Billing Tutorial");
    Book2.book_id = 6495700;

    printBook(&Book1);    /* 通过传 Book1 的地址来输出 Book1 信息 */
    printBook(&Book2);    /* 通过传 Book2 的地址来输出 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);
}
View Code
Book title : C Programming
Book author : Nuha Ali
Book subject : C Programming Tutorial
Book book_id : 6495407
Book title : Telecom Billing
Book author : Zara Ali
Book subject : Telecom Billing Tutorial
Book book_id : 6495700
结果

为什么要使用指向结构的指针。

  1. 和指向数组的指针比数组本身更容易操控(如,排序问题)一样,指向结构的指针通常比结构本身更容易操控。
  2. 在一些早期的C实现中,结构不能作为参数传递给函数,但是可以传递指向结构的指针。
  3. 传递指针通常更有效率。

位域

  为了节省存储空间,并使处理简便,C 语言提供了一种数据结构,称为"位域"或"位段",带有预定义宽度的变量,位域定义与结构定义相仿,其形式为:

struct 位域结构名 {
    type [member_name] : width ;
    ...
};

其中

  • type:只能为 int,unsigned int,signed int
  • member_name:位域的名称。
  • width:位域中位的数量。宽度必须小于或等于指定类型的位宽度。
#include <stdio.h>
#include <string.h>

/* 定义简单的结构 */
struct{
    unsigned int widthValidated;
    unsigned int heightValidated;
} status1;

/* 定义位域结构 */
struct{
    unsigned int widthValidated : 2;
    unsigned int heightValidated : 2;
} status2;

int main(){
    printf("占用的内存大小: %d\n", sizeof(status1));    // 8
    printf("占用的内存大小: %d\n", sizeof(status2));    // 4

    return 0;
}

超出位域则无法完成,例如:

#include <stdio.h>
#include <string.h>

struct{
    unsigned int age : 3;
} Age;

int main(){
    Age.age = 4;
    printf("Sizeof( Age ) : %d\n", sizeof(Age));    // 4
    printf("Age.age : %d\n", Age.age);        // 4

    Age.age = 7;
    printf("Age.age : %d\n", Age.age);    // 7

    Age.age = 8; // 8的二进制表示为 1000 有四位,超出
    printf("Age.age : %d\n", Age.age);    // 0

    return 0;
}
View Code

对于位域的定义尚有以下几点说明:

一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:

struct bs {
    unsigned a : 4;
    unsigned : 4;    /* 空域,该4位不能使用 */
    unsigned b : 4;    /* 从下一单元开始存放 */
    unsigned c : 4
}

在这个位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用,b 从第二字节开始,占用 4 位,c 占用 4 位。

位域的使用

位域的使用和结构成员的使用相同,其一般形式为:

位域变量名.位域名
位域变量名->位域名

位域允许用各种格式输出。

#include<stdio.h>

int main() {
    struct bs {
        unsigned a : 1;
        unsigned b : 3;
        unsigned c : 4;
    } bit, * pbit;
    bit.a = 1;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
    bit.b = 7;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
    bit.c = 15;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */
    printf("%d,%d,%d\n", bit.a, bit.b, bit.c);    /* 以整型量格式输出三个域的内容 */

    pbit = &bit;    /* 把位域变量 bit 的地址送给指针变量 pbit */
    pbit->a = 0;    /* 用指针方式给位域 a 重新赋值,赋为 0 */
    pbit->b &= 3;    /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */
    pbit->c |= 1;    /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */
    printf("%d,%d,%d\n", pbit->a, pbit->b, pbit->c);    /* 用指针方式输出了这三个域的值 */
1, 7, 15
0, 3, 15
结果

共用体

   共用体允许我们在相同的内存位置存储多个不同数据类型的成员,但是任何时候只能有一个成员带有值

共用体提供了一种使用相同的内存位置的有效方式。

定义共用体

  共用体的定义和结构体类似,共用体需要使用 union 语句,格式为:

union 共同体标签
{
   成员定义;
   成员定义;
   ...
} 一个或多个共用体变量;

举例说明:

union Data
{
   int i;
   float f;
   char  str[20];
} data;

共用体标签 和 共用体变量 是可选的,

  共用体 占用的内存等于 最大成员的存储内存

#include <string.h>

union Data{
    int i;
    float f;
    char  str[20];
};

int main(){
    union Data data;
    char  str_1[20];
    float f_1;

    printf("数据占用的内存: %d\n", sizeof(f_1));     // 4
    printf("数据占用的内存: %d\n", sizeof(str_1));   // 20
    printf("数据占用的内存: %d\n", sizeof(data));    // 20

    return 0;
}
View Code

访问共用体成员 格式为: 共用体变量.成员 

#include <stdio.h>
#include <string.h>

union Data{
    int i;
    float f;
    char  str[20];
};

int main(){
    union Data data;

    data.i = 10;
    data.f = 220.5;
    strcpy(data.str, "C Programming");

    printf("data.i : %d\n", data.i);    // 1917853763
    printf("data.f : %f\n", data.f);    // 4122360580327794860452759994368.000000
    printf("data.str : %s\n", data.str);    // C Programming

    return 0;
}
View Code

我们发现共用体的 i 和 f 成员的值有损坏,因为最后赋给变量的值占用了内存位置,这也是 str 成员能够完好输出的原因。

  现在让我们再来看一个相同的实例,这次我们在同一时间只使用一个变量,这也演示了使用共用体的主要目的:

#include <stdio.h>
#include <string.h>

union Data{
    int i;
    float f;
    char  str[20];
};

int main(){
    union Data data;

    data.i = 10;
    printf("data.i : %d\n", data.i);    // 10

    data.f = 220.5;
    printf("data.f : %f\n", data.f);    // 220.500000

    strcpy(data.str, "C Programming");
    printf("data.str : %s\n", data.str);    //C Programming

    return 0;
}
View Code

在这里,所有的成员都能完好输出,因为同一时间只用到一个成员

参考

菜鸟教程

 

posted @ 2021-07-14 12:17  凌逆战  阅读(285)  评论(0编辑  收藏  举报