C语言加强篇——(2)学习笔记 之 结构体、结构体指针、函数指针

C语言加强篇——(2)学习笔记 之 结构体、结构体指针、函数指针,第1张

系列文章目录

C语言加强篇——(1)学习笔记 之 变量、指针、关键字
C语言加强篇——(2)学习笔记 之 结构体、结构体指针、函数指针


文章目录
  • 系列文章目录
  • 前言
  • 结构体
    • 1.什么是结构体
    • 2.结构体占空间吗?
    • 3.结构体成员变量赋值
    • 4.结构体应用
    • 5.函数指针
  • 小结


前言

C语言加强篇——(1)学习笔记 记录了变量、指针以及常见的关键字,其关键在于两句口诀。

本文将继续深入来学习C语言知识:结构体、结构体指针、函数指针。


结构体 1.什么是结构体

C 数组允许定义可存储相同类型数据项的变量,结构是 C 编程中另一种用户自定义的可用的数据类型,它允许存储不同类型的数据项。

为了定义结构,必须使用 struct 语句。

struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:

struct tag { 
    member-list
    member-list 
    member-list  
    ...
} variable-list ;
  1. tag 是结构体标签。

  2. member-list 是标准的变量定义,比如 int i; 或者 float f,或者其他有效的变量定义。

  3. variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。

  4. 在一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个。

关于结构体网上相关的介绍也是比较多的上面介绍参考C结构体 | 菜鸟教程,下面带你了解不一样的结构体。

2.结构体占空间吗?

结构体占空间吗?
要解决这个问题我们先来看什么是结构体,看结构体是怎么定义的:

/*  结构体成员是学生的名字,年龄  */
struct person {
	char * name;
	int age;
}

了解了什么是结构体后,我们我们来对比着学习就知道了。


int、char 等占空间吗?不占空间;int 、char 是变量类型,不是变量;变量才分配内存;
那么
struct person 占空间吗?也不占空间;结构体是我们自定义的数据类型,不是变量;

只有将类型实例化才占内存空间;

那么实例化的结构体占多少内存呢?

struct person zhangsan = {"zhangsan", 22};
/*实例化后的运行结果*/
sizeof(int) = 4, sizeof(char) = 1
sizeof(int *) = 4, sizeof(char *) = 4
sizeof(struct person) = 8
sizeof(struct person *) = 4

struct person 的8个字节怎么来的呢?由该结构体的成员变量决定的。

struct person {
	char * name;  //占 4个字节
	int age;      //占 4个字节
}

看完后是不是感觉原来如此简单?你掌握了吗?我们来看一下下面的结构体:

struct person2 {
	char sex;     //占 1个字节
	int age;      //占 4个字节
}struct person3 {
	char sex;     //占 1个字节
	char high;    //占 1个字节
	int age;      //占 4个字节
}

上面这两个结构体又占多少字节呢??是分别占5个字节、6个字节吗?不是的。

/*实例化后的运行结果*/
sizeof(struct person2) = 8
sizeof(struct person3) = 8

没错这两个结构体都是占8个字节,实际上是:
在struct person2中char sex; 被分配了 4 个字节,sex 用了 1 个字节另外 3 个字节没有用;
在struct person3中char sex; 也被分配了 4 个字节,sex 用了 1 个字节另外 3 个字节没有用;在给char high;分配空间时使用的是 char sex;分配的未使用的空间

如下图:

那么问题有来了为什么 int age;所占的4字节不从char sex;不用的空间开始往后取4个字节呢?

这就涉及到数据对齐的问题了,CPU为了更快的取指令和数据,一般是成块的取,(比如一次取四字节),这个块一般是2的倍数。

如果从奇数地址开始取数,就有可能发生一个数据正好落在两个块里,这样自然就要取两次。

所以一般即使数据不够一个块,我们也把下一个数据放到下一个块里,而不是把数据紧密的排在一起。

让CPU只取一次就得到需要的数据数是高效率的表现。

学习完以上内容后来看下面一段代码,这段代码是否有问题?问题出现在哪里?


如果把第35行注释掉是正常的:


问题就在于,man.a是char型变量占内存1个字节,而指针p 是int型变量占内存4个字节,这时就会出现错误;所以在写程序时一定要注意空间大小问题。

3.结构体成员变量赋值

你知道下面这阳简单的语句执行过程吗?

	a = 123

接下来我们来看一下语句执行过程:
一个芯片内部有CPU、FLASH、RAM等,程序是被下载到FLASH上的,芯片上电后CPU从FLASH中得到指令后,CPU去执行指令;

FLASH上有指令:a = 123; 要将变量 a 写到内存中,分为以下三步:

  1. 得到 a 的地址
  2. 得到数据值:123
  3. 把数据写入地址对应的空间

由这三步看出语句执行a = 123; 隐含了对地址的 *** 作;那么

	int * p;
	p = &a;
	*p = 123;

这三条语句与a = 123;语句含义相同。

p 是 int 型指针变量;
p 在内存中有空间,可以往空间中写值(这个值是变量 a 的地址),可以使用这个地址来 *** 作变量 a;

整型变量赋值可以通过指针赋值,那么给结构体成员变量赋值呢?

struct person {
	char a;   
	char b;   
	int c;      
}int main(void)
{
	int m;
	struct person man = {'A', 'B', 20};
	
	int *p;                 //int型指针 , 占 4 字节
	struct person *pt;      //结构体指针, 占 4 字节

	m = 123;              //赋值方式1
	p = &m;		//语句1
	*p = 123;	//语句2     赋值方式2(语句1、语句2)
	
}

上面是 int 型变量赋值,是否能够对比写出结构体成员变量赋值呢?
先画个图看一下变量存储情况:

结构体成员有3个变量,赋值时需要确定是给哪一个变量赋值

/*给结构体成员 c 赋值*/
	man.c = 30;        		 //赋值方式1
	pt = &man;		//语句1
	pt->c = 40;		//语句2     赋值方式2(语句1、语句2)
/*
*	man.c = 30; 	理解为结构体 man 的 c
*	pt->c = 40;     理解为结构体指针 pt 指向的 c
*/
4.结构体应用
#include "stdio.h"


typedef struct student{
	char * name;                //名字
	int age;                    //年龄
	struct student * classmate; //同桌
}student, * pstudent;

int main(void)
{
	student ZhangSan = {"zhangsan", 21, NULL};
	student LiSi = {"lisi", 22, NULL};
	
	ZhangSan.classmate = &LiSi; //张三的同桌是李四
	LiSi.classmate = &ZhangSan; //李四的同桌是张三

	printf("ZhangSan's classmate is : %s\r\n", ZhangSan.classmate->name);

	while(1);
}
5.函数指针
#include "stdio.h"
#include "string.h"

/*	要求
 *		张三执行动作1
 *		李四执行动作2
 */
 
typedef struct student{
	char * name;                //名字
	int age;                    //年龄
	struct student * classmate; //同桌
}student, * pstudent;

void work1()
{
	/*可编写动作内容*/
}
void work2()
{
	/*可编写动作内容*/
}

int main(void)
{
	int i;
	student ss[2] = {{"zhangsan", 21, NULL},
					{"lisi", 22, NULL}
					};
					
	for(i = 0; i < 2 ; i++)
	{
		if(strcmp(ss[i].name, "zhangsan") == 0)
			work1();
		if(strcmp(ss[i].name, "lisi") == 0)
			work2();
	}
	
	while(1);
}

这是一种写法,如果同学有很多,50个,100个呢,这就需要写50个,100个判断语句??显然这种写法不是很理想,是否还有其他写法呢?

这就要引入函数指针了

函数指针是指向函数的指针变量。


通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。


函数指针可以像一般函数一样,用于调用函数、传递参数。


函数指针变量的声明:

typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型

那么结构体就可以重新定义,将执行的动作函数定义为函数指针:

typedef struct student{
	char * name;                //名字
	int age;                    //年龄
	void (* work)(void);		//函数指针
	struct student * classmate; //同桌
}student, * pstudent;

定义的学生结构体数组就可以写成:

	void work1()
	{
		/*可编写动作内容*/
	}
	void work2()
	{
		/*可编写动作内容*/
	}


	student ss[2] = {{"zhangsan", 21, work1, NULL},    //直接将函数名称传进来就行了
					 {"lisi", 22, work2,NULL}
					};


整体写下来就是:

#include "stdio.h"
#include "string.h"

/*	要求
 *		张三执行动作1
 *		李四执行动作2
 */
 
typedef struct student{
	char * name;                //名字
	int age;                    //年龄
	void (* work)(void);		//函数指针
	struct student * classmate; //同桌
}student, * pstudent;

void work1()
{
	/*可编写动作内容*/
}
void work2()
{
	/*可编写动作内容*/
}

int main(void)
{
	int i;
	student ss[2] = {{"zhangsan", 21, work1, NULL},    //直接将函数名称传进来就行了
					 {"lisi", 22, work2,NULL}
					};


	for(i = 0; i < 2 ; i++)
	{
		ss[i].work();   //		ss[0].work();   等价于  work1();
	}
	
	while(1);
}



小结

本文主要讲述结构体、结构体指针、函数指针;针对C语言的学习请持续关注本专栏。

学习百问网资源总结笔记。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/662093.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-18
下一篇 2022-04-18

发表评论

登录后才能评论

评论列表(0条)

保存