2天学完C语言-------三 结构体和联合体

1. 结构体的定义和使用

     结构体(Structure)和联合体(Union)是C语言中用于组织和存储不同类型数据的复合类型。

1. 结构体(Structure)

  • 结构体是一种用户自定义的数据类型,可以将不同类型的数据组合在一起形成一个新的数据类型。
  • 结构体通过使用`struct`关键字来定义,结构体内部可以包含多个成员(可以是不同类型的数据)。
  • 结构体的每个成员可以通过成员运算符`.`来访问。
  • 结构体可以用来表示一些复杂的数据结构,例如,描述一个人的信息,一个图形的属性等。

示例:

   struct Rectangle {
       int length;
       int width;
   };

   int main() {
       struct Rectangle rect;
       rect.length = 5;
       rect.width = 3;
       int area = rect.length * rect.width;
       printf("Area: %d\n", area);
       
       return 0;
   }

 2. 联合体(Union)

  • 联合体也是一种用户自定义的数据类型,与结构体类似,但不同的是联合体的所有成员共享同一块内存空间。
  • 联合体通过使用`union`关键字来定义,联合体中所有成员的大小总和等于最大成员的大小。
  • 联合体的所有成员可以共享同一块内存,但一次只能存储其中一个成员的值。
  • 联合体适用于需要节省内存空间的情况,例如,可以用于表示不同类型的变量,但同时只有一个变量是有效的。

示例:

   union Data {
       int i;
       float f;
       char c;
   };

   int main() {
       union Data data;
       
       data.i = 10;
       printf("i: %d\n", data.i);
       
       data.f = 3.14;
       printf("f: %.2f\n", data.f);
       
       data.c = 'A';
       printf("c: %c\n", data.c); 
       return 0;
   }

       结构体和联合体是C语言中强大的数据组织和管理工具,可以帮助我们更好地组织和操作数据。结构体适用于需要组合多种不同类型的数据的情况,而联合体适用于需要在同一块内存中共享不同类型数据的情况。

2. 结构体的嵌套和指针

       结构体的嵌套和指针是C语言中常见且强大的特性,它们可以用来表示复杂的数据结构,并允许在程序中对其进行灵活的操作。

1. 结构体的嵌套(Nested Structure)

- 结构体的成员可以是其他结构体,这被称为结构体的嵌套。

- 结构体的嵌套可以用于表示更复杂的数据结构,使得数据的组织更灵活且具有层次结构。

- 可以通过使用成员运算符`.`来访问嵌套结构体的成员。

示例:

 struct Date {
       int day;
       int month;
       int year;
   };

   struct Person {
       char name[20];
       int age;
       struct Date birthdate;
   };
int main() {
       struct Person person;
       
       strcpy(person.name, "Alice");
       person.age = 25;
       person.birthdate.day = 10;
       person.birthdate.month = 5;
       person.birthdate.year = 1998;
       
       printf("Name: %s\n", person.name);
       printf("Age: %d\n", person.age);
       printf("Birthdate: %d-%d-%d\n", person.birthdate.day, person.birthdate.month, person.birthdate.year);
       
       return 0;
   }

2. 结构体指针(Structure Pointer):

     结构体指针是指向结构体的指针变量,它可以用来直接操作结构体变量,特别是当需要动态分配内存时非常有用。

可以使用`->`运算符来通过结构体指针访问结构体的成员。

结构体指针可以传递给函数,允许在函数中对结构体的成员进行修改。

示例:

struct Student {
       char name[20];
       int age;
       float gpa;
   };

   int main() {
       struct Student student;
       struct Student* ptrStudent;
       
       ptrStudent = &student;
       
       strcpy(ptrStudent->name, "Bob");
       ptrStudent->age = 20;
       ptrStudent->gpa = 3.5;
       
       printf("Name: %s\n", ptrStudent->name);
       printf("Age: %d\n", ptrStudent->age);
       printf("GPA: %.2f\n", ptrStudent->gpa);
       
       return 0;

结构体的嵌套和指针使得我们能够更好地组织和操作复杂的数据结构。结构体的嵌套可以创建具有层次结构的数据模型,而结构体指针允许我们直接和动态地操作结构体变量。这些特性使得程序具有更高的灵活性和扩展性。

3. 联合体的概念和应用

        联合体(Union)是C语言中一种特殊的数据类型,与结构体类似,但所有成员共享同一块内存空间。联合体的概念和应用如下:

        联合体(Union)是C语言中一种特殊的数据类型,与结构体类似,但所有成员共享同一块内存空间。联合体的概念和应用如下:

1. 联合体的概念

  • 联合体是一种用户自定义的数据类型,通过`union`关键字定义。
  • 联合体的成员共享同一块内存空间,大小等于最大成员的大小,并且只能存储其中一个成员的值。
  • 联合体的不同成员可以存储不同类型的数据,但同时只有一个成员是有效的。
  • 联合体的内存布局允许节省空间,因为在任何给定时间点只有一个成员被使用。

示例:

union Data {
       int i;
       float f;
       char c;
   };

2. 联合体的应用

  • 联合体可以用于节省内存空间,特别是在需要存储不同类型但仅使用其中一种类型数据的情况。
  • 联合体常用于实现变体数据类型,其中不同的成员表示数据的不同状态或类型。
  • 联合体可以用于处理数据类型转换或对数据的不同解释,例如将一个整数与浮点数按不同的方式解释。
  • 联合体还可以与结构体嵌套使用,用于创建更复杂的数据结构。

示例:

union Variant {
       int intValue;
       float floatValue;
       char stringValue[20];
   };

   struct Record {
       int type;          // 记录类型,例如1表示整数,2表示浮点数,3表示字符串
       union Variant data;  // 记录数据
   };

int main() {
       struct Record record;
       
       record.type = 1;    // 整数类型
       record.data.intValue = 10;
       printf("Integer value: %d\n", record.data.intValue);
       
       record.type = 2;    // 浮点数类型
       record.data.floatValue = 3.14;
       printf("Float value: %.2f\n", record.data.floatValue);
       
       record.type = 3;    // 字符串类型
       strcpy(record.data.stringValue, "Hello");
       printf("String value: %s\n", record.data.stringValue);
       
       return 0;
   }

联合体在某些情况下可以提供灵活且高效的数据处理方式,特别是当需要处理不同类型的数据但同时只使用其中一种类型的时候。使用联合体可以节省内存空间,并能够进行数据类型转换和数据解释等操作。然而,需要注意联合体的使用方式,以避免出现潜在的问题,如访问未初始化的成员或同时使用多个成员导致数据混淆。

3. 结构体对齐和填充规则

在32位平台上:

  • char:1字节
  • short:2字节
  • int:4字节
  • long:4字节
  • float:4字节
  • double:8字节
  • 指针(32位):4字节

在64位平台上:

  • char:1字节
  • short:2字节
  • int:4字节
  • long:8字节
  • float:4字节
  • double:8字节
  • 指针(64位):8字节

      在C语言中,结构体对齐(Struct Alignment)和填充(Padding)是为了保证结构体的成员在内存中以有效和高效的方式进行访问。结构体对齐和填充规则如下:

在常见的32位和64位平台上,以下是常见数据类型的大小:

1. 结构体成员对齐规则

  • 结构体的成员通常按照其自身大小进行对齐。例如,char类型通常对齐到1字节边界,int类型通常对齐到4字节边界(根据编译器和平台的不同可能有所变化)。
  • 结构体的成员顺序对结构体的对齐方式有影响。通常,将较大的成员放在较前面可以避免填充空间(尽量使结构体的大小和成员的总大小一致)。
  • 通过在结构体定义中使用特定的对齐属性,可以修改结构体成员的对齐方式。

2. 结构体的填充规则

  • 结构体成员之间可能存在填充字节,以保证对齐规则得到满足,这被称为填充。
  • 填充字节的大小取决于结构体成员的类型和位置,以及编译器和平台的规则。
  • 填充字节的存在可能导致结构体的大小变大,并占用额外的存储空间。
  • 可以使用`#pragma pack`指令或特定的编译器选项来修改或禁用填充字节。

示例:

#include 
struct Example {
    char c;
    int i;
    char d;
};
int main() {
    struct Example ex;
    
    printf("Sizeof(struct Example) = %zu\n", sizeof(struct Example));
    
    printf("Offset of c: %zu\n", offsetof(struct Example, c));
    printf("Offset of i: %zu\n", offsetof(struct Example, i));
    printf("Offset of d: %zu\n", offsetof(struct Example, d));
    
    return 0;
}

在上面的示例中,假设 char 类型的对齐是 1 字节,int 类型的对齐是 4 字节。根据对齐规则和填充规则,结构体中的成员会自动对齐和填充。

因此,结构体 Example 的大小将是 12 字节(1字节的char+c填充+4字节的int+1字节的char+d填充)。

请注意,结构体的对齐和填充规则在不同的编译器和平台上可能会有所不同。为了代码的可移植性,请确保在编写结构体时考虑对齐和填充的影响,并且遵循编译器和平台的规则。

4. 结构体与指针的应用

结构体与指针的应用广泛,可以带来很多好处。下面是一些结构体与指针的常见应用:

1. 动态内存分配

      使用结构体指针可以方便地进行动态内存分配,由于结构体通常包含多个成员,使用指针可以节省内存并提高效率。

示例:

struct Person {
       char name[20];
       int age;
   };
   struct Person* createPerson() {
       struct Person* p = malloc(sizeof(struct Person));
       return p;
   }

2. 数据结构

结构体和指针常用于构建各种数据结构,如链表、树等。

示例:

struct Node {
       int data;
       struct Node* next;
   };

   struct Node* createNode(int data) {
       struct Node* newNode = malloc(sizeof(struct Node));
       newNode->data = data;
       newNode->next = NULL;
       return newNode;
   }

3. 函数参数传递

       通过将结构体指针作为函数参数,可以避免复制整个结构体的开销,提高性能,同时可以在函数内修改结构体的值。

示例:

struct Point {
       int x;
       int y;
   };
   
   void movePoint(struct Point* p, int dx, int dy) {
       p->x += dx;
       p->y += dy;
   }

4. 动态多态

      结构体指针可以用于实现动态多态的概念,通过在结构体中包含函数指针,可以根据实际对象的类型来调用相应的函数。

示例:

 struct Shape {
       void (*draw)(void);
   };
   
   void drawCircle() {
       printf("Drawing a circle\n");
   }
   
   void drawRectangle() {
       printf("Drawing a rectangle\n");
   }
   int main() {
       struct Shape circle;
       circle.draw = drawCircle;
       circle.draw();  // 调用drawCircle函数
       
       struct Shape rectangle;
       rectangle.draw = drawRectangle;
       rectangle.draw();  // 调用drawRectangle函数
       
       return 0;
   }

      结构体与指针相结合的应用可以提供更灵活、高效和模块化的编程方式。它们可以用于管理动态内存、构建复杂的数据结构、传递大型结构体作为函数参数,并实现动态多态等功能。在使用结构体指针时,需要确保合适地分配和释放内存,以避免内存泄漏和无效访问的问题。

5. 联合体的高级应用

联合体(Union)是一种特殊的数据类型,它允许在同一内存位置存储不同类型的数据。联合体的大小等于最大成员的大小。联合体的高级应用主要包括以下几个方面:

1. 节省内存空间:联合体可以使用一块内存空间来存储不同类型的数据,这可以节省内存开销。通过合理设计联合体的成员,可以在不同情况下使用所需的空间最少。

2. 数据类型转换:联合体可以用于数据类型的转换。将不同类型的数据存储在一个联合体中,可以通过访问相同的内存位置来解释数据的含义。这在某些特定的场景下非常有用,例如将二进制数据解析为不同的数据类型。

3. 利用共享内存:联合体可以用于多个线程或进程间共享内存。不同线程或进程可以通过读写不同的联合体成员来访问和处理共享内存数据。

4. 模拟多态行为:联合体可以模拟多态行为,允许在不改变函数签名的情况下传递不同类型的参数。通过联合体,可以将不同类型的数据封装在一个统一类型的参数中,然后根据实际需要进行访问和处理。

需要注意的是,使用联合体需要小心,因为它们容易导致数据类型的混淆和错误解读。确保在使用联合体时,合理设置成员类型、顺序和访问方式,以避免潜在的错误。

        这些是联合体的一些高级应用,希望能帮助大家更好地理解联合体的潜在用途。如果存在还有其他问题或者不同见解,麻烦请在评论区留言,共同讨论。

你可能感兴趣的:(2天学完C语言,c语言,开发语言,c++,笔记,学习,算法)