文章目录

  • ​​一. 为什么需要结构体​​
  • ​​二. 什么叫结构体​​
  • ​​三. 如何声明一个结构体​​
  • ​​1. 声明一个结构体类型​​
  • ​​四. 结构体类型变量的定义​​
  • ​​1. 第一种方式(先声明结构体类型再定义变量名)(推荐使用)​​
  • ​​2. 第二种方式(在声明的同时定义变量)​​
  • ​​3. 第三种方式(直接定义结构体类型变量)​​
  • ​​五. 怎么使用结构体变量​​
  • ​​1. 赋值和初始化​​
  • ​​2. 如何取出结构体变量中的每一个成员​​
  • ​​2.1 有三种等价的方式:​​
  • ​​2.2 引用结构体变量时注意:​​
  • ​​3. 结构体变量的运算​​
  • ​​4. 结构体变量和结构体指针变量作为函数参数传递问题​​
  • ​​5. 举例:动态构造存放学生信息的结构体数组​​
  • ​​6. 用typedef说明一种新类型​​

一. 为什么需要结构体

为了表示一些复杂的事物,而普通的基本类型无法满足实际要求

举例:

#include <stdio.h>

struct Student //定义了一个数据类型 ,用来模拟学生类型,数据类型的名字:struct Student
{
int age;
float score;
char sex;
}; //分号不能省略

int main(void)
{
struct Student st = {80, 66.6, 'F'}; //定义了一个变量
/* //学生一
int age;
float score;
char name[100];
//学生二
int age2;
float score2;
char name2[100];
*/
return 0;
}

二. 什么叫结构体

用户根据实际需要把一些基本类型数据组合在一起形成的一个新的复合数据类型,这个就叫结构体

三. 如何声明一个结构体

1. 声明一个结构体类型

struct 结构体标识名
{
类型名1 结构体成员名1;
类型名2 结构体成员名2;
...
类型名n 结构体成员名n;
}; //分号不能省

注:
结构体类型名不仅可以是简单的数据类型,也可以是结构体类型,当结构体说明中又包含结构体时,称为结构体的嵌套。

四. 结构体类型变量的定义

有三种方式:

1. 第一种方式(先声明结构体类型再定义变量名)(推荐使用)

#include <stdio.h>

//定义结构体的第一种方式
struct Student //定义了一个数据类型 ,用来模拟学生类型 ,只是定义了一个新的数据类型,没有定义变量
{ //结构体类型名为struct Student
int age;
float score;
char sex;
}; //分号不能省略

int main(void)
{
struct Student st = {80, 66.6, 'F'}; //定义了一个变量

return 0;
}

2. 第二种方式(在声明的同时定义变量)

//定义结构体的第二种方式 
struct Student2 //定义数据类型的同时,定义一个st2的变量名
{
int age;
float score;
char sex;
}st2;

3. 第三种方式(直接定义结构体类型变量)

//定义结构体的第三种方式 
struct //结构体省略了名字
{
int age;
float score;
char sex;
}st3; //定义的同时也定义了变量

五. 怎么使用结构体变量

1. 赋值和初始化

定义的同时可以整体赋值,
如果定义完之后,则只能单个的赋初值

举例:

#include <stdio.h>

struct Student
{
int age;
float score;
char sex;
};

int main(void)
{
struct Student st = {80, 66.6, 'F'}; //初始化 定义的同时赋初值
//定义完之后进行赋值(赋值只能单个单个赋值)
struct Student st2;
st2.age = 10;
st2.score = 88;
st2.sex = 'F';

printf("%d %f %c\n", st.age, st.score, st.sex);
printf("%d %f %c\n", st2.age, st2.score, st2.sex);

return 0;
}

2. 如何取出结构体变量中的每一个成员

2.1 有三种等价的方式:

  • 结构体变量名.成员名
  • 指针变量名->成员名 (主要使用)
  • (*结构体指针变量名).成员名

2.2 引用结构体变量时注意:

  • 结构体变量不能作为一个整体而对其进行任何操作,只能对结构体变量中的各个成员分别进行输入和输出等操作。
  • 如果结构体的某个成员本身又是一个结构体类型,可以使用若干个成员运算符“.”,一级一级的找到最低一级成员,只能对最低一级成员进行赋值或取值及运算。
  • 结构体变量的初始化,是指逐个对结构体变量的各个成员进行初始化的过程。

举例:

#include <stdio.h>

struct Student
{
int age;
float score;
char sex;
};

int main(void)
{
struct Student st = {80, 66.6, 'F'}; //初始化 定义的同时赋初值

struct Student *pst = &st; //表示指针变量pst可以存放struct Student类型的地址
//注:*和pst之间不能加空格,否则使用pst->的时候不会出现提示成员名

st.age = 10; //第一种方式
pst->age = 88; //第二种方式
printf("%d %f",st.age, pst->score);

return 0;

//结果输出是88 66.599998:浮点数不能精确存储
}

//结果输出是88
}
1. pst->age在计算机内部会被转化为(*pst).age
这是->的含义,这是一种硬性规定

2. 所以pst->age 等价于 (*pst).age 也等价于 st.age

3. 我们之所以知道pst->age 等价于 st.age是因为pst->age是被转化成了(*pst).age来执行

4. pst->age 的含义:pst所指向的那个结构体变量中的age这个成员

C语言程序设计——结构体_赋值

3. 结构体变量的运算

结构体变量不能相加,不能相减,不能乘除
但结构体变量可以相互赋值

struct Student
{
int age;
char sex;
char name[100];
};
struct Student s1, s2;
st1+st2; st1*st2; st1/st2;都是错误的

st1 = st2 或者 st2 = st1 都是正确的

4. 结构体变量和结构体指针变量作为函数参数传递问题

  • 对结构体变量输入函数(发送地址)
  • 对结构体变量输出函数(可以发送内容,也可以发送地址)
    推荐发送地址,因为指针有一点的优点:
快速传递函数
耗用内存小
执行速度快

但也有一些缺点:

能够改写值,但在Java和C++中可避免
  • 举例理解
/*
功能:通过函数完成对结构体变量的输入和输出
*/

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

struct Student
{
int age; //4个字节
char sex; //1个字节
char name[100]; //100个字节
}; // 分号不能省略

void InputStudent(struct Student *stu);
void OutputStudent(struct Student ss);

int main(void)
{
struct Student st;

printf("%d\n", sizeof(st)); //输出变量st所占字节数 结果是:108个字节 其中有3个字节(目前不了解,需要学习计组)
InputStudent(&st); //对结构体变量输入 只能通过发送地址在子函数里面来修改变量的值
OutputStudent(st); //对结构体变量输出 可以发送st的地址,也可以直接发送st的内容

return 0;
}

//方式一 直接发送st的内容
//缺点:由于变量st占108字节,而通过发送内容的话,拷贝的ss也会占108字节,会浪费空间和时间
void OutputStudent(struct Student ss) //把st的内容复制一份给了ss,所以输出ss的内容和输出st的内容是一样的
{
printf("%d %c %s\n", ss.age, ss.sex, ss.name);
}

//方式二 发送st的地址
//好处:这样只需要分配4个(8个)指针变量所占的字节数,耗费更少的内存和时间
void OutputStudent(struct Student *pst)
{
printf("%d %c %s\n", pst->age, pst->sex, pst->name);
}


//只有一种方式
void InputStudent(struct Student *pstu) //Pstu只占4(或8个)个字节,根据编译器的运行位数
{
(*pstu).age = 10;
strcpy(pstu->name, "张三"); //不能写成stu.name = "张三";
pstu->sex = 'F';
}
/*
//本函数无法修改主函数的变量st的值,所以本函数是错误的
若强行输出结果为: -858993460 ? 烫烫烫...
//原因是里面是垃圾值,所以第一个垃圾指就解读成一个很大的负数,第二个就解读成? 第三个就是解读为烫烫..
void InputStudent(struct Student stu)
{
stu.age = 10;
strcpy(stu.name, "张三"); //不能写成stu.name = "张三";
stu.sex = 'F';
}
*/
  • 将一个结构体变量的值传递给另一个函数
    方法一:结构体变量的成员作为实参传递给主调函数
    方法二:可以用结构体变量作为一个整体实参
    方法三:C语言中,允许将结构体变量的地址作为实参传递,这是,对应的形参应该是一个基类型相同的结构体类型的指针

5. 举例:动态构造存放学生信息的结构体数组

动态构造一个数组,存放学生的信息,然后按分数排序输出

#include <stdio.h>
#include <malloc.h>

struct Student
{
int age;
float score;
char name[100];
};

int main(void)
{
int len;
int i, j;
struct Student t;
struct Student *pArr; //因为存放的是struct Student类型的数据,所以定义成struct Student *类型的指针
//指针本身所占是8个(4个字节) ,struct Student表示指针指向的变量的类型

printf("请输入学生的个数:\n");
printf("len = ");
scanf("%d", &len);
pArr = (struct Student *)malloc(sizeof(struct Student) * len);

//输入
for (i=0; i<len; ++i)
{
printf("请输入第%d个学生的信息:\n", i+1);
printf("age = ");
scanf("%d", &pArr[i].age); //因为pArr[i] 等价于*(pArr+i)类型是struct Student类型,所以可以给里面的成员赋值

printf("name = ");
scanf("%s", pArr[i].name); //这里不用加&,因为name是数组名,本身就已经是数组首元素的地址

printf("score = ");
scanf("%f", &pArr[i].score);

printf("\n学生的信息是:\n");
}

//排序(使用冒泡排序算法实现)
for (i=0; i<len-1; ++i)
{
for(j=0; j<len-1-i; j++)
{
if(pArr[j].score > pArr[j+i].score) //互换的是变量整体的位置,而不是互换变量的成绩成员的内容
// >是升序,<是降序
{
t = pArr[j];
pArr[j] = pArr[j+1];
pArr[j+1] = t;
}
}
}

//输出
for (i=0; i<len; ++i)
{
printf("第%d个学生的信息是:\n", i+1);
printf("age = %d\n", pArr[i].age);
printf("name = %s\n", pArr[i].name);
printf("score = %f\n", pArr[i].score);
}

return 0;
}

缺点:应该把输入、输出、排序分别放在一个子函数中

6. 用typedef说明一种新类型

  • 一般形式:typedef 类型名 标识符;
类型名:一定在此语句之前已定义的类型标识符
标识符:是一个用户定义标识符,用来标识新的类型名
  • 语句作用:
用标识符来代表已存在的类型名,并没有产生新的数据类型,因此原有的类型名依然有效
  • 举例
typedef float FLO;
FLO a;
  • 举例1
#include <stdio.h>

typedef int status; //为int在重新多取一个名字,status等价于int

typedef struct Student //typedef相当于为struct Student{int sid;char name[100];char sex;} 重新取了一个名字叫ST
{ //但是原来的名字struct Student依旧可以使用
int sid;
char name[100];
char sex;
}ST;

int main(void)
{
// int i = 10; //等价于 status i = 10;
// status j = 20;
// printf("%d\n", j);

struct Student st; //等价于ST st;
struct Student *ps = &st; //等价于ST *ps;

ST st2;
st2.sid = 200;
printf("%d\n", st2.sid);

return 0;
}
  • 举例2
#include <stdio.h>

typedef struct Student
{
int sid;
char name[100];
char sex;
}* PST; //PST 等价于struct Student *类型

/*
表示把:
struct Student
{
int sid;
char name[100];
char sex;
}*
整体当做一个类型
就是struct Student * 类型
*/

int main(void)
{
struct Student st;
PST ps = &st;
ps->sid = 99;
printf("%d\n", ps->sid);

return 0;
}
  • 举例3
#include <stdio.h>

typedef struct Student
{
int sid;
char name[100];
char sex;
}* PSTU, STU; //等价于STU代表了struct Student, PSTU 代表了struct Student *类型
//并且* PSTU, STU的顺序可以互换,例如:STU, *PSTU

int main(void)
{
STU st; //struct Student st;
PSTU ps = &st; //struct Student *ps = &st;
ps->sid = 99;
printf("%d\n", ps->sid);

return 0;
}