C语言之结构体

server/2025/1/13 14:16:42/

欢迎拜访:雾里看山-CSDN博客
本篇主题:C语言之结构体
发布时间:2025.1.11
隶属专栏:C语言

在这里插入图片描述

目录

  • 结构体类型的声明
  • 结构体的自引用
  • 结构体变量的定义和初始化
  • 结构体成员访问
  • 结构体传参
  • 结构体内存对齐(重要部分)
    • 结构体对齐的规则
    • 常见结构体的计算
    • 为什么存在内存对齐
    • 修改默认对齐数

结构体类型的声明

结构体是一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同类型的变量。
结构体内部的成员可以是标量、数组、指针、也可以是其他结构体。

声明

struct tag
{member-list;
}variable-list;

例如用结构体描述一个朋友

typedef struct friend
{char name[20];//名字int age;//年龄char sex[5];//性别char number[20]//电话号码
}friend;//结尾分号不能丢

特殊的声明
在对结构体进行声明的时候, 可以进行不完全声明。

struct
{int a;char b;float c;
}x;struct
{int a;char b;float c;
}a[20], *p;

上面的两个结构体在声明的时候,省略了结构体的标签

若对于以上代码执行p = &x;语句,则会报错。因为编译器会认为这是两个不同的结构体。

结构体的自引用

在结构体中包含一个类型为该结构体本身的结构体成员是否可以?

struct Node
{int data;struct Node next;//包含自身
};

不可以!! 不明白的可以想一下sizeof(struct Node)的结果是多少。上述代码会在初始化结构体的时候报错。

正确的引用方式如下:

struct Node
{int data;struct Node *next;	
};

结构体变量的定义和初始化

struct Point
{int x;int y;
}p1;//声明类型的同时定义变量P1;
struct Point P2;//直接定义变量P2;//初始化:定义变量的同时赋初值
struct Point P3 = { x, y};struct Friend
{char name[20];int age;
};
struct Friend f = {"zhangsan", 20};//初始化struct Node
{int data;struct Point P;struct Node* next;
}n1 = {10, {4, 5}, NULL};//结构体嵌套初始化struct Node n2 = {20, {3, 4}, NULL};//结构体嵌套初始化

结构体成员访问

结构体变量访问内部成员是通过点操作符 . 对内部成员进行访问。点操作符接受两个操作数。
例如:
结构体f
当我们需要访问结构体内的成员nameage 时,可以进行如下操作

struct Friend f;
strcpy(f.name, "lisi");//使用.操作符访问name成员
f.age = 21;//使用.操作符访问age成员

在实际应用中,我们更多的时候是遇到结构体的指针,这时我们可以进行如下操作。

struct Friend
{char name[20];int age;
};void Print(struct Friend *pf)
{printf("name = %s   age = %d\n", (*pf).name, (*pf).age);//使用结构体指针访问指向对象的成员printf("name = %s   age = %d\n", pf->name, pf->age);
}int main()
{struct Friend f = {"zhangsan", 20};Print(&f);return 0;
}

结构体传参

我们仔细观察下面的代码。

struct Friend
{char name[20];int age;
};
//结构体传参
void Print1(struct Friend f)
{printf("name = %s   age = %d\n", f.name, f.age);
}
//结构体指针传参
void Print2(struct Friend *pf)
{printf("name = %s   age = %d\n", pf->name, pf->age);
}int main()
{struct Friend f = {"zhangsan", 20};Print1(f);//传结构体Print2(&f);//传结构体指针return 0;
}

在实际应用中,传结构体和传结构体指针哪个更好一点?

在函数传参的时候,参数是需要压栈的,如果传递结构体,当结构体太大的时候,参数压栈的系统开销会很大,结果则会导致性能的下降。
结论: 传结构体指针更优。

结构体内存对齐(重要部分)

结构体对齐的规则

  1. 第一个成员在与结构体变量偏移量为 0 的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = min(编译器默认对齐数, 该成员大小)
  3. 结构体总大小为最大对齐数(每一个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体, 嵌套的结构体先确定自己的大小,再按照上面的规则计算。

常见结构体的计算

以在VS编译器下为例(默认对齐数是8)

struct S1
{char c1;//编译器默认对齐数为8, 该成员大小1,对齐数1 int i;//编译器默认对齐数为8, 该成员大小4,对齐数4char c2;//编译器默认对齐数为8, 该成员大小1,对齐数1 
};
printf("%d\n", sizeof(struct S1));//最终大小为12
struct S2
{int i;//编译器默认对齐数为8, 该成员大小4,对齐数4 char c1;//编译器默认对齐数为8, 该成员大小1,对齐数1 char c2;//编译器默认对齐数为8, 该成员大小1,对齐数1 
};
printf("%d\n", sizeof(struct S2));//最终大小为8
struct S3
{double d;//编译器默认对齐数为8, 该成员大小8,对齐数8 char c;//编译器默认对齐数为8, 该成员大小1,对齐数1 int i;//编译器默认对齐数为8, 该成员大小4,对齐数4 
};
printf("%d\n", sizeof(struct S3));//最终大小为16
struct S4
{char c1;//编译器默认对齐数为8, 该成员大小1,对齐数1 struct S3 s3;//编译器默认对齐数为8, 该成员大小16,对齐数8 double d;//编译器默认对齐数为8, 该成员大小8,对齐数8 
};
printf("%d\n", sizeof(struct S4));//最终大小为32

在这里插入图片描述

为什么存在内存对齐

  1. 平台原因(代码的可移植性):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件只能在某些地址处取某些特定类型的数据,否则则会抛出异常。
  2. 性能问题:数据结构(尤其是栈),应该尽可能的在自然边界上对齐原因在于,为了访问未对齐的内存,处理器需要做两次内存访问; 而对齐的内存只需要访问一次即可。

总体来说 : 结构体的内存对齐是拿空间换取时间的做法。

在设计的时候,要让占用空间小的尽量在一起,这样即满足对齐,又可以节省空间。

struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2;int i;
};

S1和S2内部成员函数相同,但是S2所占的空间却小于S1。

除此之外,还可以修改默认对齐数

修改默认对齐数

在结构体对齐方式不合适的时候, 我们可以自己更改默认对齐参数。
使用#pragma这个预处理命令可以更改我们的默认对齐数。

#include<stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int mian()
{printf("S1 = %d\n", sizeof(struct S1));printf("S2 = %d\n", sizeof(struct S1));return 0;
}

⚠️ 写在最后:以上是我对C语言结构体部分的一些学习后的总结, 如有错误或者需要补充的地方欢迎各位大佬私信我交流!!!


http://www.ppmy.cn/server/158010.html

相关文章

如何用 ESP32-CAM 做一个实时视频流服务器

文章目录 ESP32-CAM 概述ESP32-S 处理器内存Camera 模块MicroSD 卡槽天线板载 LED 和闪光灯其他数据手册和原理图ESP32-CAM 功耗 ESP32-CAM 引脚参考引脚排列GPIO 引脚哪些 GPIO 可以安全使用&#xff1f;GPIO 0 引脚MicroSD 卡引脚 ESP32-CAM 的烧录方式使用 ESP32-CAM-MB 编程…

苹果手机(IOS系统)出现安全延迟进行中如何关闭?

苹果手机&#xff08;IOS系统&#xff09;出现安全延迟进行中如何关闭&#xff1f; 一、设置二、隐私与安全性三、失窃设备保护关闭 一、设置 二、隐私与安全性 三、失窃设备保护关闭

#Uniapp: uniapp国际化适配

uniapp国际化适配 插件安装 npm i vue-i18n9.1.9根目录下新建locales文件目录 import Vue from vue; import VueI18n from vue-i18n; import zhCN from ./lang/zh-CN; import enUS from ./lang/en-US;// 获取默认语言 export const defaultLang uni.getStorageSync(language…

“多维像素”多模态雷视融合技术构建自动驾驶超级感知能力|上海昱感微电子创始人蒋宏GADS演讲预告

2025年1月14日&#xff0c;第四届全球自动驾驶峰会将在北京中关村国家自主创新示范区展示交易中心-会议中心举行。经过三年的发展&#xff0c;全球自动驾驶峰会已经成长为国内自动驾驶领域最具影响力、规模最大的产业峰会之一。在主会场下午的城市NOA专题论坛上&#xff0c;上海…

Flink 应用

Flink 应用 Flink 应用的特点Flink 应用的组成数据源&#xff08;Source&#xff09;数据流处理逻辑&#xff08;Processing Logic&#xff09;数据目的地&#xff08;Sink&#xff09;运行时配置&#xff08;Runtime Configuration&#xff09;状态&#xff08;State&#xff…

react ts 定义基本类型,组件通过ref调用时类型提示

记录&#xff0c;以防忘记 子组件 import React, { forwardRef, Ref, useImperativeHandle, useState } from react;// 类型定义方式1 interface IProps {/**参数1 */params1: number | string | undefined/**参数2 */params2: number | string | undefined/**方法 */openDia…

web作业

作业一 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>Document</title> </head&g…

202506读书笔记|《飞花令·江》——余霞散成绮,澄江静如练,江梅一夜落红雪,便有夭桃无数开

202506读书笔记|《飞花令江》——余霞散成绮&#xff0c;澄江静如练&#xff0c;江梅一夜落红雪&#xff0c;便有夭桃无数开 摘录 《飞花令江》素心落雪编著&#xff0c;飞花令得名于唐代诗人韩翃《寒食》中的名句“春城无处不飞花”&#xff0c;类似于行酒令&#xff0c;是文人…