C语言结构体详解



一、结构体应用情景

C语言中的结构体(struct)是一种复合数据类型,也可以理解为自定义的一种数据类型,可以将不同类型的数据组合成一个单一的实体。
结构体在C语言中通常用于以下情景:

  1. 封装相关数据:当需要将多个相关的数据项组合成一个单一的逻辑单元时,结构体非常有用。例如,如果你想存储一个学生的信息,包括学号、姓名和年龄,这些数据项虽然类型不同,但都描述了学生的属性,因此可以封装在一个结构体中。
  2. 数据库替代:由于C语言本身不支持数据库操作,结构体可以用来在内存中存储和操作大量数据,作为数据库的一种替代方法。
  3. 高级数据结构实现:结构体可以包含指向自己类型的指针,这使得它们可以用于实现链表、树等更高级的数据结构。
  4. 嵌入式开发:嵌入式开发过程中很多时候需要接受判断同一模块的多个参数,已经控制多个动作的是实现,使用结构体非常有利于操作
  5. 函数参数和返回值:结构体可以作为函数的参数和返回值传递,这样可以在函数之间传递复杂的数据结构。

二、结构体的定义、访问、赋值

1. 定义

结构体通过struct关键字来定义。它的一般形式如下:

注意:每一个数据类型的定义用分号结尾,整个结构体的定义后也有一个分号。类似普通数据类型定义语句。

struct 结构体名称
{
    数据类型 成员1;
    数据类型 成员2;
    ...
};

例如,定义一个表示书籍的结构体:

struct Book 
{
    char title[50];
    char author[50];
    char rate;
    int pages;
    float price;
};

在这个例子中,Book是结构体的名称,而title、author、pages和price是结构体的成员。

一旦定义了结构体类型,就可以创建该类型的变量,声明结构体变量的方法合声明普通数据类型的方法相同。

struct Book MyBook;
struct Book HisBook,HerBook;

其实也可以在定义的同时就声明结构体变量。

struct Book 
{
    char title[50];
    char author[50];
    char category;
    int pages;
    float price;
}MyBook;

2. 访问结构体成员

可以使用点运算符(.)来访问结构体的成员, 如果成员是字符串,可以借助字符串函数strcpy()

strcpy(myBook.title, "C语言程序设计");
myBook.pages = 300;
myBook.price = 29.99;

3. 赋值

1. 在声明的同时赋值

赋值顺序和结构体内变量定义顺序需要一致。

struct Book Mybook = 
{
     "C Programming",
     "Nikita",
     ‘A',
     300,
     500.00
};

2. 对结构体内对象逐个赋值

对整形,浮点型和字符型可以用运算符 ‘=’ 赋值,而字符串即字符数组需要借助strcpy() 赋值

strcpy(Mybook.title, "C Programming");
strcpy(Mybook.author, "Nikita");
Mybook.category = 'A';
Mybook.pages = 300;
Mybook.price = 500.00;

整个程序示例

#include <stdio.h>
#include <string.h>
int main()
{
    struct Book 
    {
        char title[50];
        char author[50];
        char category;
        int pages;
        float price;
    };

    /*声明的同时整体赋值,此种形式需要按顺序
    struct Book Mybook = 
    {
        "C Programming",
        "Nikita",
        'A',
        300,
        500.00
    };
    */

    struct Book Mybook;

    // 使用 strcpy() 将字符串字面量赋给字符数组
    strcpy(Mybook.title, "C Programming");
    strcpy(Mybook.author, "Nikita");
    Mybook.category = 'A';
    Mybook.pages = 300;
    Mybook.price = 500.00;


    printf("Book title: %s\n", Mybook.title);
    printf("Book author: %c\n", Mybook.category);
    printf("Book pages: %d\n", Mybook.pages);

    return 0;
}

三、结构体数组

也可以创建结构体的数组来存储多个结构体变量,例如

#include <stdio.h>

struct book
{
    char title[50];
    char author[50];
    float value;
};

int main()
{
    struct book library[100];
    printf("Please enter the book title.\n");
    gets(library[0].title);
    printf("Please enter the author.\n");
    gets(library[0].author);
    printf("Please enter the value.\n");
    scanf("%f", &library[0].value);

    printf("The book title is %s.\n", library[0].title);
    printf("The author is %s.\n", library[0].author);
    printf("The value is %f.\n", library[0].value);
    
    return 0;
}

以上代码把library声明为一个内含100个元素的数组。数组的每个元素都是一个book类型的数组。数组名library本身不是结构名,它是一个数组名,该数组中的每个元素都是struct book类型的结构变量。因此,library[0]是第1个book类型的结构变量,library [1]是第2个book类型的结构变量,以此类推。如图所示。
在这里插入图片描述

标识结构数组的成员

为了标识结构数组中的成员,可以采用访问单独结构的规则:在结构名后面加一个点运算符,再在点运算符后面写上成员名。如下所示:

library[0].value;	//第1个数组元素与value相关联
library[4].title;	//第5个数组元素与title.相关联

注意,数组下标紧跟在library后面,不是成员名后面:

library.value[2]; //错误
library [2].value; //正确

那么下面的表达式代表什么意思呢?

library[2].title[4];

这是library数组第3个结构变量(library[2]部分)中书名的第5个字符(title [4]部分)。
以最后,总结一下:

library;	//一个 book 结构的数组
library[2];		//一个数组元素,该元素是book结构
library[2].title;	//一个char数组(library[2]的title成员)
library[2].title [4]	//数组中library[2]元素的title成员的一个字符

四、结构体指针

可以使用指针来访问结构体变量,这在函数参数传递中特别有用:

struct Book *ptr;
ptr = &myBook;
printf("书名: %s\n", ptr->title);

在C语言中使用结构体指针是一种高效的方式来访问和操作结构体变量。下面是如何在结构体中使用指针的步骤:

1. 定义结构体指针

首先,定义一个结构体和它的指针类型。例如:

struct Point
{
    int x;
    int y;
};

2. 创建结构体指针

声明一个指向结构体的指针:

struct Point *ptr;

3. 初始化结构体指针

指针需要指向一个结构体实例:

struct Point point = {10, 20};
ptr = &point;

4. 通过指针访问结构体成员

  • 使用箭头运算符(->)来访问结构体成员:
//pointer->memberName
printf("X coordinate: %d\n", ptr->x);
printf("Y coordinate: %d\n", ptr->y);

使用(*)和(.)来访问结构体成员:

//(*pointer).memberName
printf("X coordinate: %d\n", (*ptr).x);
printf("Y coordinate: %d\n", (*ptr).y);

注意:. 的优先级高于*,(pointer)两边的括号不能少。如果去掉括号写作pointer.memberName,那么就等效于*(pointer.memberName),这样意义就完全不对了。

箭头运算符(->)

在C语言中,箭头运算符(->)用于通过结构体指针访问结构体的成员。当你有一个指向结构体的指针时,可以使用箭头运算符来访问该结构体的任何成员。这个运算符是由减号(-)和大于号(>)组合而成的。

下面是箭头运算符的详细讲解。
箭头运算符的使用: 假设你有一个结构体Point和一个指向Point的指针ptr:

struct Point 
{
    int x;
    int y;
};
struct Point *ptr;

如果你想通过指针ptr来访问Point结构体中的x和y成员,你应该这样做:

ptr->x = 10;
ptr->y = 20;

这里,ptr->x和ptr->y分别访问了指针ptr所指向的结构体中的x和y成员。

箭头运算符与点运算符的区别:
• 点运算符(.)用于直接通过结构体变量访问其成员。
• 箭头运算符(->)用于通过指向结构体的指针访问结构体的成员。

例如,如果你有一个结构体变量point和一个指向结构体的指针ptr:

struct Point point;
struct Point *ptr = &point;

你可以使用点运算符来访问point的成员:

point.x = 10;
point.y = 20;

相对地,你可以使用箭头运算符来通过ptr访问相同的成员:

ptr->x = 10;
ptr->y = 20;

箭头运算符的优先级: 箭头运算符的优先级非常高,仅次于点运算符。这意味着在表达式中,箭头运算符会在大多数其他运算符之前被计算。

5. 结构体指针与函数

结构体指针可以作为函数参数传递,这样可以避免复制整个结构体:

void printPoint(struct Point *p) 
 {
    printf("X coordinate: %d\n", p->x);
    printf("Y coordinate: %d\n", p->y);
}

6. 动态分配结构体

使用malloc为结构体动态分配内存,并使用指针来访问:

ptr = (struct Point *)malloc(sizeof(struct Point));
if (ptr != NULL)
{
    ptr->x = 10;
    ptr->y = 20;
}

内存分配部分会在内存那节详细讲解,记得使用free来释放动态分配的内存。


五、结构体嵌套

在C语言中,结构体可以嵌套其他结构体,这是通过在一个结构体中定义另一个结构体类型的成员来实现的。这种方式可以帮助你创建更复杂的数据结构,例如,表示一个公司的员工和部门的关系。下面是一个如何在结构体中嵌套其他结构体的例子:
首先,定义一个表示日期的结构体:

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

然后,定义一个表示个人信息的结构体,其中包含Date结构体作为成员:

struct Person 
{
    char name[50];
    struct Date birthday; // 嵌套结构体
    float height;
    float weight;
};

在这个例子中,Person结构体有一个名为birthday的成员,它的类型是之前定义的Date结构体。
初始化嵌套结构体: 当创建一个Person类型的变量时,可以这样初始化嵌套的Date结构体:

struct Person person1 = {
    "张三",
    {1990, 1, 1}, // 初始化嵌套的Date结构体
    170.5,
    65.2
};

访问嵌套结构体成员:

要访问嵌套结构体的成员,可以使用两个点运算符:

printf("出生日期: %d-%d-%d\n", person1.birthday.year, person1.birthday.month, person1.birthday.day);

参考资料:C语言中文网
C primer plus(第六版)


  • 46
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值