c语言tips-结构体

news2025/1/10 5:59:04

1. 结构体定义和使用

#include <stdio.h>

struct stu {
    int age;
    int height;
    int weight;
};


int main(int argc, char *argv[]) {
    struct stu stu1;
    stu1.age = 18;
    stu1.height = 172;
    stu1.weight = 62;
    printf("年龄为:%d, 身高为:%d, 体重为:%d", stu1.age, stu1.height, stu1.weight);
    return 0;
}

定义结构体,然后把它当作一个类型初始化,并对其赋值和获取

#include <stdio.h>

struct stu {
    int age;
    int height;
    int weight;
} stu1;


int main(int argc, char *argv[]) {
    stu1.age = 18;
    stu1.height = 172;
    stu1.weight = 62;
    printf("年龄为:%d, 身高为:%d, 体重为:%d", stu1.age, stu1.height, stu1.weight);
    return 0;
}

在定义结构体的时候就初始化一个结构体变量

2. 结构体的存储空间

结构体是一种自定义的数据类型,是创建变量的模板,不占用内存空间;结构体变量才包含了实实在在的数据,需要内存空间来存储。

我们看看以下例子:

#include <stdio.h>

struct stu {
    int age;
    int height;
    int weight;
} stu1;


int main(int argc, char *argv[]) {
    stu1.age = 18;
    stu1.height = 172;
    stu1.weight = 62;
    printf("年龄为:%d, 身高为:%d, 体重为:%d\n", stu1.age, stu1.height, stu1.weight);
    printf("结构体所占的内存空间为:%d", sizeof(stu1));
    return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UhFTJM7O-1669625987454)(img/image-20221128111618814.png)]

可以看到当前结构体的内存空间为12刚好等于三个int类型的内存大小

3. c语言结构体的内存填充

本节内容来自c语言中文网http://c.biancheng.net/view/vip_2093.html加上我自己的理解

3.1 计算机内存寻址

计算机内存是以字节( Byte)为单位划分的,理论上 CPU 可以访问任意编号的字节,但实际情况并非如此

CPU 通过地址总线来访问内存,一次能处理几个字节的数据,就命令地址总线读取几个字节的数据。 32 位的CPU 一次可以处理 4 个字节的数据,那么每次就从内存读取 4 个字节的数据;少了浪费主频,多了没有用。 64位的处理器也是这个道理,每次读取 8 个字节。

以 32 位的 CPU 为例,实际寻址的步长为 4 个字节,也就是只对编号为 4 的倍数的内存寻址,例如 0、 4、 8、12、 1000 等,而不会对编号为 1、 3、 11、 1001 的内存寻址。如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B0NJYwVy-1669625987456)(img/image-20221101161741561.png)]

这样做可以以最快的速度寻址:不遗漏一个字节,也不重复对一个字节寻址

对于程序来说,一个变量最好位于一个寻址步长的范围内,这样一次就可以读取到变量的值;如果跨步长存储,就需要读取两次,然后再拼接数据,效率显然降低了

例如一个 int 类型的数据,如果地址为 8,那么很好办,对编号为 8 的内存寻址一次就可以。如果编号为 10,就比较麻烦, CPU 需要先对编号为 8 的内存寻址,读取 4 个字节,得到该数据的前半部分,然后再对编号为12 的内存寻址,读取 4 个字节,得到该数据的后半部分,再将这两部分拼接起来,才能取得数据的值。如下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mrd1PJER-1669625987456)(img/image-20221128152832263.png)]

将一个数据尽量放在一个步长之内,避免跨步长存储,这称为内存对齐。 在 32 位编译模式下,默认以 4 字节对齐;在 64 位编译模式下,默认以 8 字节对齐

为了提高存取效率,编译器会自动进行内存对齐,请看下面的代码:

#include <stdio.h>

struct stu {
    int age;
    char height;
    int weight;
} stu1;


int main(int argc, char *argv[]) {
    stu1.age = 18;
    stu1.weight = 62;
    printf("年龄为:%d 体重为:%d\n", stu1.age,stu1.weight);
    printf("结构体所占的内存空间为:%d", sizeof(stu1));
    return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RTINJA7u-1669625987457)(img/image-20221128152938645.png)]

如果不考虑内存对齐,结构体变量 t 所占内存应该为 4+1+4 = 9 个字节。考虑到内存对齐,虽然成员 b 只占用 1 个字节,但它所在的寻址步长内还剩下 3 个字节的空间,放不下一个 int 型的变量了,所以要把成员 c 放到下一个寻址步长。剩下的这 3 个字节,作为内存填充浪费掉了。请看下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iN0F5e2M-1669625987458)(img/image-20221101162705657.png)]

编译器之所以要内存对齐,是为了更加高效的存取成员 c,而代价就是浪费了 3 个字节的空间

4. c语言结构体数组

结构体数组,是指数组中的每个元素都是一个结构体。

#include <stdio.h>

struct Date {
    int year;
    int month;
    int date;
} dates[] = {
        2022, 1, 1,
        2021, 6, 12,
        2011, 10, 10
};

int main(int argc, char *argv[]) {
    for(int i = 0; i < 3; i++)
    {
        printf("%d-%d-%d\n", dates[i].year, dates[i].month, dates[i].date);
    }
    return 0;
}

5.结构体嵌套结构体

结构体的成员里面有结构体

#include <stdio.h>

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

struct People
{
    struct Date born_date;
    char[20] name;
    int age;
    int height;
    int weight;
}people1 = {{2011, 2, 1}, "jack", 18, 172, 62};

int main(int argc, char *argv[]) {
    printf("名字为:%s, 出生日期为:%d-%d-%d, 年龄:%d, 身高:%d, 体重:%d",
           people1.name, people1.born_date.year, people1.born_date.month, people1.born_date.date, people1.age, people1.height, people1.weight);
    return 0;
}

6.结构体指针

指向结构体的指针称为结构体指针

编写格式:struct xxx* xxx

例子如下:

#include <stdio.h>

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

struct People
{
    struct Date born_date;
    char[20] name;
    int age;
    int height;
    int weight;
}people1 = {{2011, 2, 1}, "jack", 18, 172, 62};

int main(int argc, char *argv[]) {
    struct People* pt = &people1;
/*    printf("名字为:%s, 出生日期为:%d-%d-%d, 年龄:%d, 身高:%d, 体重:%d",
           people1.name, people1.born_date.year, people1.born_date.month, people1.born_date.date, people1.age, people1.height, people1.weight);*/
    printf("名字为:%s, 出生日期为:%d-%d-%d, 年龄:%d, 身高:%d, 体重:%d\n",
           pt->name, pt->born_date.year, pt->born_date.month, pt->born_date.date, pt->age, pt->height, pt->weight);
    printf("名字为:%s, 出生日期为:%d-%d-%d, 年龄:%d, 身高:%d, 体重:%d\n",
           (*pt).name, (*pt).born_date.year, (*pt).born_date.month, (*pt).born_date.date, (*pt).age, (*pt).height, (*pt).weight);
    return 0;
}

pt->xxx(*pt).xxx是一样的,那为什么要这个结构体指针呢,初始化这么麻烦,取值赋值都这么麻烦,这在我们后面函数赋值再来探讨

7.结构体的赋值传递和函数传递

直接赋值传递

#include <stdio.h>

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

struct People
{
    struct Date born_date;
    char[20] name;
    int age;
    int height;
    int weight;
}people1 = {{2011, 2, 1}, "jack", 18, 172, 62};

int main(int argc, char *argv[]) {
    struct People people2 = people1;
    printf("名字为:%s, 出生日期为:%d-%d-%d, 年龄:%d, 身高:%d, 体重:%d",
       people2.name, people2.born_date.year, people2.born_date.month, people2.born_date.date, people2.age, people2.height, people2.weight);
    return 0;
}

函数传递

结构体变量名代表的是整个集合本身,作为函数参数时传递的整个集合,也就是所有成员,而不是像数组一样被编译器转换成一个指针。如果结构体成员较多,尤其是成员为数组时,传送的时间和空间开销会很大,影响程序的运行效率。所以最好的办法就是使用结构体指针,这时由实参传向形参的只是一个地址,非常快速。

请看第一个例子:

#include <stdio.h>

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

struct People
{
    struct Date born_date;
    char* name;
    int age;
    int height;
    int weight;
}people1 = {{2011, 2, 1}, "jack", 18, 172, 62};

void print_struct(struct People people2){
    printf("名字为:%s, 出生日期为:%d-%d-%d, 年龄:%d, 身高:%d, 体重:%d",
           people2.name, people2.born_date.year, people2.born_date.month, people2.born_date.date, people2.age, people2.height, people2.weight);
}

int main(int argc, char *argv[]) {
    struct People people2 = people1;
    print_struct(people2);
    return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Jzt4Dke-1669625987459)(img/FireShot Capture 001 - Python Tutor code visualizer_ Visualize code in Python, JavaScript, C_ - pythontutor.com.png)]

通过代码的可视化展示我们可以看到,当结构体变量传到函数时会新开辟一个内存来存储传过来的结构体变量,有些结构体变量存储的数据过大调用函数会造成极大的时间和空间资源的浪费

我们再来看第二个例子:

#include <stdio.h>

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

struct People
{
    struct Date born_date;
    char* name;
    int age;
    int height;
    int weight;
}people1 = {{2011, 2, 1}, "jack", 18, 172, 62};

void print_struct(struct People *pt){
    printf("名字为:%s, 出生日期为:%d-%d-%d, 年龄:%d, 身高:%d, 体重:%d",
           pt->name, pt->born_date.year, pt->born_date.month, pt->born_date.date, pt->age,pt->height, pt->weight);
}

int main(int argc, char *argv[]) {
    struct People people2 = people1;
    print_struct(&people2);
    return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fonICsY7-1669625987460)(img/FireShot Capture 002 - Python Tutor code visualizer_ Visualize code in Python, JavaScript, C_ - pythontutor.com.png)]

可以看到还是节省不少资源实现了相同的功能

8.typedef关键字在结构体中的使用

通过前面的例子我们发现在定义结构体变量时需要struct 结构体名称 结构体变量名,定义时还需要定义struct就很烦,有没有什么办法在定义时可以省略掉这个strcut呢?这就需要用到typedef

格式: typedef 原类型名 新类型名;

请看以下例子:

#include <stdio.h>

typedef struct Date {
    int year;
    int month;
    int date;
}Date;

typedef struct People
{
    Date born_date;
    char* name;
    int age;
    int height;
    int weight;
}People;

void print_struct(struct People *pt){
    printf("名字为:%s, 出生日期为:%d-%d-%d, 年龄:%d, 身高:%d, 体重:%d",
           pt->name, pt->born_date.year, pt->born_date.month, pt->born_date.date, pt->age,pt->height, pt->weight);
}

int main(int argc, char *argv[]) {
    People people1 = {{2011, 2, 1}, "jack", 18, 172, 62};
    People people2 = people1;
    print_struct(&people2);
    return 0;
}

这样我们就可以少写一个struct,例如struct People people1 --> People people1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/42992.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Self-attention自注意力机制——李宏毅机器学习笔记

self-attention想要解决的问题 复杂的输入 每次输入的length不同时&#xff0c;即Vector Set大小不同&#xff0c;该如何处理&#xff1f; one-hot encoding&#xff0c;缺点&#xff1a;所有的词汇之间没有语义资讯。 word embedding&#xff0c;会给每一个词汇一个向量&…

02-Docker-常用命令

目录 一、帮助启动类命令 启动docker 停止docker 重启docker 查看docker状态 开机启动 查看docker概要信息 查看docker总体帮助文档 查看docker命令帮助文档 二、镜像命令 1. 列出本地主机上的镜像 2. 查找镜像 3. 下载镜像 4. 查看镜像/容器/数据卷所占的空间 5.…

[附源码]SSM计算机毕业设计小说网站的设计与实现1JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Java#数据结构----2

目录 一.数据结构(树) 二.二叉树(任意节点的度<2) 二叉查找树又称为二叉排序树/二叉搜索树 平衡二叉树 平衡二叉树的旋转机制 三.红黑树 一.数据结构(树) 基本概念: 度: 每一个节点的子节点数量 树高: 树的总层数 根节点: 最顶层的节点 左子节点: 左下方的节点 右子节…

【Java八股文总结】之面试题(二)

文章目录面试题一、几种锁的区别1、synchronized 和ReentrantLock的区别二、Java高级1、反射1、反射的应用场合2、Java反射API3、反射使用步骤&#xff08;获取Class 对象、调用对象方法&#xff09;4、获取Class对象的3种方法5、创建对象的两种方法 newInstance(&#xff09;2…

数据结构和算法——基于Java——3.1链表(单链表)

理论补充 概念:链表是以节点的方式存储&#xff0c;是链式存储 特性 每个节点包含一个data数据域&#xff08;存放数据&#xff09;&#xff0c;一个next域&#xff08;指向下一个节点&#xff09;链表分带头结点和不带头节点&#xff08;单链表带头&#xff09;链表的内存分…

移动端开发:WebView介绍和使用、JSBridge等

Android应用层的开发有几大模块&#xff0c;其中WebView是最重要的模块之一。Android系统的WebView发展历史可谓一波三折&#xff0c;系统WebView开发者肯定费劲心思才换取了今天的局面——应用里的WebView和Chrome表现一致。 webview是什么&#xff1f; WebView是一种控件&a…

计算机的存储器(详解)

文章目录一&#xff0c;存储器&#xff08;一&#xff09;存储器的分类1) 根据功能和特性分类2) 根据存储容量和所在位置分类二&#xff0c;寄存器&#xff08;一&#xff09;寄存器的类别三&#xff0c;高速缓冲存储器&#xff08;一&#xff09;高速缓存的类别1) 一级高速缓存…

QT-MySQL QSqlDatabase: QMYSQL driver not loaded

使用Qt连接mysql数据库&#xff0c;遇到了一个问题&#xff0c;就是QT5.14.1版本在连接MySQL数据库时候&#xff0c;提示驱动加载失败&#xff0c;“QMYSQL driver not loaded” 编程环境&#xff1a;   Qt5.14.1,编译器是MSVC2017_64,是64位的。   mysql-8.0.30-winx64&am…

场馆系统的数据分析功能怎么样?

在大数据时代&#xff0c;对人们的消费数据进行分析&#xff0c;对企业发展至关重要。对于正在向智能化靠近的运动球馆来说&#xff0c;收集数据、分析数据来了解和改善场馆的运营已经成了一个新的趋势。 场馆系统组建了数据管理分析模块&#xff0c;让球馆通过信息化管理&…

【IoT】如何快速了解一个行业?如何做市场洞察?

如果要做一个新的事情&#xff0c;往往我们也不知道未来会怎么样。 我们准备去做一个事情之前一般都是会有个假设&#xff0c;比方说我们学习一项新技术&#xff0c;我们假设学习了以后能够给未来的生活带来提升&#xff0c;能给自己创造更多的机会&#xff0c;这就是个假设。 …

C++求解汉明距离

目录汉明距离介绍汉明距离应用解法1&#xff1a;Brian Kernighan算法解法2解法3汉明距离介绍 leetcode 461 汉明距离&#xff0c;难度&#xff1a;简单 两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。 给你两个整数 x 和 y&#xff0c;计算并返回…

[附源码]计算机毕业设计Springboot电影推荐网站

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

APS软件的技术指标与特色

企业可能经常会因为无法掌握生产制造现场的实际产能状况及物料进货状况&#xff0c;导致物料及产能规划与现场详细作业排程难度增大&#xff0c;从而采取有单就接的接单政策与粗估产能的生产排程方式。这种方式就可能导致企业的生产状况频发&#xff1a;在提高对顾客的服务水平…

【Android App】实战项目之实现你问我答的智能语音机器人(超详细 附源码和演示视频)

需要全部代码请点赞关注收藏后评论区留言私信~~~ 一、需求描述 想必大家都见过商场里的智能语音机器人&#xff0c;你对它提问时它可以自动回答你的问题&#xff0c;接下来我们也实现这样一个机器人&#xff0c;它依靠语音技术完成问询服务 基本功能如下 1&#xff1a;接收人们…

锦江酒店(中国区):在变革中拥抱未来 实现高质量发展

过去十年&#xff0c;中国酒店业经历了规模急剧扩张的黄金时期。后疫情时代&#xff0c;酒店行业正在发生深刻变革。 2022年&#xff0c;面对诸多外部不确定性&#xff0c;国内企业均感受到挑战。如何做到在国内大循环为主体、国内国际双循环相互促进的全新格局下高质量发展&a…

Java+JSP基于ssm共享充电宝管理系统-计算机毕业设计

项目介绍 本共享充电宝管理系统主要包括系统用户管理模块、维护人员模块、投放信息管理、区域信息管理、使用记录管理、登录模块、和退出模块等多个模块,系统采用SSM(springMvcspringMybatis)框架进行开发,本系统使用mysql,开发工具支持eclipse idea myeclipse等主流开发工具i…

点云库PCL学习——点云的格式、PCD文件的打开和显示

1、主要参考 (1)格式说明&#xff1a; 点云库PCL学习——点云的格式、PCD文件的打开和显示 ROS知识&#xff1a;点云文件.pcd格式_无水先生的博客-CSDN博客_pcd文件 &#xff08;2&#xff09;点云滤波&#xff0c;对nan的滤波 Python点云数据处理(三)滤波与RANSAC分割 - …

Bootstrap学习从一个模板开始

开源代码&#xff1a; BootstrapDash/celestialAdmin-free-admin-template: Celestial Free Bootstrap Admin Dashboard Template (github.com)https://github.com/BootstrapDash/celestialAdmin-free-admin-template 作为一个新手&#xff0c;知识还停留在简单的HTMLJSCSS的h…

2022王道计组 chap1 计算机系统概述

2022王道计组 chap1计算机系统概述 1.1 计算机发展历程 本节思维导图 知识点 [计算机系统] 计算机系统 硬件 软件 计算机性能的好坏取决于“软”“硬”件功能的总和 硬件&#xff1a;计算机的实体&#xff0c;如主机、外设等 发展变化&#xff1a;见思维导图 软件&#xf…