C语言之结构体学习

本文详细介绍了C语言中的结构体,包括结构体的基本概念、定义方法、结构体变量的使用及操作,以及结构体数组和结构指针等内容。还讲解了如何使用typedef简化结构体的定义。

结构体

概述

有时候需要将不同类型的数据组合成一个有机的整体,以便于引用,所以出现了结构体。

例如,一个学生有学号/姓名/性别/年龄/地址等属性

int num;
char name[20];
char sex;
int age;
int char addr[30];

定义一个结构体的一般形式为:

struct 结构名
{
    成员表列
};

成员表列由若干个成员组成,每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其形式为:

类型说明符 成员名;

成员名的命名应符合标识符的书写规定。

例如:

struct student
{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
};

而结构体就是把它们放到一起引用,其实刚开始学C语言可能并不好学这种结构,感觉和Java类有些类似,那些都是该类下的属性,学过面向对象的Java后就能找到相似之处,加深理解。例如:

class Student
{
    int num;
    String name;
    String sex;
    int age;
    float score;
    String addr;
}

不过如果没有学过Java语言,可能就看起来不明白了,不过原理就是相通的。

定义结构体类型变量的方法

说明结构变量有以下三种方法:

(1)先声明结构体类型,再定义变量名

一般形式为:

struct student;
struct student student1,student2;
// struct是类型名
// student是结构体
// student1,student2是变量名

实例:

struct student
{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
};
struct student student1,student2;

在定义了结构体变量后,系统会为之分配内存单元。

student1和student2在内存中各占(4+20+1+4+4+30=67)字节。

(2)在声明类型的同时定义变量

一般形式为:

struct 结构体名
{
    成员列表
} 变量名表列;

例如:

struct student
{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
} student1,student2;

(3)直接定义结构体类型变量,即不出现结构体名

一般形式:

struct 
{
    成员列表;
} 变量名列表;

例如:

struct
{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
} student1,student2;

列表成员可以是一个结构,例如,根据下图给出结构体定义:

struct date
{
	int year;
	int month;
	int day;
};

struct student
{
	int num;
	char name[20];
	char sex;
	struct date birthday;
	float score;
} stu1;

如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级的成员。只能对最低级的成员进行赋值或存取以及运算。

例如:

student1.num; // 访问num
student1.birthday.month; // 访问birthday中的month

对结构体变量的成员可以像普通变量一样进行各种运算。

例如:

student2.score=student1.score;
sum=student1.score+student2.score;
student1.age++;
++studdent2.age;

可以引用结构体变量成员的地址,也可以引用结构体变量的地址。

但不能用以下语句来整体读入结构体变量:

scanf("%d, %d, %c, %d, %f, %s", &student1);

结构体变量的地址主要用作函数参数,传递的是结构体变量的地址。

结构体变量的初始化

例如:

#include <stdio.h>

struct student
{
	int num;
	char *name;
	char sex;
	float score;
} student1,student2={102, "张三", 'M', 99.5};

int main()
{
	student1=student2;
	
	printf("编号:%d\n姓名:%s\n性别:%c\n分数;%1.1f\n", student1.num, student1.name, student1.sex, student1.score);
	printf("\n");
	printf("编号:%d\n姓名:%s\n性别:%c\n分数;%1.1f\n", student2.num, student2.name, student2.sex, student2.score);
} 

结构体数组

概述

一个结构体变量可以存放一组数据(如一个学生的学号、姓名、成绩等信息)。

如果有50个学生的数据需要参加运算,就该使用数组来存储,这就是结构体数组。

结构体数组里面的每一个元素都是一个结构体类型的变量。

定义结构体数组

与定义结构体变量的方法相仿,只需要说明其为数组即可。例如:

struct student
{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
};
struct student stu1[10];// 表示一个结构体数组,里面有10个元素

结构体数组的初始化

结构体数组的初始化的一般形式:

struct student
{
    int num;
    ...
};
struct student str[]{{...}, {...}, {...}};

即先声明结构体类型,然后定义数组为该结构体类型,在定义数组时初始化。

例如:

struct student
{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
};
struct student stu[2]={
    {101, "张三", 'M', 18, 88.5, "北京市"}, 
    {102, "李四", 'F', 19, 60.5, "上海市"}
};

结构指针变量的说明和使用

指向结构体类型数据的指针

1、结构指针变量的声明

一个指针变量当用来指向一个结构变量时,称之为结构指针变量。结构指针变量中的值是所指向的结构变量的首地址。通过结构指针即可访问该结构变量,这与数组指针和函数指针的情况是相同的。

结构指针变量说明的一般形式为:

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

例如:

struct student *stu;
// 指向student的指针变量stu

2、结构指针变量的赋值

结构指针变量必须要先赋值后才能使用。

注意:赋值是把结构变量的首地址赋予该指针变量,不能把结构名赋予该指针变量。

pstu=&student1;// 这是正确的,因为studnet1是结构体变量
pstu=&student;// 这是错误的,因为student是结构体名称

结构名和结构变量是两个不同的概念,结构名只表示一个结构形式,编译系统并不对其分配内存空间。

只有当某结构变量被说明为这种类型的结构时,才对该变量分配存储空间。

结构指针变量,能够更加方便的访问结构变量的各个成员。

3、结构指针变量的访问

其访问的一般形式为:

(*结构指针变量).成员名

// 或

结构指针变量->成员名

例如:

(*pstu).num

// 或

pstu->num

结构指针变量的完整实例:

#include <stdio.h>

struct student
{
	int num;
	char *name;
	char sex;
	float score;
} student1={101, "张三", 'M', 55.4};

int main()
{
	// 结构指针变量的声明
	struct student *stu;
	// 结构指针变量的赋值
	stu=&student1;
	// 结构指针变量的访问
	printf("%d %s %c %1.1f\n", (*stu).num, (*stu).name, (*stu).sex, (*stu).score);
	printf("%d %s %c %1.1f\n", stu->num, stu->name, stu->sex, stu->score);
} 

结构指针变量作函数参数

将一个结构体变量的值传递给一个函数,有如下三种方法:

  • 用结构体变量的成员作参数
  • 用结构体变量作实参
  • 用指向结构体变量(或数组)的指针作实参,将结构体变量(或数组)的地址传给形参

实例:用结构体变量作函数参数

#include <stdio.h>

struct student
{
	int num;
	char *name;
	char sex;
	float score;
};

void print(struct student stu)
{
	printf("%d %s %c %1.1f\n", stu.num, stu.name, stu.sex, stu.score);
}

int main() 
{
	struct student stu={101, "张三", 'M', 55.4};
	print(stu);
} 

实例:用指向结构体变量的指针作实参

#include <stdio.h>

struct student
{
	int num;
	char *name;
	char sex;
	float score;
};

// 注意形参用的是结构指针变量 
void print(struct student *stu)
{
	// 注意函数内部的访问结构成员的形式 
	printf("%d %s %c %1.1f\n", stu->num, stu->name, stu->sex, stu->score);
}

int main()
{
	struct student stu={101, "张三", 'M', 55.4};
	// 注意传入的实参形式 
	print(&stu);
} 

动态存储分配

概述

由于数组的长度是必须先定义好的,在整个程序中固定不变。C语言中不允许动态数组类型。

例如注意的:int a[n];

用变量表示长度,想对数组进行大小进行动态说明,是不行的。

在实际中,所需要的内存空间往往取决了用户实际输入的数据,而不清楚实际大小,无法预先得知。

所以C语言提供了一些内存管理函数,来动态分配内存空间,也可以把不需要的内存空间回收,有效管理内存资源。

也是为后面的链表等数据结构打下基础。

常用的内存管理函数有如下三个:

  • 分配内存空间函数malloc和calloc,其中malloc用得比较多。
  • 释放内存空间函数free,也常用。

在使用上述三个函数前,需要引入

#include <stdlib.h>

malloc函数

函数原型为void *malloc(unsigned int size);

调用形式:

(类型说明符*)malloc(size)

功能:在内存的动态存储区中分配一块长度为"size"字节的连续区域。函数的返回值为该区域的首地址。

  • “类型说明符”表示把该区域用于何种数据类型。
  • (类型说明符*)表示把返回值强制转换为该类型指针。
  • “size”是一个无符号数。

例如:

char *pc=(char *)malloc(100);

表示分配100个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量pc。

calloc函数

函数原型为void *calloc(unsigned n, unsigned size);

调用形式:

(类型说明符*)calloc(n,size)

功能:在内存动态存储区中分配n块长度为“size”字节的连续区域。函数的返回值为该区域的首地址。

  • (类型说明符*)用于强制类型转换。
  • calloc函数与malloc 函数的区别仅在于一次可以分配n块区域。

例如:

struct stu *ps=(struct stu*)calloc(2,sizeof(struct stu));

其中的sizeof(struct stu)是求stu的结构长度。因此该语句的意思是:按stu的长度分配2块连续区域,强制转换为stu类型,并把其首地址赋予指针变量ps。

free函数

函数原型为void free(void *p);

调用形式:

free(void*ptr);

功能:释放ptr所指向的一块内存空间,ptr是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区应是由malloc或calloc函数所分配的区域。

链表

这里就不讲了,可以查看其他博客。

用typedef定义类型

基本使用

typedef用来声明新的类型名来替代已有的类型名。

例如:

typedef int INTEGER;
// 其中int是已有的类型,INTEGER是新的类型名
// 声明了INTEGER为整型

实例:

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

typedef int INTEGER;

int main() 
{
	INTEGER i=1;
	int j=2;
	
	printf("%d %d\n", i, j);
} 

声明结构体类型

例如:

typedef struct {
    int year;
    int month;
    int day;
} DATE;

实例:

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

typedef struct{
	int year;
	int month;
	int day;
}DATE;

int main() 
{
	DATE date;
	date.year=2022;
	date.month=11;
	date.day=12;
	
	printf("%d-%d-%d\n",date.year,date.month,date.day);
} 

声明数组类型

一般形式为:

typdef int num[100];

实例:

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

typedef int NUM[100];

int main() 
{
	NUM num={0};
	printf("%d\n\n", sizeof(num));
	return 0;
} 

声明字符指针类型

一般形式:

typedef char* STRING;

实例:

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

typedef char* STRING;

int main() 
{
	STRING str;
	str="I love C!";
	printf("%s",str);
	
	return 0;
} 

声明指向函数的指针类型

一般形式:

typedef int (*POINTER)();

实例:

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

typedef void (*P)();

void fun()
{
	printf("I love C!");
}

int main() 
{
	P p1;// void (*p1)();
	p1=&fun;
	(p1)();
	
	return 0;
} 

用typedef定义类型的方法

步骤:

  • 先按定义变量的方法写出定义体(如:int i)
  • 将变量名换成新类型名(如:将i换成COUNT)
  • 在最前面加typedef(如:typedef int COUNT)
  • 然后可以新类型名去定义常量(如:COUNT i,j;)

关于typedef的一些说明

用typedef可以声明各种类型名,但不能用来定义变量,如typedef i;,这是错误的。

用typedef只是对已经存在的类型增加一个类型名,而没有创造新的类型。

当不同源文件用到同一类型数据时,常用typedef声明一些数据类型,把它们单独放在一个文件中,然后在需要用到它们的文件中用#include命令把它们包含进来。

使用typedef有利于程序的通用与移植。

typedef与#define有相似之处,例如:typedef int COUNT;#define COUNT int的作用都是用COUNT代表int。但是,它们二者是不同的。

#define是在预编译时处理的,它只能作简单的字符串替换,而typedef是在编译时处理的。实际上它并不是作简单的字符串替换,而是采用如同定义变量的方法那样来声明一个类型。

区别:typedef和define

typedef (int*) p1;

// 和

#define p2 int*

//注意,一个有分号,一个没有分号

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值