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 ;
- tag 是结构体标签。
- member-list 是标准的变量定义,比如 int i; 或者 float f,或者其他有效的变量定义。
- variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。
- 在一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个。
关于结构体网上相关的介绍也是比较多的上面介绍参考C结构体 | 菜鸟教程,下面带你了解不一样的结构体。
结构体占空间吗?
要解决这个问题我们先来看什么是结构体,看结构体是怎么定义的:
/* 结构体成员是学生的名字,年龄 */
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个字节,这时就会出现错误;所以在写程序时一定要注意空间大小问题。
你知道下面这阳简单的语句执行过程吗?
a = 123;
接下来我们来看一下语句执行过程:
一个芯片内部有CPU、FLASH、RAM等,程序是被下载到FLASH上的,芯片上电后CPU从FLASH中得到指令后,CPU去执行指令;
FLASH上有指令:a = 123; 要将变量 a 写到内存中,分为以下三步:
- 得到 a 的地址
- 得到数据值:123
- 把数据写入地址对应的空间
由这三步看出语句执行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语言的学习请持续关注本专栏。
学习百问网资源总结笔记。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)