自定义类型

简介: 自定义类型

 

结构体

结构体的声明

结构体的基础知识

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。(而数组是相同类型的变量)。

结构的声明

struct tag//标签名,可以自定义)

{

       member-list;//成员列表

}variable-list;//变量列表

例如描述一个学生:

struct Stu
{
    char name[20];//名字
    int age;//年龄
    char sex[5];//性别
    char id[5];//学号
};//分号不能丢

特殊的声明

在声明结构的时候,可以不完全声明。(无标签名)

比如:

//匿名结构体类型
struct
{
  int a;
  char b;
  float c;
}x;//注:利用现有的内容直接创建出结构体变量,在其他任何地方都无法创建

上面的结构在声明的时候都省略掉了结构体标签。

结构的自引用

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

//代码1
struct Node
{
    int data;
    struct Node next;
};
//不可行!

显然这种形式是不可行的,因为,结构体中又创建了一个自己作为成员,该成员中又会含有自己作为成员,这样做会形成套娃,无穷无尽的套下去。

正确的自引用方式:

//代码2
struct Node
{
    int data;//数据域
    struct Node* next;//指针域
//创建了一个结构体指针
};

注意:

//代码3
typedef struct
{
    int data;
    Node* next;
}Node;
//这样写代码是否可行?
//不可行,因为第一次使用Node时就有问题,不知道Node是什么

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

有了结构体类型,那应该如何定义变量?其实很简单!

struct Point
{
  int x;
  int y;
}p1;//声明类型的同时
 
struct Point p2;//定义结构体变量p2
 
//初始化:定义变量的同时赋初值
struct Point p3 = { 1,2 };
 
struct Stu
{
  char name[15];//名字
  int age;//年龄
};
struct Stu s = { "zhangsan",20 };//初始化
 
struct Node
{
  int data;
  struct Point p;
  struct Node* next;
}n1 = {10,{4,5},NULL};//结构体嵌套初始化

结构体的内存对齐

我们已经掌握了结构体的基本使用了。

现在我们来讨论一个问题:如何计算结构体的大小。

这也是一个特别热门的考点:结构体的内存对齐

我们先来看这样一道题吧。

struct s1
{
  char c1;
  int i;
  char c2;
};
//计算s1大小

我们首先来看一看这三个变量在内存中的分布情况。

注:使用宏(offsetof())可以计算结构体成员相较于结构体起始位置偏移量.(stdeff为头文件)

#include <stddef.h>
struct s1
{
  char c1;
  int i;
  char c2;
};
 
int main()
{
  printf("%d\n", offsetof(struct s1, c1));
  printf("%d\n", offsetof(struct s1, i));
  printf("%d\n", offsetof(struct s1, c2));
  printf("%d\n", sizeof(struct s1));
  return 0;
}

运算结果:

为什么会有这样的情况呢,按一般思维我们可能想的是0,1,5 ,大小是6.

但事实并非如此,那么就让我们看看结构体的对齐规则吧。

1.结构体的第一个成员可以放在相较于结构体变量的起始位置的偏移量为0的位置。

2.从第二个成员开始,往后的每个成员都要对齐到某个对其数的整数倍处。

 对齐数:结构体成员自身大小与默认对齐数大小的最小值(不同编译器对齐数可能不同)。

3.结构体的总大小必须是最大对齐数的整数倍。最大对齐数是所有成员最大对齐数的最大值。

下面再用上面的规则完成一下这个题。(平台以VS为例,VS默认对齐数是8)

再来做几个练习:

#include<stdio.h>
struct s3
{
  double d;
  char c;
  int i;
};
 
int main()
{
  printf("%d", sizeof(struct s3));
  return 0;
}

结果是16.

#include <stdio.h>
 
struct s3
{
  double d;
  char c;
  int i;
};
 
 
struct s4
{
  char c1;
  struct s3 s;
  double d;
};
 
int main()
{
  printf("%d", sizeof(struct s4));
  return 0;
}

这里要注意结构体的嵌套,s3的大小是16,直接算进去,但最大对齐数(含嵌套结构体的对齐数)是基本数据类型的大小,即double,结果算出来是32。

为什么会存在内存对齐?

1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些特定地址处取某些特定类型的数据,否则会抛出硬件异常。

2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要两次内存访问,而对齐的只需要一次。

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

那在设计结构体时,我们既要满足内存对齐,又要节省空间,就要做到:

让占用小的成员尽可能聚集在一起。

比如:

struct s1
{
  char c1;
  int i;
  char c2;
};
 
//改为
 
struct s2
{
  char c1;
  char c2;
  int i;
};

修改默认对齐数

我们可以使用#pragma这个预处理指令,可以修改对齐数。

eg.#pragma pack(8),修改默认对齐数为8。

    #pragma pack( ),取消设定的默认对齐数,还原为默认。

结论:在默认对齐数不合适时,我们可以自己修改默认对齐数。

结构体传参

#include <stdio.h>
 
struct S
{
  int data[100];
  int num;
};
 
struct S s = { 1,2,3,4 };
 
//结构体传参
void print1(struct S s)
{
  printf("%d\n", s.num);
}
 
//结构体传参
void print2(const struct S* ps)//加上const防止参数被改
{
  printf("%d\n", ps->num);
}
 
int main()
{
  print1(s);//传结构体
  print2(&s);//传地址
  return 0;
}

上面哪个函数更好些?

显然是print2.原因:函数传参的时候,参数需要压栈,会有时间和空间上的系统开销。

如果传递一个结构体对象时,结构体过大,参数压栈是开销大,导致性能下降/

结论:结构体传参时,要传结构体地址。

相关文章
|
自然语言处理 搜索推荐 开发者
SmartArXiv——基于OpenSearch LLM智能问答版构建的智能学术论文助手正式发布
本文介绍智能学术论文助手SmartArxiv的架构、应用场景和产品功能。
2550 1
|
XML Java 数据格式
深入理解 Spring IoC 和 DI:掌握控制反转和依赖注入的精髓
在本文中,我们将介绍 IoC(控制反转)和 DI(依赖注入)的概念,以及如何在 Spring 框架中实现它们。
854 0
|
存储 弹性计算 安全
云基础设施处理器CIPU 2.0技术解读
本文深入解读阿里云的CIPU(Cloud Infrastructure Processing Unit)技术,探讨其在云计算中的定位与价值。面对当前XPU命名泛滥的问题,阿里云为何选择CIPU这一名称?CIPU旨在解决云计算中的弹性、安全、稳定、性能和成本五大核心需求。通过对比DPU和IPU,CIPU更专注于云环境下的基础设施处理,确保数据的安全性、传输的可靠性和存储的稳定性。此外,文章还回顾了神龙计算的历史发展,强调CIPU2.0在提升性能、优化资源调度和满足大客户需求方面的进展。最终,CIPU作为手段,其核心目标是为客户提供更高性价比和更稳定的云服务。
|
人工智能 算法 大数据
科技云报到:以数据“价值三角”为擎,探索数据治理实践路径
过去四十年,经济发展主要依靠土地、劳动力和传统技术。如今,数据成为继土地、劳动、资本和技术后的新型生产要素,推动数字经济时代的融合创新。然而,数据共享受限于标准缺失、系统壁垒和安全问题,亟需数据治理以激活其价值。国家数据局等17部门发布《“数据要素×”三年行动计划》,旨在2026年前拓展数据应用并打造示范场景。蚂蚁数科推出的DataFab平台和新一代AI数据标注产品,助力企业高效管理数据资产,提升标注效率,推动数据要素市场的全面发展。数据作为新型生产要素,在云计算和人工智能的驱动下,正加速变革生产生活、经济发展和社会治理方式。
332 3
|
SQL XML Java
Mybatis的原理和MybaitsPlus
这篇文章对比分析了Mybatis和Mybatis Plus的特点与底层实现机制,探讨了两者之间的差异及各自的优势。
404 0
|
算法 vr&ar
基于自适应波束成形算法的matlab性能仿真,对比SG和RLS两种方法
```markdown - MATLAB2022a中比较SG与RLS自适应波束成形算法。核心程序实现阵列信号处理,强化期望信号,抑制干扰。RLS以其高效计算权重,而SG则以简单和低计算复杂度著称。[12345] [6666666666] [777777] ```
|
SQL 安全 网络安全
数据安全之认识数据库防火墙
随着信息技术的快速发展,数据库已成为企业信息化建设的核心组成部分,存储着大量的关键业务数据和敏感信息。与此同时,数据库也面临着来自内部和外部的各种安全威胁和攻击,如SQL注入、未授权访问、数据泄露等。为了保护数据库的安全性和完整性,传统的安全措施如防火墙、入侵检测系统等在一定程度上起到了作用。然而,这些措施往往只关注于网络层面的安全防护,而缺乏对数据库应用层面的深入保护。因此,针对数据库的安全防护需求,数据库防火墙应运而生。
654 0
|
Java API
java Files和Paths的使用详解 附有使用demo
java Files和Paths的使用详解 附有使用demo
514 0
|
SQL 关系型数据库 OLAP
PostgreSQL从小白到专家 - 第25讲:窗口函数
从小白到专家 PostgreSQL技术大讲堂 - 第25讲:窗口函数
306 1