C语言基础(结构体)

news/2024/12/17 8:24:44/

文章目录

    • 构造类型
      • 数据类型
      • 结构体
        • 结构体的定义
        • 结构体变量的定义
        • 结构体变量的使用
        • 结构体数组
      • 构造体类型
        • 构造体数组
          • 案例
        • 构造体指针
        • 构造体成员访问
        • 构造体类型求大小
        • 共用体/联合体类型

构造类型

数据类型

1.基本类型/基础类型

  • 整数类型
    • 短整型:short/short int(2字节)
    • 整型(默认):int(4字节)
    • 长整型:long/long int(8字节(64位系统))
    • 长长整型:long long/long long int(16字节)
  • 浮点类型
    • 单精度:float(4字节)
    • 双精度:double(8字节)
    • 长双精度:long double(16字节(64位系统))
  • 字符型:char(1字节)

2.指针类型

​ ①数据类型:int*,char*,float*等(8字节)

​ ②void*:任意数据类型的指针(万能指针)(8字节)

3.空类型

  • void:没有返回值或没有形参(不能定义变量)

4.自定义类型/构造类型

  • 结构体类型:struct
  • 共用体/联合体类型:union
  • 枚举类型:enum

注意:整数型和字符型分有符号和无符号,默认是有符号,有符号可以省略关键字signed

结构体

结构体的定义
  • 定义:自定义数据类型的一种,关键字struct,结构体类型的变量可以存储多个不同数据类型的数据

  • 定义格式:

struct 结构体名
{数据类型1 成员名称1;//结构体中的变量叫做成员数据类型2 成员名称2;...
}

注意:结构体中定义的变量,我们称之为成员变量

  • 格式说明

    • 结构体名:合法的标识符,建议单词的首字母大写(所谓的结构体名,就是自定义类型的类型名称)
      • 数据类型n:C语言支持的所有类型(包括函数,函数在这里用函数指针表示)
      • 成员名称:合法的标识符,就是合法的标识符,变量的命名标准
      • 数据类型n成员名称n:类型于定义变量,定义了结构体中的成员
  • 注意:

    • 结构体在定义的时候,成员不能赋值

举例:

struct Cat
{int age = 5;//错误,结构体定义的时候,成员不能赋值double height;//正确void (*run)(void);//正确
}
  • 常见的定义格式
    • 方式1:常规定义(命名结构体,只定义类型)–推荐写法
struct Student
{int num;//学号char name[20];//姓名char sex;//姓名int age;//年龄char address[100];//家庭住址void (*info)(void);//信息输出(函数指针)
}	
  • 方式2:定义匿名结构体(常用于作为其他结构体的成员使用)
struct Dog
{char *name;//姓名int age;//年龄struct //结构体中嵌套的结构体不能有名字,故被称作匿名结构体{int year;//年int month;//月int day;//日}
}

注意:定义匿名结构体的同时必须定义结构体变量,否则编译报错,结构体可以作为另一个结构体的成员

总结:

  • 结构体可以定义在局部位置,也可以定义在全局位置(用的较多)

  • 全局位置的结构体名和局部位置的结构体名可以相同,就近原则(和普通变量的定义同理)

  • 结构体类型的使用:

利用结构体类型定义变量,定义数组;结构体类型的使用与基本数据类型的使用类似

结构体变量的定义
  • 三种形式定义结构体变量

结构体变量也称为结构体的实例

  • 第1种

①先定义结构体(自定义数据类型)

②然后使用

struct 结构体名 变量名(实例);

举例:

//先定义结构体(自定义数据类型)
struct A
{int a;char b;
};
//后定义结构体变量(使用自定义数据类型)
struct A x;
struct A y;
  • 第2种

在定义结构体的同时,定义结构体变量

struct 结构体名
{数据类型1 数据成员1...
}变量列表

举例:

struct A
{int a;char b;
}x,y;struct A z;//也可以

此时定义了一个结构体A,x和y是这个结构体类型的变量

  • 第3种(不推荐)

定义匿名结构体的同时,定义结构体变量

struct 
{int a;char b;
}x,y;struct
{int a;char b;
}z;

此时定义了一个没有名字的结构体(叫做匿名结构体);x,y是这个结构体类型的变量

  • 匿名结构体:–弊大于利(尽量少用)
    • 优点:少写一个结构体名称
    • 缺点:只能使用一次,定义结构体类型的同时必须定义变量
    • 应用场景:
      • 当结构体类型只需要使用一次,并且定义类型的同时定义变量
      • 作为其他结构体的成员使用
  • 定义结构体的同时,定义结构体变量初始化
struct Cat
{int age;char color[20];
}cat;
  • 结构体成员部分初始化时,大括号{}不能省略
  • 结构体成员,没有默认值,是不确定的数
结构体变量的使用
  • 结构体变量访问结构体成员
    • 格式:
结构体变量名.成员名字;

可以通过访问给成员赋值(存数据)

可以通过访问获取成员的值(取数据)

  • 结构体变量未初始化,结构体的成员值随机(不确定)

  • 结构体变量在定义时,可以初始化

    • 建议用大括号标明数据的范围
    • 结构体成员初始化,可以部分初始化,部分初始化时一定要带大括号标明数据的范围
  • 案例:

#include <stdio.h>/**全局结构体(数据类型)
*/struct Dog 
{char* name;//姓名int age;//年龄char sex;//M:公,W:母void (*eat)(void);//吃饭
};void eat()
{printf("狗狗在吃狗粮\n");
}/**先定义再初始化*/
void fun1()
{//定义结构体变量struct Dog dog;//给结构体变量赋值,其实就是给其成员赋值dog.name = "旺财";dog.age = 5;dog.eat = eat;//访问结构体变量,其实就是访问其成员printf("%s,%d,%c\n",dog.name,dog.age,dog.sex);//访问函数dog.eat();
}/*定义同时初始化*/
void fun2()
{//定义结构体变量并初始化struct Dog dog = {"招财",23,'M'};//初始化时,保留大括号//修改成员的值dog.name = "金宝";printf("%s,%d,%c\n",dog.name,dog.age,dog.sex);
}int main(int argc,char *argv[])
{fun1();fun2();return 0;
}

在这里插入图片描述

结构体数组
  • 什么时候需要结构体数组

比如:我们需要管理一个学生对象,只需要定义一个struct Student july

假如:我们需要管理多个学生对象,此时就需要一个结构体数组struct Student student[29];

  • 四种形式定义结构体数组
    • 第1种:先定义结构体类型,然后定义结构体变量,最后将变量存储到结构体数组
//定义一个学生类型的结构体(定义数据类型)
struct Student
{char *name;//学生姓名int age;	//学生年龄float scores[3];//三门课程的成绩
}//定义结构体对象(实例)(使用结构体定义变量)
struct Student zhangsan = {"张三",21,{89,99,78}};
struct Student Lisi = {"李四",22,{56,89,54}}; //定义结构体数组
struct Student students[3] = {zhangsan,Lisi}
  • 第2种:定义结构体类型,然后定义结构体数组并初始化
//定义一个学生类型的结构体
struct Student
{int id;	//学生编号char *name; //名字int age;	//年龄float scores[3];//成绩
};//定义结构体数组并初始化
struct Student student[3] = {{1,"张三",21,{89,23,45}},//注意:这里赋值的顺序需要跟成员在结构体中的顺序一致{2,"李四",20,{56,23,78}},
}; 

第3种:定义结构体类型的同时定义结构体并完成初始化

//定义一个学生类型的结构体
struct Student
{int id;	//学生编号char *name; //名字int age;	//年龄float scores[3];//成绩
} students[3] = {{1,"张三",21,{89,23,45}},//注意:这里赋值的顺序需要跟成员在结构体种的顺序一致{2,"李四",20,{56,23,78}},
};
  • 第4种:定义结构体类型的同时定义结构体数组,通过索引给结构体成员赋值
//定义一个学生类型的结构体
struct Student
{int id;	//学生编号char *name; //名字int age;	//年龄float scores[3];//成绩
} stus[3] ;//赋值
stus[0].id = 1;
stus[0].name = "张胜男";
stus[0].age = 21;
stus[0].scores[0] = 98;

小贴士:

结构体数组名访问结构体成员:

格式:结构体数组名->成员名(一般针对函数)

案例:

#include <stdio.h>/*
*全局结构体(数据类型)
*/struct Dog 
{char* name;//姓名int age;//年龄char sex;//M:公,W:母void (*eat)(void);//吃饭
};void eat()
{printf("狗狗在吃狗粮\n");
}/**先定义再初始化*/
void fun1()
{//定义结构体变量struct Dog dog;//给结构体变量赋值,其实就是给其成员赋值dog.name = "旺财";dog.age = 5;dog.eat = eat;//访问结构体变量,其实就是访问其成员printf("%s,%d,%c\n",dog.name,dog.age,dog.sex);//访问函数dog.eat();
}/*定义同时初始化*/
void fun2()
{//定义结构体变量并初始化struct Dog dog = {"招财",23,'M'};//初始化时,保留大括号//修改成员的值dog.name = "金宝";printf("%s,%d,%c\n",dog.name,dog.age,dog.sex);
}void info(char *str)
{printf("%s\n",str);
}void fun3()
{//定义学生类型的结构体struct Student{int id;char *name;int age;float scores[3];void (*info)(char*);};//定义结构体数组并初始化struct Student stu1 = {1,"张三",21,{56,89,45}};struct  Student stu2 = {2,"李四",20,{60,79,65}};stu1.info = info;stu2.info = info;//定义结构体数组并初始化struct  Student stus[] = {stu1,stu2};//遍历结构体数组//计算结构体数组的大小int len = sizeof(stus)/sizeof(stus[0]);struct Student *p = stus;//遍历for(;p < stus+len;p++){printf("%d,%s,%d ",p->id,p->name,p->age);float* scs = p->scores;for(;scs< p->scores+3;scs++)printf("%.2f ",*scs);printf("\n");p->info(p->name);}printf("\n");
}int main(int argc,char *argv[])
{fun1();fun2();fun3();return 0;
}

在这里插入图片描述

构造体类型

构造体数组
案例

需求:对候选人得票的统计程序。设有3个候选人,每次输入一个得票的候选人名字,要求最后输出个人的得票结果

#include <stdio.h>
#include <string.h>/***定义一个候选人类型的构造体(对象)
*/struct Person
{char name[20];//候选人名字int count; //候选人票数
};/**定义候选人数组,并初始化
*/struct Person persons[3] = {{"钢铁侠",0},{"蜘蛛侠",0},{"金刚狼",0}
};int main(int argc,char *argv[])
{//定义循环变量int i = 0;int j = 0;//创建一个char数组,用来存储控制台输入的候选人名字char leadername[20];//使用一个循环完成10次投票(模拟投票)for(int i = 0;i < 10;i++){printf("请输入您要投票的候选人姓名:\n");scanf("%s",leadername);//给被投票的候选人+1票for(int j = 0;j < 3;j++){//判断两个字符串的结果是否相同if(strcmp(leadername,persons[j].name)==0){persons[j].count++;}}}printf("\n投票结果: \n");//数组法for(int i = 0;i < 3;i++){printf("%s:%d\n",persons[i].name,persons[i].count);}//指针法/* struct Person *p = persons;for(int i = 0; i< 3;i++){printf("%s:%d\n",(persons+i)->name,(persons+i)->count);}*/return 0;
}

在这里插入图片描述

构造体指针
  • 定义:结构体类型的指针变量指向结构体变量或者数组的起始地址
  • 语法:
struct 结构体名 *指针变量列表;
  • 案例:
struct Dog
{char name[20];int age;
}
struct Dog dog = {"富贵",5};
struct Dog *p = &dog;
构造体成员访问
  • 结构体成员的访问
    • 结构体数组名访问结构体成员
      • 格式:结构体数组名->成员名字;

举例:

printf("%s:%d\n",persons->name,persons->count);
  • 结构体成员访问符号

    • .:左侧是结构体变量(结构体对象/结构体实例),也可以叫做结构体对象访问成员符,右侧是结构体成员
    • ->:左侧是一个指针,也可以叫做结构体指针访问成员符,右侧是结构体成员
  • 访问结构体成员有两种类型,三种方式:

  • 1.类型1:通过结构体对象访问成员

struct Stu
{int id;char name[20];
}stu;//结构体变量stu.name;
  • 第2种:指针解引用间接访问成员
struct Stu
{int id;char name[20];
}stu;//结构体变量
struct Stu *p = &stu;
p->name;
  • 结构体数组中元素的访问
struct Stu
{int id;char name[20];float scores[3];
} stus[3] = {{1,"张三",{67,77,88}},{2,"李四",{90,89,45}},{3,"王五",{65,12,47}}
};//取数据--下标法
printf("%s,%f",stus[1].name,stus[1].scores[1]);//取数据--指针法
printf("%s,%f",stus->name,stus->scores[2]);//张三,88
printf("%s,%f",(stus+1)->name,(stus+1)->scores[1]);//李四,89
printf("%s,%f",(*(stus+1)).name,(*(stus+1)).scores[1]);//李四,89

小贴士:

结构体是自定义数据类型,它是数据类型,用法类似于基本类型的int;

结构体数组它是存放结构体对象的数组,类似于int数组存放int数据;

基本类型数组怎么用,结构体数组就怎么用—>可以遍历,可以作为形式参数,也可以做指针等;

  • 结构体类型的使用案例

结构体可以作为函数的返回类型、形式参数

#include <stdio.h>
// 定义结构体
struct Cat
{
char *name;// 姓名
int age;// 年龄
char color[20];// 颜色
}
// 1.结构体类型作为形式参数void test1(struct Cat c);
// 2.结构体类型作为形式参数,结构体类型作为返回值类型
struct Cat test2(struct Cat c);
// 3.结构体数组作为形式参数
void test3(struct Cat cats[],int len);
// 4.结构体数组作为形式参数,结构体指针作为返回值数据类型
struct Cat *test4(struct Cat cats[],int len);
构造体类型求大小
  • 规则:字节对齐(默认,数据在内存中存储在其类型大小的整数倍上)
    • 首先保证结构体中的成员存储在自身的对齐边界(类型大小的整数倍)
    • 在满足1的条件下,最终大小要满足最大成员类型所占存储单元的整数倍
  • 为什么要使用字节对齐

节省内存,提高访问效率

  • 在GNU标准中,可以在定义结构体时,指定对齐规则:
_attribute_((packed));//结构体所占内存大小是所有成员所占内存大小之和
--attribute_((aligned(n)));//设置结构体占n个字节,如果n比默认值小,n不起作用;n必须是2的次方
  • 柔性数组
struct St
{char arr[0];
}

柔性数组不占有结构体的大小

案例:

#include <stdio.h>int main(int argc,char *argv[])
{struct Cat{int id;char name[20];char arr[0];//柔性数组不占用结构体的大小char sex __attribute((aligned(2)));//1--2//设置结构体占n个字节,}__attribute__((packed));//结构体所占内存大小是所有成员所占内存大小之和printf("%ld",sizeof(struct Cat));//默认字节对齐(28) / 使用packed后(25)return 0;
}
  • 快速计算结构体大小

https://blog.csdn.net/weixin_72357342/article/details/131135555 https://blog.csdn

共用体/联合体类型
  • 定义:使几个不同的变量占用同一段内存的结构。共用体按定义中需要存储空间最大的成员来分配存储单元,其他成员也是用该空间,它们的首地址是相同的
  • 定义格式:
union 共用体名称
{数据类型 变量名;---共用体成员数据类型 变量名;....
};
  • 共用体定义和结构体类似:
    • 可以有名字,也可以匿名
    • 共用体在定义时也可以定义共用体变量
    • 共用体在定义时也可以初始化成员
    • 共用体也可以作为形参和返回值类型使用
    • 共用体也可以定义共用体变量

也就是说,结构体的语法,共用体都支持

  • 注意:
    • 共用体弊大于利,尽量少用
    • 共用体变量在某一时刻只能存一个数据,并且也只能取出一个数
    • 共用体和结构体都是自定义数据类型,用法类似于基本数据类型
      • 共用体可以是共用体的成员,也可以是结构体的成员

http://www.ppmy.cn/news/1555813.html

相关文章

【Linux金典面试题(下)】41道Linux金典面试问题+详细解答,包含基本操作、系统维护、网络配置、脚本编程等问题。

大家好&#xff0c;我是摇光~&#xff0c;用大白话讲解所有你难懂的知识点 之前写了一篇关于 python 的面试题&#xff0c;感觉大家都很需要&#xff0c;所以打算出一个面试专栏。 【数据分析岗】Python金典面试题 这个专栏主要针对面试大数据岗位、数据分析岗位、数据运维等…

如何用AI技术解锁一键生成PPT的新边界?

当代社会PPT早已不仅是职场人用来开会的工具&#xff0c;更是承载创意、传递信息的桥梁。但面对紧张的时间与无尽的需求&#xff0c;一份吸睛的PPT却往往成为无数人头疼的难题。难道我们真的要在熬夜改幻灯片中度过职业生涯的黄金岁月&#xff1f;答案或许藏在“AI生成PPT”这一…

【操作系统】文件管理

1. 文件和文件系统 文件系统的管理功能是将其管理的程序和数据通过组织为一系列文件的方式实现的。 文件则是指具有文件名的若干相关元素的集合。 元素通常是记录&#xff0c;而记录又是一组有意义的数据项的集合。 可见&#xff0c;基于文件系统的概念&#xff0c;可以把数…

阿里云效流水线部署springboot项目

1.新建流水线,选中部署到阿里云ECS 2.配置流水线源 3.java构建上传,其他都可以默认,打包路径填写jar包路径.(切记不要填写多个打包路径,否则后面会解压到别的目前,暂未解决) 4.主机部署,部署脚本是解压和执行脚本 脚本说明 deploy.sh、compose.yaml、Dockerfile这三个脚本要提…

Leetcode1847:最近的房间

题目描述&#xff1a; 一个酒店里有 n 个房间&#xff0c;这些房间用二维整数数组 rooms 表示&#xff0c;其中 rooms[i] [roomIdi, sizei] 表示有一个房间号为 roomIdi 的房间且它的面积为 sizei 。每一个房间号 roomIdi 保证是 独一无二 的。 同时给你 k 个查询&#xff…

【SpringBoot】配置文件

在 Spring 项目中&#xff0c;我们必须自行创建 Spring 的配置文件&#xff0c;通常命名为 "spring-config.xml" 。但在 SpringBoot 中无需自行创建&#xff0c;在 SpringBoot 项目创建时就存在了即 application.properties 文件。它的用处: 设置项目的启动端口设置数…

简单的Java小项目

学生选课系统 在控制台输入输出信息&#xff1a; 在eclipse上面的超级简单文件结构&#xff1a; Main.java package experiment_4;import java.util.*; import java.io.*;public class Main {public static List<Course> courseList new ArrayList<>();publi…

【Linux 进程间的通信】匿名管道

匿名管道是UNIX系统进程间通信&#xff08;IPC&#xff09;的一种基本形式&#xff0c;它允许具有血缘关系的进程之间进行数据传输。在Linux环境下&#xff0c;匿名管道是通过内核中的缓冲区实现的&#xff0c;这个缓冲区的大小是有限的&#xff0c;通常由操作系统决定。匿名管…