C语言中结构体详解

C语言中结构体详解

1.1 结构体的相关概念

结构体: 将多个相同或不同类型的数据存在一块连续的内存空间

说明:
  1. 结构体和数组一样,同属于符合类型的数据;
  2. 结构体和数组的区别:数组用于保存相同类型的数据,而结构体通常用于描述一个具体事务的属性,保存若干个不同/相同类型的数据;
  3. C语言的结构体和C++/Java/Python中的class类似;

1.2 结构体变量的定义和初始化

结构体定义方法:
struct tag { 
    member-list
    member-list 
    member-list  
    ...
} variable-list ;

    - tag 是结构体标签。
    - member-list 是标准的变量定义,比如 int i; 或者 float f,或者其他有效的变量定义。
    - variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。
定义结构体变量的方式:
  • 先声明结构体类型再定义变量名;
  • 在声明类型的同时定义变量
  • 直接定义结构体类型变量(无类型名)
示例:
#include <stdio.h>
#include <stdlib.h>

struct Student {
    int id;
    char stuName[10];
    char major[10];
    char sex;
};

void defStructs() {
    struct Student s1 = {
            1,
            "jack",
            "science",
            'm'
    };
    printf("id=%d name=%s,major=%s sex=%c", s1.id, s1.stuName, s1.major, s1.sex);
    //id=1 name=jack,major=science sex=m
}

//部分元素初始化:
//.property = value
结构体类型和结构体变量关系:
  • 结构体类型:指定了一个结构体类型,它相当于一个模型,但其中并无具体数据,系统对之也不分配实际内存单元;
  • 结构体变量:系统根据结构体类型(内部成员状况)为之分配空间;
注意:
  1. 定义结构体时,结构体的成员变量不能赋值;
结构体的初始化
  1. 如果通过结构体变量操作结构体成员,使用"点域"操作, 如:

    s1.name
    s1.age
    s1.sex
    ...
    
  2. 如果通过结构体地址操作结构体的成员变量,使用-> ;如:

    struct *Student s1;
    s1->name;
    s1->age;
    s1->major;
    ...
    
  3. 结构体初始化案例:

    #include <stdio.h>
    #include <string.h>
    
    struct Student {
        int id;
        char stuName[10];
        char major[10];
        char sex;
    };
    
    void getStructProperty() {
        struct Student s1;
        (&s1)->id = 2;
        strcpy((&s1)->stuName, "Tom");
        strcpy((&s1)->major, "English");
        (&s1)->sex = 'f';
        printf("id=%d name=%s,major=%s sex=%c", s1.id, s1.stuName, s1.major, s1.sex);
        //id=2 name=Tom,major=English sex=f
    }
    

1.3 结构体数组

是一个数组,数组的每一个元素都是结构体

例如:

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

struct Person {
    int id;
    char name[16];
    char sex[8];
};

void defArray() {
    struct Person pArr[3] = {
            {1, "Asus", "male"},
            {2, "Tom",  "male"},
            {3, "suse", "female"}
    };
    //获取属性
    printf("%s\n", pArr[0].sex); //male
    printf("%s\n", pArr[1].name); //Tom
    printf("%d\n", pArr[2].id); //3

    //遍历结构体数组
    for (int i = 0; i < sizeof(pArr) / sizeof(pArr[0]); i++) {
        printf("id=%d name=%s sex=%s\n",
               pArr[i].id, pArr[i].name, pArr[i].sex);
    }
}

int main() {
    defArray();
    return 0;
}

1.4 结构体嵌套

// STATEMENT : 结构体的嵌套

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

struct Person {
    char name[16];
    char sex[8];
};

struct Student {
    int No;
    char major[8];
    // 嵌套结构体Person
    struct Person person;
};

void defProperty() {
    struct Student student;
    (&student)->No = 1;
    strcpy((&student)->major, "c++");
    strcpy((&student)->person.sex, "male");
    strcpy((&student)->person.name, "Tom");

    printf("No=%d major=%s name=%s sex=%s",
           student.No, student.major, student.person.name, student.person.sex);
    //No=1 major=c++ name=Tom sex=male
}


int main() {
    defProperty();
    return 0;
}

1.5 结构体和指针

1.5.1 指向普通结构体变量的指针

如:

struct Student s1 = {1,"Tom","male"}

//定义结体指针变量
struct Student *pS1 = &s1;
//获取属性值:
s1->id;
s1->name;
s1->gender;

案例:封装函数,实现对结构体内容的copy

//封装函数,实现对结构体内容的copy
#include <stdio.h>
#include <string.h>

struct Employee {
    int No;
    char name[16];
    double salary;
};

void copy_struct_value(struct Employee *pEmployee, struct Employee *pEmployee1, int copy_size);

int main() {
    struct Employee e1 = {
            1,
            "Lucy",
            8888.76
    };
    struct Employee e2;
    copy_struct_value(&e1, &e2, sizeof(e1));
    printf("No=%d name=%s salary=%.3f",
           e2.No, e2.name, e2.salary);
}

void copy_struct_value(struct Employee *pEmployee, struct Employee *pEmployee1, int copy_size) {

    memcpy(pEmployee1, pEmployee, copy_size);
}
1.5.2 堆区结构体变量
示例:
// STATEMENT : 堆区结构体变量

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

struct Car {
    double price;
    double length;
    double width;
    char comment[16];
};

struct Car *get_cars(double width, double length, const char *comment, double price) {
    //在堆区定义结构体变量
    struct Car *car = malloc(sizeof(struct Car));
    car->length = length;
    strcpy(car->comment, comment);
    car->price = price;
    car->width = width;
    return car;
}

int main() {
    //传参接受堆区返回的内存地址
    struct Car *car = get_cars(1.8, 4.7, "good", 45.88);
    printf("length=%.2f width=%.2f comment=%s price=%.2f",
           car->length, car->width, car->comment, car->price);
    //length=4.70 width=1.80 comment=good price=45.88
    free(car);
    return 0;
}
实现思路:
  • 定义一个结构体
  • 定义一个返回堆区内存地址的函数
    • 函数内向堆内存申请一块空间,存放结构体;
    • 给结构体的成员变量做初始化操作
    • 初始化完成后,return这个存放结构体地址的指针;
  • main函数中调用上一步定义的函数,传输适当的参数,用一个指针变量接受返回的堆内存地址;
  • 输入打印
  • 释放堆区开辟的内存空间
1.5.3 结构体套一级指针
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct testA {
    double a;
    char *b;
};

int main() {
    struct testA *t = malloc(sizeof(struct testA));
    t->a = 3;
    /*strcpy(t->b, "afd");*/     //err,操作了野指针
    //需要给b单独开辟内存空间
    t->b = malloc(64);
    strcpy(t->b, "aaa");
    printf("a=%f b=%s", t->a, t->b);
    //a=3.000000 b=aaa
    free(t->b);
    free(t);
    return 0;
}
注意:
  1. 释放内存的顺序一定是从里往外释放,否则会造成内存泄漏;
  2. 给结构体testA开辟空间时,同时也给b开辟了空间,但是b没有指向,因此需要给b(指针变量)单独开辟一个空间,否则b为野指针;
  3. 如果变量名是一个结构体类型,使用.访问成员变量;如果变量类型为结构体指针,则使用->访问成员变量.
1.5.5 结构体普通变量做函数参数
// STATEMENT : 结构体普通变量做函数参数


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

//结构体类型的定义
struct stu {
    char name[50];
    int age;
};

//函数参数为结构体普通变量
void set_stu(struct stu tmp) {
    strcpy(tmp.name, "mike");
    tmp.age = 18;
    printf("tmp.name = %s, tmp.age = %d\n", tmp.name, tmp.age);
    //tmp.name = mike, tmp.age = 18
}

int main() {
    struct stu s = {0};
    set_stu(s); //值传递
    printf("s.name = %s, s.age = %d\n", s.name, s.age);//s.name = , s.age = 0
    return 0;
}
1.5.6 结构体数组名做函数参数
#include <string.h>
#include <stdio.h>

struct stu {
    char name[32];
    int age;
};

void set_stu_pro(struct stu pStu[5], int len);


void printInfo(struct stu pStu[5], int len);

int main() {
    struct stu stuList[5];
    int len = sizeof(stuList) / sizeof(stuList[0]);
    set_stu_pro(stuList, len);
    printInfo(stuList, len);
    //name = hahah  age = 0
    //name = hahah  age = 3
    //name = hahah  age = 6
    //name = hahah  age = 9
    //name = hahah  age = 12
}

void printInfo(struct stu pStu[5], int len) {
    for (int i = 0; i < len; i++) {
        printf("name = %s  age = %d\n", pStu[i].name, pStu[i].age);
    }
}


void set_stu_pro(struct stu pStu[5], int len) {
    for (int i = 0; i < len; i++) {
        pStu[i].age = i * 3;
        strcpy((char *) (pStu + i), "hahah");
    }
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]:C语言字节对齐问题详解提到了C语言的字节对齐问题。在结构体,为了提高内存访问的效率,编译器会对结构体进行字节对齐。这意味着结构体的成员在内存并不是紧凑排列的,而是按照一定的规则进行对齐。具体的对齐规则取决于编译器和编译选项。\[1\] 引用\[2\]:在C语言,可以使用宏offsetof来获取结构体成员相对于结构体开头的字节偏移量。这个宏非常有用,可以帮助我们计算出每个结构体成员相对于结构体开头的偏移字节数。通过这个宏,我们可以更好地理解结构体的内存布局。\[2\] 引用\[3\]:在C语言,指针和结构体的组合常常用于处理复杂的数据结构。指针可以指向结构体的成员,通过指针可以方便地对结构体进行操作。指针和结构体的组合可以实现更灵活的数据处理和内存管理。\[3\] 综上所述,C语言的指针结构体组合可以用于处理复杂的数据结构,而字节对齐问题则是在结构体为了提高内存访问效率而进行的优化。通过使用宏offsetof,我们可以更好地理解结构体的内存布局。 #### 引用[.reference_title] - *1* *3* [结构体指针,C语言结构体指针详解](https://blog.csdn.net/weixin_34069265/article/details/117110735)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [C语言结构体详解](https://blog.csdn.net/m0_70749276/article/details/127061692)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值