自学内容网 自学内容网

c语言结构体

c语言结构体(struct)

C语言的结构体(struct)从本质上讲自定的数据类型,只不过这种数据类型比较复杂,是由int、float、char等基本数据类型组成。是一种聚合数据类型。
在实际开发中,我们可以将一组类型不同,但是用来描述同一件事物的变量放到结构体中。比如:学生有名字、年龄、身高、成绩等多个属性。使用结构体之后,就不需要零散定义多个变量,可以将他们都放到结构体中。

结构体的定义

基本格式

struct 结构体名{
结构体所要包含的数据类型
};

//基本定义
struct Teacher
{
    char name[180];
    int age;
    char *job;
};
//1.定义多个结构体变量用逗号隔开
struct Teacher
{
    char name[180];
    int age;
    char *job;
}tea1,tea2;
//2.也可以在下面用变量定义 格式 struct 结构体名 变量名
struct Teacher tea3,tea4;

注意:在定义的时候最好给个初始值不然就会出现乱码或者在结构体创建的时候就开始定义

初始赋值为0或者NULL(两种)

struct Teacher
{
    char name[180];
    int age;
    char *job;
};

int main()
{
    struct Teacher tea2 = {.name = "", .age = 0, .job = NULL}; // 初始化
    printf("%s %d %s\n", tea2.name, tea2.age, tea2.job);
}


------------------------------------------------------------
struct Teacher
{
    char name[180];
    int age;
    char *job;
} stu1 = {"", 0, NULL}, stu2; //写跟不写初始值是一样

//两种写法一样
struct Teacher
{
    char name[180];
    int age;
    char *job;
} stu3, stu4 = {"sss", 18, "fffff"};
int main()
{
    printf("%s %d %s\n", stu1.name, stu1.age, stu1.job); //"\0" 0 NULL
    printf("%s %d %s\n", stu2.name, stu2.age, stu2.job);   // "" 0 NULL
    printf("%s %d %s\n", stu3.name, stu3.age, stu3.job); //"" 0 NULL
    printf("%s %d %s\n", stu4.name, stu4.age, stu4.job);   // "sss", 18, "fffff"
    struct Teacher tea2; //但是这里不给初始值就会乱码
    printf("1=%s %d %s\n", tea2.name, tea2.age, tea2.job); // 乱码 0 乱码
    
}

清空结构体让其赋初始值

...//头文件
struct Teacher
{
    char name[180];
    int age;
    char *job;
};

int main()
{
    struct Teacher tea2;
    memset(&tea2, 0, sizeof(tea2)); // 清零
    // 现在可以安全地读取这些值
    printf(" %s %d %s\n", tea2.name, tea2.age, tea2.job); //"\0" 0 NULL  因为清空了所以有的初始值
    return 0;
}

匿名结构体特殊

因为匿名结构体没有名字,所以后续没有办法再给这个结构体定义结构体变量,只能在定义结构体的时候创建变量。不能使用struct 结构体名 结构体变量名,因为结构体名为空所以这种方式没法定义


//错误写法无用结构体
struct 
{
int a;
float b;
double c;
char d;
};
//正确写法无用结构体
struct 
{
int a;
float b;
double c;
char d;
}node1,node2;
node1.a=100;

结构体成员的赋值和获取

结构体变量名.成员名;
注意:字符串赋值不能直接赋值要使用strcpy来赋值,或者直接使用地址赋值,使用地址赋值要记得先开辟空间

struct Student
{
    char name[30]; // 学生姓名
    int age;
    char *job; // 职业
} stu2;        // 结构体变量stu2

int main()
{
    struct Student stu1; // 结构体变量stu1
    stu1.age = 18;
    // stu1.name="小明";//数组不能这样赋值
    strcpy(stu1.name, "小明");
    // 字符串指针可以这么赋值但是需要开辟空间
    stu1.job = (char *)malloc(30); // 规范这样些更规范
    stu1.job = "数学";
    printf("%s %d %s \n", stu1.name, stu1.age, stu1.job); // 小明 18 数学
    return 0;
}

结构体数据大小

结构体大小不是结构体中所有变量大小的和

补齐

struct A
{
int a;//4
char b;//1
};//8
printf("%d \n", sizeof(struct A)); //8

寻址

struct B
{
int data;//4
char a;//4
float b;//4  补齐--》8
int *c;//8
int e;//4
};//32

在内存中,假设结构体 B 的起始地址为 0x1000,则各字段的地址如下:
data: 0x1000 (4 bytes)
a: 0x1004 (1 byte)
padding: 0x1005, 0x1006, 0x1007 (3 bytes) 补齐
b: 0x1008 (4 bytes) 12是8的倍数所以补到16
c: 0x100C (8 bytes) 24
e: 0x1014 (4 bytes) 28
padding: 0x1018, 0x1019, 0x101A, 0x101B (4 bytes)
整体是28, 28 不是8的倍数,这时候就需要寻址找到8的倍数 32

struct A
{
    int g;  // 4
    int a;  // 4  这里4+4=8可以是8的倍数
    int *c; // 8 (在64位系统)
    int e;  // 4
    int d;  // 4
}; // 4+4+8+4+4= 24

重要性
这种合理的寻址和填充方式,不仅提高了内存访问的效率,还避免了潜在的对齐错误,使得结构体在内存中更易于处理。了解结构体的寻址对于高效编程和优化数据结构非常重要

总结就是一个个往下找不够下个类型的倍数自动补齐,结束时不够最大字节倍数自动寻址

结构体数组

#define LEN 2
struct Student
{
char *name;
int num;//学号
char sex;//M W
float score[3];
char job[20];//专业
}clsz[LEN];

int main()
{
//struct Student stu1;

for (int i = 0; i < LEN; ++i)
{
printf("请输入%d位学生信息\n",i+1 );
printf("请输入姓名:");
char *addname=(char *)malloc(20);
scanf("%s",addname);
clsz[i].name=addname;
printf("请输入人学号:");
scanf("%d",&clsz[i].num);
getchar();//获取回车这个字符
printf("请输入性别:");
scanf("%c",&clsz[i].sex);
printf("请输入语文成绩:");
scanf("%f",&clsz[i].score[0]);
printf("请输入数学成绩:");
scanf("%f",&clsz[i].score[1]);
printf("请输入英语成绩:");
scanf("%f",&clsz[i].score[2]);
printf("请输入专业:");
scanf("%s",clsz[i].job);
}

for (int i = 0; i < LEN; ++i)
{
printf("%s %d %c %.1f %.1f %.1f %s\n",clsz[i].name,clsz[i].num,clsz[i].sex,clsz[i].score[0],clsz[i].score[1],clsz[i].score[2],clsz[i].job );
}
return 0;
}

结构体嵌套

结构体内部的结构体,必须要定义结构体变量才能使用

struct Structb
{
    float c;
    double d;
};

struct Structa
{
    char *a;
    int b;
    struct Structb sb;
} sa;
----------------------------------------------
//这两种写法效果一样只不过一个抽出来而已
struct Structa
{
    char *a;
    int b;
    struct Structb
    {
        float c;
        double d;
    } sb;
} sa;
--------------------------------------------------
int main()
{
    char *pre = (char *)malloc(30); // 动态为数组开辟空间
    if (NULL == pre)
    {
        return 1;
    }
    sa.a = "llll";
    sa.b = 100;
    sa.sb.c = 1.000;
    sa.sb.d = 2.000;
    printf("%s %d %f %lf", sa.a, sa.b, sa.sb.c, sa.sb.d);
    free(pre);
    return 0;
}

结构体嵌套就是一个链表式的嵌套,取值跟赋值只能单个赋值,也可以复制整个结构体

结构体指针

struct 结构体名 * 变量名;

struct Structa
{
    char *a;
    int b;

} sa = {"张三", 18}, *sb = &sa; // 初始设置指向

int main()
{
    // 定义sc指向sa的地址
    struct Structa *sc = &sa;
    printf("%s %d", (&sa)->a, sa.b); // 张三 18  &(*sa).a-->(&sa)->a, 普通数据类型不能使用->, 必须是指针
    printf("%s %d", sb->a, (*sb).b); // 张三 18   箭头跟点的两种不同写法
    printf("%s %d", (*sc).a, sc->b); // 张三 18
    return 0;
}

结构体访问成员名

(*sb).b;

第一种写法的.点优先级高于*,()不能省略掉,如果省略*sb.b;意义不对
sb->a
第二种写法:->是一个运算符,通过结构体指针直接获取结构体成员

定义结构体指针

定义结构体指针可以在初始化的就直接定义,也可以在后面重新定义,sb,sc就是两种定义方式
结构体变量名和数组变量名不同,数组名在表达式中会被转换成数组的指针,而结构体名不会,无论在任何的表达式中他表示的是整个集合的本身。要想取得结构体变量的地址,必须在前面加上&。

结构体数组指针

struct Structa
{
    int a;
    int b;
    char c;
} arrz[2] = {
    {10, 20, 'z'},
    {30, 40, 'a'}},
  *arr;

int main()
{

    for (int i = 0; i < 2; i++)
    {
        printf("%d %d %c\n", arrz[i].a, arrz[i].b, arrz[i].c);
        printf("%d %d %c\n", (*(arrz + i)).a, (*(arrz + i)).b, (*(arrz + i)).c);
    }
    for (arr = arrz; arr < arrz + 2; arr++)
    {
        printf("%d %d %c\n", arr->a, arr->b, arr->c);       // 10 20 z
        printf("%d %d %c\n", (*arr).a, (*arr).b, (*arr).c); // 10 20 z
    }
------------------------------------------------------------------
//相当于
    arr = arrz;
    for (int i = 0; i < 2; i++)
    {
        printf("%d %d %c\n", arr->a, arr->b, arr->c);       // 10 20 z
        printf("%d %d %c\n", (*arr).a, (*arr).b, (*arr).c); // 10 20 z
        arr++;
    }
    return 0;
}

结构体指针作为函数参数

结构体变量名代表整个集合数据的本身,作为函数参数时传递的是整个集合数据,也就是所有的成员。而不像数组会被编译转换为一个指针。如果结构体成员较多,传递的时间和空间开销就会很大。所以最好的办法就是用结构体指针,这是由实参向形参传递一个地址,速度非常快。并且如果不传递指针在函数内部进行操作赋值的话,在函数运行结束也会随之释放,出现bug

#include <stdio.h>
struct Student
{
char name[10];
int age;
float score;
};


//函数结束之后,形参stu空间会被释放
void inputStudent(struct Student stu)
{
printf("请输入姓名:");
scanf("%s",stu.name);
printf("请输入年龄:");
scanf("%d",&stu.age);
printf("请输入分数:");
scanf("%f",&stu.score);
}

void inputStudent2(struct Student *stu)
{
printf("请输入姓名:");
scanf("%s",stu->name);
printf("请输入年龄:");
scanf("%d",&stu->age);
printf("请输入分数:");
scanf("%f",&stu->score);
}

void showStudent(struct Student stu)
{
printf("%s %d %f\n",stu.name,stu.age,stu.score );
}

void showStudent2(struct Student * stu)
{
printf("%s %d %f\n",stu->name,stu->age,stu->score );
}


int main()
{

struct Student stu1;
//inputStudent(stu1);
inputStudent2(&stu1);
//showStudent(stu1);
showStudent2(&stu1);
return 0;
}

原文地址:https://blog.csdn.net/weixin_48255917/article/details/142911227

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!