小白学习笔记(结构体)

声明结构体变量并调用:

格式:

struct 结构体类型名称 结构体变量名;

举例:

//声明结构体变量
int main() {
    //struct 结构体类型名称  结构体变量名;
    struct student stu1;
    struct cat cat1;


    return 0;
}

调用结构体:

格式:

结构体变量名.成员名 = 常量或变量值 ;

举例:

//声明结构体变量
int main() {
    //struct 结构体类型名称  结构体变量名;
    struct student stu1;
    struct cat cat1;
//调用:结构体变量名.成员名 = 常量或变量值;
    stu1.id = 1;
  //  stu1.name = "Tom"//报错,针对于数组,不能重新赋值
    strcpy(stu1.name,"Tom");//正确的方式,并没有给数组重新赋值
    stu1.gender = 'M';
    stu1.age = 18;
    strcpy(stu1.address,"北京市海淀区中关村");
    printf("id = %d,gender = %c,age = %d,address = %s\n",stu1.id,stu1.gender,stu1.age,stu1.address);


    return 0;
}

调用的其他写法:

方式2:

#include <stdio.h>
struct Cat {
    char *name;
    int age;
    char gender;

};
int main() {
    struct Cat cat1 ={"小黄",2,'M'};
    printf("name = %s,age = %d,gender = %c",cat1.name,cat1.age,cat1.gender);//赋值的顺序要和声明的顺序一致
    struct Cat cat2 = {"小黑"};//没有赋值的成员,会被赋值为默认值
    
    return 0;
}

方式3:

#include <stdio.h>
struct Cat {
    char *name;
    int age;
    char gender;

};
int main() {
    //struct 结构体名 结构体变量 = {.成员1 = xxxx,.成员2 = yyyy……}
    struct Cat cat = {.gender = 'F',.age = 2,.name = "小回"};//这种赋值方法不用和声明的顺序一致
    printf("name = %s,age = %d,gender = %c\n",cat.name,cat.age,cat.gender);//剩下没赋值的成员会被默认赋值为默认值


    return 0;
}

方式4:

//声明类型的同时定义变量
struct sudent {
    char *name;
    int age;
}stu1,stu2;

stu1.name = "tomo";
stu1.age = 18;


return 0;

方式5:

struct{//匿名结构体
    char *name;
    int age;
}t1,t2;

t1.name = "tomo";
t1.age = 18;

方式6(就按这种写):

//在格式4的基础上融合格式3的写法
struct person {
    char *name;
    int age;
}per1 ={.name ="jack",.age = 18},per2 ={.name = "luci",.age = 21};

方式7:

typedef     struct student {
        char *name;
        int age;
    }Emp;
    Emp stu1,stu2;
    return 0;
}

说明:

在创建一个结构体变量后,需要给成员赋值,在没有给成员赋值的情况下使用,打印的值就是垃圾数据,可能导致程序异常终止

不同结构体变量的成员是独立的,互不影响,一个结构体变量成员的修改,不影响另一个

练习:

//练习,编程创建一个box结构体,在其中定义三个成员表示一个立方体的长,宽,高,长宽高可以通过控制台输入
#include <stdio.h>
struct box {
    int length;
    int width;
    int height;
}box1;
//定义一个函数,获取立方体的体积
int box_volume(struct box box1) {
    return box1.length * box1.width * box1.height;
}

int main() {
    struct box box1;
    printf("输入长宽高:");
    scanf("%d%d%d",&box1.length,&box1.width,&box1.height);
    printf("%d",box_volume(box1));


}

结构体嵌套:

结构体的成员可以是基本数据类型,也可以是数组,指针,结构体等,如果结构体的成员是另一个结构体,这就构成了结构体嵌套

举例1:

//结构体练习
#include<stdio.h>
#include<string.h>
struct Name {
    char firstName[20];
    char lastName[20];
};

struct student {
    int age;
    struct Name name;
    char gender;
}stu1 = {
    .age = 18,
    .name.firstName = "wang",
    .name.lastName = "zhu",
    .gender = 'M'};
int main() {
    printf("%d\n%s %s\n%c\n",stu1.age,stu1.name.firstName,stu1.name.lastName,stu1.gender);


    return 0;
}

举例2:

//测试结构体嵌套
struct Birthday {
    int year;
    int month;
    int day;
}birthday;

struct employee {
    char   name[30];
    int age;
    char gender;
    struct Birthday birthday;
}employee1 ={.age = 18,.name = "wangzhu",.gender = 'M',.birthday = {.year = 2020,.month = 4,.day = 8}};

int main(){
    printf("%d\n%s\n%c\n%d %d %d\n",
        employee1.age,employee1.name,employee1.gender,employee1.birthday.year,employee1.birthday.month,employee1.birthday.day);


    return 0;
}

(要打印的数据的名字要写全,一路.过去)

举例3:单链表结构的定义

//单链表结构的定义:
struct node {
    int data;//节点中保存的数据
    struct node *next;
}node1 = {.data = 10},node2 = {.data = 20,.next = &node1},node3 = {.data = 30,.next = &node2},node4 = {.data = 40,.next = &node3};

node4的指针指向node3,node3的指针指向2,2的指针指向1,1的指针指向NULL值,由此可以得到一个单链表,并且通过node3可以遍历整个列表

举例4:二叉树结构的定义:

//二叉树结构的定义
typedef struct node {
    int data;//这里默认是int型
    struct node *left;//对应左边
    struct node *right;//对应右边
};

结构体占用空间:

结构体占用的存储空间,不是各个属性存储空间之和。

为了计算效率,C语言的内存占用空间一般来说都是int型存储空间的整数倍,如果int类型的是4字节,那struct的就必须是4的整数倍

举例:

//结构体占用空间
#include <stdio.h>
struct A {
    char a;
    int b;
    //int c;
};
int main() {
    printf("%d",sizeof(struct A));//输出为8
    return 0;
}

struct A 存储的空间不是5个字节,而是8个字节,a和b之间有3个字节空洞

如果加上int c那么就是12个字节。计算的时候是把每个属性都自动补齐成4个字节。

结构体变量的赋值操作:

同类型的结构体变量可以使用赋值运算符=,赋给另一个变量,比如:

struct 1 = struct 2;//假设struct 1和struct 2已定义为同类型的结构体变量

这时会生成一个新的副本,系统会分配一块新的内存,大小与原来的变量相同,把每个属性的复制过去。也就是说,结构体变量的传递机制是值传递,而不是地址传递

举例1;

#include <stdio.h>
struct Car {
    char name [10];
    double price;
}a = {.name = "BMW", .price = 2342.02};
int main() {
    struct Car b = a;
    printf("%p\n",&a);//00007ff664fba000
    printf("%p\n",&b);//0000003e1fbffa90
    printf("%p\n",a.name);//00007ff664fba000
    printf("%p",b.name);//0000003e1fbffa90


    return 0;
}

也就是说,结构体的复制是将结构体变量完全复制一份,给新的结构体变量。两个结体相互独立,修改一个不会影响另一个。

举例2:

例1有一个前提,就是struct结构的属性必须定义成字符数组,才可以复制数据。如果属性定义成字符指针,结果又不一样。

struct Car {
    char *name;
    double  price;
}a = {.name = "BMW",.price = 2342.02};//这里的.name就是一个指针变量,指向BWM

int main() {
    struct Car b = a;//b复制后的b.name也是一个指针变量,指向的也是BWN
    printf("%p\n",&a);//00007ff6e831a000
    printf("%p\n",&b);//000000b7ce1ff8e0//此时的b就是一个新的结构体,地址与a不同
    
    printf("%p\n",a.name);//00007ff6e831b000
    printf("%p\n",b.name);//00007ff6e831b000//
//因为BWM只有一个,所以a.name和b.name指向的是同一个地址,所以地址相同
    return 0;
}

上例中,name属性变成了一个字符指针,这时a赋值给b,此时b变量仍然是新开辟的内存空间,但是,a和b的name成员保存的指针相同,也就是说两个属性共享一个BWM

在C语言中,相同的字符串,常量通常只会保存一份,所以那些字符串常量共享相同的内存。当你声明多个指针变量,并让他们指向相同的字符串常量时,他们实际上都指向相同的内存空间,字符串常量的共享,有利于减小程序的内存占用

注意:

C语言没有提供比较两个自定义数据结构是否相等的方法,无法用比较运算符(比如:==和!=)来比较两个数据结构是否相等或不等

结构体数组:

对比结构体和数组:

结构体数组的声明:

结构体数组:数组元素是结构体变量而构成的数组。先定义结构体类型,然后用结构体类型定义数组变量。

方式1:先声明1个结构体类型,再用此类型定义结构体数据

格式:结构体类型 数组名[数组长度];

举例:

#include <stdio.h>
struct person {
    char *name;
    int age;

};
//对应方式2
struct student {
    int age;
    char name[30];
}stu[3] = {{.age = 18,.name = "susi"},{.age = 25,.name = "sara"},{.age = 28,.name = "lusiya"}};

int main() {
    //1.如何创建结构体数组
    //1.1  方式1:声明结构体之后,声明结构体类型
    struct person person[3];
    //1.2  方式2:声明结构体的同时,创建结构体数组


    //2.如何给结构体数组的成员赋值
    //2.1  方式1:对应1.1的方式1
    struct person per[3] = {
        {.name = "ton",.age = 18},
        {.name = "som",.age = 24},
        {.name = "sara",.age = 26}};

    //2.2  方式2:对应1.2的方式2
    //看上面

    //3.如何调用
    //方式1:使用数组角标方式
    printf("%s\n",per[2].name);

    //方式2:使用指向数组或数组元素的指针(具体看下节内容)
    struct person *p = &per[1];
    printf("%s\n",p->name);


    return 0;
}

练习1:

//输入一个班级的学生信息(包括id,name,gender,score),并把学习成绩超过全班平均成绩的学生找出,输出这部分学生的姓名和成绩
#include <stdio.h>
#define  NUM 2
int i;
struct student {
    int id;//学号
    char name[20];
    char gender;//性别
    int score;
};
double total = 0,average;
int main() {
    struct student stu[NUM];
    for ( i = 0;i < NUM;i++){
        printf("请输入学生信息\n学号:  \n"
               "姓名:  \n"
               "性别:  \n"
               "成绩:  \n");
        scanf("%d %s %c %d",&stu[i].id,&stu[i].name,&stu[i].gender,&stu[i].score);
        total += stu[i].score;

    }
    average = total / NUM;
    for (int j =0;j < NUM;j++) {
        if (stu[j].score > average) {
            printf("%s  %d\n",stu[j].name,stu[j].score);
        }
    }



    return 0;
}

练习2:记录投票

#include <stdio.h>
#include <string.h>
#define  N 10
struct Person {
    char name[30];
    int count;
}perarr[3] = {{.name = "zhang3",.count = 0},{"li4",0},{"wang5",0}};
char vote_name[30];//记录投票给谁
int main() {
    for (int i = 0; i < N; i++) {
        printf("你要给谁投票:(zhang3,li4,wang5)\n");
        scanf("%s",&vote_name);
        for (int j = 0;j < 3;j++) {
            if (strcmp(vote_name,perarr[j].name)==0) {
                perarr[j].count++;
            }
        }
    }
    for (int i =0;i < 3;i++) {
        printf("%s %d\n",
        perarr[i].name,perarr[i].count);
    }
    
    return 0;
}

补充一个知识点:

在C语言中,strcmp 函数用于比较两个字符串。这个函数的原型如下:

int strcmp(const char *s1, const char *s2);
  • s1s2 是要比较的两个字符串。
  • 如果 s1s2 相同,strcmp 返回 0。
  • 如果 s1 小于 s2(即 s1 在字典序上先于 s2),strcmp 返回一个负数。
  • 如果 s1 大于 s2(即 s1 在字典序上后于 s2),strcmp 返回一个正数。

因此,在条件判断语句中使用 == 0 是为了检查两个字符串是否完全相同。例如:

if (strcmp(vote_name, "zhang3") == 0) {
    // 如果 vote_name 等于 "zhang3",则执行这里的代码
}

在这个例子中,strcmp(vote_name, "zhang3") == 0 意味着 vote_name 字符串与 "zhang3" 完全相同。如果它们相同,则返回 0,条件为真,进入 if 语句块执行相应的代码。
总结一下,== 0 的意思是检查 strcmp 函数的结果是否为 0,即两个字符串是否相等。

结构体指针:

结构体指针:指向结构体变量的指针(将结构体变量的起始地址存放在指针变量中)

具体应用场景:1.可以指向单一的结构体变量

2.可以用作函数的参数

3.可以指向结构体数组

定义结构体指针变量的格式:

struct 结构体名 *结构体指针变量名;

举例:

#include <stdio.h>
struct person {
    char name[20];
    int age ;

}per = {.name = "wangzhu",.age = 19};
int main() {
    struct person *p = &per;
    printf("%s  %d",(*p).name,(*p).age);



    return 0;
}

注意输出里要带个括号

结构体传参:

结构体传参的方式是值传递.

举例:

#include <stdio.h>
struct student {
    char *name;
    int age;
    char *gender;

};
void addage(struct student per) {
    per.age =per.age+1;
}

void addage1(struct student *per) {
    (*per).age = (*per).age+1;
}
int main() {
    struct student p1 ={.name = "TOM",.age = 18,.gender = "male"};
    addage(p1);
    printf("%d\n",p1.age);//输出还是为18

    struct student p2 ={.name = "TOM",.age = 18,.gender = "male"};
    addage1(&p2);
    printf("%d",p2.age);//此时的输出为19,因为是结构体指针,可以直接修改本体

    return 0;
}

练习:(方式1)

#include <stdio.h>
//1.编写一个dog结构体,包含name(char[10],age[int],weight[double]属性)
struct Dog {
    char name[10];
    int age;
    double weight;
};
//2.编写一个函数say,返回字符串,字符串返回信息中包含所有成员值
char *say(struct Dog dog) {
     static char info[100];//使用static确保info在say函数结束后不会被销毁

    //引入一个新的方法,通过sprintf可以把所有信息都存入info里面
    sprintf(info,"name:%s,age:%d,weight:%.2lf",dog.name,dog.age,dog.weight);
    return info;

}
//在main函数中,创建Dog结构体变量,调用say函数,将调用结果打出
int main() {
    struct Dog dog = {.name = "Tom",.age = 18,.weight = 121};
    //或者:strcpy(dog.name,"Tom");给数组赋值
    say(dog);
    printf("%s",say(dog));

    return 0;
}

方式2:用结构体指针

//结构体指针练习
#include <stdio.h>
//1.编写一个dog结构体,包含name(char[10],age[int],weight[double]属性)
struct Dog {
    char name[10];
    int age;
    double weight;
};
//2.编写一个函数say,返回字符串,字符串返回信息中包含所有成员值
char *say(struct Dog *dog) {
     static char info[100];//使用static确保info在say函数结束后不会被销毁

    //引入一个新的方法,通过sprintf可以把所有信息都存入info里面
    sprintf(info,"name:%s,age:%d,weight:%.2lf",(*dog).name,(*dog).age,(*dog).weight);
    //(*dog).weight = 232;
    return info;

}
//在main函数中,创建Dog结构体变量,调用say函数,将调用结果打出
int main() {
    struct Dog dog = {.name = "Tom",.age = 18,.weight = 121};
    //或者:strcpy(dog.name,"Tom");给数组赋值
    say(&dog);
    printf("%s",say(&dog));

    return 0;
}

推荐使用方式2,因为这样可以直接修改要输出的东西。方式1只是复制了一份原有的。

练习2:方法1


#include <stdio.h>
#define N 3
//1.编写游客结构体,包含姓名,年龄,应付门票
struct victory {
    char name[50];
    int age;
    int ticket;
}vic[N];
//2.编写函数ticket(),根据年龄段决定能够购买的门票的价格并输出。年龄 >= 18,门票为20,其他情况免费
int ticket(struct victory  vic) {
    if (vic.age >= 18) {
        vic.ticket = 20;
    }else{vic.ticket = 0;}
    return vic.ticket;
}
//3可以循环从控制台输入名字和年龄,打印门票的收费情况,如果名字输入n,则退出程序
int main() {
    for (int i = 0;i < N;i++ ) {
        printf("请输入游客信息:(name,age)\n");
        char info[100];
        scanf("%s  %d",&vic[i].name,&vic[i].age);
        vic[i].ticket = ticket(vic[i]);
        sprintf(info,"name:%s  age:%d  ticket:%d",vic[i].name,vic[i].age,vic[i].ticket);
        printf("%s\n",info);
        if (vic[i].name[0] == 'n') {
            break;
        }
    }

    return 0;
}

方法2:使用结构体指针

#include <stdio.h>
#define N 3
//1.编写游客结构体,包含姓名,年龄,应付门票
struct victory {
    char name[50];
    int age;
    int ticket;
}vic[N];
//2.编写函数ticket(),根据年龄段决定能够购买的门票的价格并输出。年龄 >= 18,门票为20,其他情况免费
int ticket(struct victory  *vic) {
    if ((*vic).age >= 18) {
        (*vic).ticket = 20;
    }else{(*vic).ticket = 0;}
    return (*vic).ticket;
}
//3可以循环从控制台输入名字和年龄,打印门票的收费情况,如果名字输入n,则退出程序
int main() {
    for (int i = 0;i < N;i++ ) {
        printf("请输入游客信息:(name,age)\n");
         char info[100];
        scanf("%s  %d",&vic[i].name,&vic[i].age);
        vic[i].ticket = ticket(&vic[i]);
        sprintf(info,"name:%s  age:%d  ticket:%d",vic[i].name,vic[i].age,vic[i].ticket);
        printf("%s\n",info);
        if (vic[i].name[0] == 'n') {
            break;
        }
    }

    return 0;
}

操作符:->

举例:

struct student {
    char name[20];
    int age;
    char gender;
};
int main() {
    //打印结构体信息
    struct student s = {"wangzhu", 18, 'm'};
    //方式1,为结构成员访问操作符
    printf("%s %d %c", s.name, s.age, s.gender);

    //方式2:
    struct student *p = &s;
    printf("%s %d %c", (*p).name, (*p).age, (*p).gender);

    //方式3:方式3 简洁一点
    printf("%s %d %c", p->name, p->age, p->gender);

    
    return 0;
}

总结:

指向结构体数组的指针:

举例:

struct person {
    int id;
    char name[20];
    
};
int main() {
    struct person p1;
    struct person arr[10];

    struct person *p,*q;

    p = &p1;
    q = arr;
    
    return 0;
}

举例:

#include <stdio.h>
struct student {
    int id;
    char name[20];
    char gender;
}stu[3] = {
    {1001,"Tom",'M'},
    {1002,"Jerry",'M'},
    {1003,"sara",'F'}
};
int main() {
    //方式1
     for (int i = 0; i < 3; i++) {
         printf("%d %s %c\n",stu[i].id,stu[i].name,stu[i].gender);
     }

    //方式2:
    struct student *p =stu;//将stu数组的首地址赋值给指针变量
    for (int i = 0; i < 3; i++) {
        printf("%d %s %c\n",p[i].id,p[i].name,p[i].gender);
        //方式3:
        printf("%d %s %c\n",p->id,p->name,p->gender);
    }





    return 0;
}

结构体在数据结构中的应用:

声明结点的结构体:

链表是一种动态的数据存储结构(并非固定长度),链表的基本单位是结点(node),同一链表的所有结点具有相同的数据结构,而结点使用结构体类型进行定义

一个链表节点包括数据域和指针域:数据域存储需要处理的数据,指针域存储下一个结点的位置

单链表结构的结点定义如下:

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

或者:

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

声明二叉树的结点:结构体

struct BTNode {
    int data;
    struct BTNode *left;
    struct BTNode *right;
};

或者

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

声明结点变量:

方式1:

//声明单链表节点
struct Node node3;
LNode node2;
//声明二叉树结点:
struct BTNode node3;
BTNode1 node4;

方式2:就用这种方式

BTNode *node5;
node5 = (BTNode *)malloc(sizeof(BTNode));

malloc模板:当要制作一个新结点时,只要把结点结构型的名称填入括号中的类型即可

类型 *p;

p = (类型 *)malloc(sizeof(类型));//将右边创建的结点的地址赋值给p

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值