结构体——3. 结构体指针

天將明° 2022-07-27 21:44:57

一. 结构体

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;
};

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

intchar 等占空间吗?不占空间;intchar 是变量类型不是变量;变量才分配内存;

那么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 person2char sex; 被分配了 4 个字节,sex 用了 1 个字节另外 3 个字节没有用;
struct person3char 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);
}

二. 函数指针

先看代码,看完后你就会发现引入函数指针的便捷。

#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语言的学习请持续关注本专栏。

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

本专栏文章:
C语言加强篇——(1)学习笔记 之 变量、指针、关键字
C语言加强篇——(2)学习笔记 之 结构体、结构体指针、函数指针
C语言加强篇——(3)学习笔记 之 链表的增、删、改、查

...全文
123 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
C语言程序设计 "实"1.掌握结构体类型的概念和说明方法 " "验"2.掌握结构体变量的定义和引用。结构体类型变量成员的使用。 " "目"3.掌握结构体数组的定义和使用方法。 " "的"4.掌握指向结构体变量的指针变量的概念和应用。 " " "5.掌握结构变量与指向结构的指针作为函数参数实现函数的调用。 " " "6.掌握共用体的概念和说明方法。 " " "7.掌握共用体变量的定义和引用。共用体类型变量成员的使用 " " "8.掌握位运算的概念和方法。 " " "9.掌握位运算符(&,", ,~)的使用方法。 " " "10.了解有关位运算的算法。 " " "11.掌握枚举类型概念和说明方法 " " "12.掌握枚举类型变量的定义以及枚举类型变量的使用。 " " "1.建立一个学生的简单信息表,其中包括学号、年龄" " " "、性别及一门课的成绩。要求从键 " " " "盘输入数据,并显示出来。上机运行以下程序。 " " " "分析:一个学生信息表可以由结构体来定义,表中的 " " " "内容可以通过结构体中的成员来 " " " "表示。体会结构体成员的点运算符引用方法。 " " " "#include"stdio.h" " " "实"void main() " " " "{ " " "验"struct st " " " "{ " " "内"int num; " " " "int age; " " "容"char sex; " " " "float score; " " "与"}; " " " "struct st info; " " "步"printf("input number:"); " " " "scanf("%d",&info.num); " " "骤"printf("input age:"); " " " "scanf("%d",&info.age); " " " "getchar(); " " " "printf("input sex:"); " " " "scanf("%c",&info.sex); " " " "printf("input score:"); " " " "scanf("%f",&info.score); " " " "printf("number=%d\n",info.num); " " " "printf("age=%d\n",info.age); " " " "printf("sex=%c\n",info.sex); " " " "printf("score=%f\n",info.score); " " " "} " " " "2.建立 5 " " " "名学生的信息表,每个学生的数据包括学号、姓名及 " " " "一门课的成绩。要求从键盘 " " " "输入这 5 名学生的信息,并按照每一行显示一名学 " " " "生信息的形式将 5 名学生的信息显示出 " " " "来。上机运行以下程序。 " " " "分析:每个学生的数据学号、姓名及一门课的成绩用 " " " "结构体表示,5 名学生的信息表 " " " "用结构体数组表示,体会结构体数组元素的引用方法 " " " "。 " " " "#include"stdio.h" " " " "#define N 5 " " " "struct stud " " " "{ " " " "int num; " " " "char name [20]; " " "实"float score; " " " "}; " " "验"struct stud s[N]; " " " "void main() " " "内"{ " " " "int i; " " "容"for (i=0;i结构体来定义,表中的内 " "

13

社区成员

发帖
与我相关
我的任务
社区管理员
  • community_1296
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧