当前位置: 首页 > news >正文

C语言自定义类型(上)

大家好,我们又见面了,这一次我们来学习一些C语言有关于自定义类型的结构。
在这里插入图片描述

目录

1.结构体
2位段

1.结构体

前面我们已经学习了一些有关于结构体的知识,现在我们进行深入的学习有关于它的知识。

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

结构体的声明

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

我们要注意的是结构体的关键字是struct,后面的就是我们自己定义的,而括号里面的叫做成员变量,它可以是任意类型的。例如我们自己创造一个学生结构体:stu就是我们创建的结构体变量,而名字,年龄,性别,学号就是成员变量。

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

匿名结构体

struct 
{char neme[20];int age;char sex[5];//一个汉字2个字符float score;
}s1,s2;

如上代码所示,可以去掉结构体的名字 匿名结构体类型,但只能用一次,后面再想定义变量不可以(只可以使用s1和s2)

结构体的自引用

就是在结构中包含一个类型为该结构本身的成员

让我们看到下面这一段代码:

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

如果我们要算结构体的大小这段代码能够实现吗?答案是否定的,因为我们这个结构体中包含着下一个结构体,我们要计算结构体大小的时候不仅是整型的大小还要加上下一个结构体的大小,那么下一个结构体中又包含了一个结构体是无法计算的,编译器会报错。那么正确的自引用是什么的样的呢?

正确的自引用:

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

这个代码可以运行,用指针的话,指针存放下一个节点的地址,指针本身的大小也是固定的,所以可以计算出来。

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

结构体的定义:

struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1struct Point p2; //定义结构体变量p2

结构体的初始化:

struct Point p3 = {x, y};struct Stu //类型声明
{
char name[15];//名字
int age; //年龄
};
struct Stu s = {"zhangsan", 20};//初始化

结构体内存对齐

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

这里我们定义两个结构体变量:

struct S1
{char c1;int i;char c2;
};struct S2
{char c1;char c2;int i;
};
int main()
{/*printf("%d\n", offsetof(struct S2, c1));printf("%d\n", offsetof(struct S2, c2));printf("%d\n", offsetof(struct S2, i));*/printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));return 0;
}

我们想知道这两个结构体的大小,那么一个整型是4个字节,一个字符型是一个字节,那么这两个结构体的大小是不是6个字节呢?

在这里插入图片描述
很显然是不对的,那为什么会造成这个结果呢?那是因为其中成员对于起始位指定的偏移量造成的,这里我们就要了解一个宏,offsetof这个宏就是专门来计算偏移量的。

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

在这里插入图片描述
在这里我们看到每个成员变量对于初始位置的偏移量都是不同的,这就是因为内存对齐导致的。那么我们有什么办法来计算结构体的大小呢?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里以s2为例我们看到c1是一个字节和vs默认的八个对齐数,所以c1的对齐数就是1,第一个成员的偏移量为0,所以c1就在下标为0的位置,而i是四个字节和八个字节相比,对齐数是4,因为下标4才是4的倍数,所以我们的i从下标为4的位置开始储存,而c2的对齐数为1,所以它的偏移量为i的倍数就直接放在i的后面,因为结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍,最大是4,所以是12.

那么我们为什么要存在内存对齐呢?

  1. 平台原因(移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:
    数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
    总体来说:
    结构体的内存对齐是拿空间来换取时间的做法。那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:让占用空间小的成员尽量集中在一起。

修改默认对齐数

#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 main()
{
//输出的结果是什么?
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));

在这里插入图片描述
这里我们将默认对齐数改了之后所得出来的结果就明显的不同了,根据我们使用的方法计算结果就是12和6。

结构体传参

struct S
{
int data[1000];
int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s)
{
printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
printf("%d\n", ps->num);
}
int main()
{
print1(s); //传结构体
print2(&s); //传地址
return 0;
}

函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。所以我们要首选传址。

位段

1.位段的成员必须是 int、unsigned int 或signed int 。
2.位段的成员名后边有一个冒号和一个数字。

例如:

struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};

那么我们位段的大小是多少呢?

struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};int main()
{printf("%d\n", sizeof(struct A));return 0;
}

在这里插入图片描述
这里我们看到程序运行的结果是8,而我们根据自己的计算得到结构体占了47个比特位,而每个字节占了8个比特位,我们换算出来就是将近6个字节,那么为什么不是6个字节呢?这就要了解它的内存分布了。

位段的内存分配

  1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
  2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

在这里插入图片描述

我们定义的位段它的类型的是char型,而我们位段的内存分配是一个字节一个字节申请的,我们先拿到一个字节的空间,首先我们要给它三个比特位,而且是从低地址到高地址分配的,我们的a是等于10的,它的二进制的前三位是010放到这个分配的字节中,而后面也是一样b在这个字节中占了4个比特位,然后这里只剩下一个比特位就放不下了,就再申请一个字节的空间,这个里面就拿来存放c的二进制,而我们看到这个字节的空间就剩下了三个比特位了,而d还要四个比特位的空间,所以我们得再申请一个字节的空间。所以这个位段的大小就是三个字节,我们在给二进制换算的十进制,所以这三个字节的内存就是62 03 04。

位段的跨平台问题:

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

总的来说:跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

好了今天的学习就到这里,我们下次再见了。

相关文章:

C语言自定义类型(上)

大家好&#xff0c;我们又见面了&#xff0c;这一次我们来学习一些C语言有关于自定义类型的结构。 目录 1.结构体 2位段 1.结构体 前面我们已经学习了一些有关于结构体的知识&#xff0c;现在我们进行深入的学习有关于它的知识。 结构是一些值的集合&#xff0c;这些值称为…...

Python - 小玩意 - 圣诞树背景音乐弹窗

import turtle as t import tkinter as tk import pygame import random as r import threading import time# 初始化背景音乐 def initialize_music():file r"./music/周杰伦-蜗牛.mp3"pygame.mixer.init()pygame.mixer.music.load(file)pygame.mixer.music.play()…...

The 2023 ICPC Asia Regionals Online Contest (1) E. Magical Pair(数论 欧拉函数)

题目 T(T<10)组样例&#xff0c;每次给出一个n(2<n<1e18)&#xff0c; 询问多少对&#xff0c;满足 答案对998244353取模&#xff0c;保证n-1不是998244353倍数 思路来源 OEIS、SSerxhs、官方题解 2023 ICPC 网络赛 第一场简要题解 - 知乎 题解 官方题解还没有…...

<十三>objectARX开发:模拟实现CAD的移动Move命令

一、目的 实现类似于CAD的移动命令,选择对象,移动到指定位置,移动过程中对象跟随鼠标移动。效果如下: 二、关键步骤 选择对象,打开实体判断类型:acedEntSel()、acdbOpenObject()、isKindOf()。指定基点:acedGetPoint()。移动模型,追踪光标移动对象实体:acedGrRead()…...

Autosar基础:模式管理-EcuM

ECUM目录 前言一、ECUM状态机二、Fixed和Flexible模式的区别与联系三、状态详解3.1.Startup3.2.UP3.3.RUN3.4.Sleep3.5.Shutdown三、EcuM唤醒源3.1 CAN Trcv唤醒3.2 唤醒后操作前言 根据Autosar对于模式管理的需求定义,模式管理有以下模块: ①ECU State Manager(EcuM):管理…...

代码随想录Day42 | 01背包问题| 416. 分割等和子集

01背包问题&#xff08;Acwing&#xff09; 有 N 件物品和一个容量是 V的背包。每件物品只能使用一次。 第 i 件物品的体积是 vi&#xff0c;价值是 wi。 求解将哪些物品装入背包&#xff0c;可使这些物品的总体积不超过背包容量&#xff0c;且总价值最大。 输出最大价值。 输入…...

UML六大关系总结

UML六大关系有&#xff1a;继承、关系、聚合、组合、实现、依赖。分为通过图和代码总结这些关系。 1、继承 继承&#xff08;Inheritance&#xff09;&#xff1a;表示类之间的继承关系&#xff0c;子类继承父类的属性和方法&#xff0c;并可以添加自己的扩展。 继承&#x…...

ElementUI基本介绍及登录注册案例演示

目录 前言 一.简介 二.优缺点 三.Element完成登录注册 1. 环境配置及前端演示 1.1 安装Element-UI模块 1.2 安装axios和qs(发送get请求和post请求) 1.3 导入依赖 2 页面布局 2.1组件与界面 3.方法实现功能数据交互 3.1 通过方法进行页面跳转 3.2 axios发送get请求 …...

Python爬虫-某网酒店评论数据

前言 本文是该专栏的第6篇,后面会持续分享python爬虫案例干货,记得关注。 本文以某网的酒店数据为例,采集对应酒店的评论数据。具体思路和方法跟着笔者直接往下看正文详细内容。(附带完整代码) 注意:本文的案例“数据集”,选用的是本专栏上一篇“Python爬虫-某网酒店数…...

C# Onnx Yolov8 Detect 水果识别

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System…...

测试网页调用本地可执行程序(续1:解析参数中的中文编码)

学习测试网页调用本地可执行程序还遗留一个问题&#xff0c;即网页中调用带中文参数的命令时&#xff0c;本地可执行程序接收到的参数字符串里的中文都转换成了编码模式&#xff0c;看起来如下所示&#xff1a; <a href TestPageCall:-a你好>启动测试程序</a><…...

C++入门知识

Hello&#xff0c;今天我们分享一些关于C入门的知识&#xff0c;看完至少让你为后面的类和对象有一定的基础&#xff0c;所以在讲类和对象的时候&#xff0c;我们需要来了解一些关于C入门的知识。 什么是C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对…...

spring和springmvc常用注解

1.Spring常用注解&#xff1a; 1&#xff09;Repository将DAO类声明为Bean 2&#xff09;Service用于修饰service层的组件 3&#xff09;Controller通常作用在控制层&#xff0c;将在Spring MVC中使用 4&#xff09;Component是一个泛化的概念&#xff0c;仅仅表示spring中的一…...

【Java】Java生成PDF工具类

Java生成PDF工具类 一、介绍 Java生成PDF工具类是一个非常实用的工具类&#xff0c;可以帮助我们以程序化的方式生成PDF文件。通过该工具类&#xff0c;我们可以向PDF文件中添加文字、图片、表格等多种内容&#xff0c;并且可以进行格式化和样式设置。Java生成PDF工具类常用于…...

STL map,插入和查找的一些注意事项

01、前言&#xff08;废话&#xff09; C 的 std::map 容器中插入键值对主要有myMap(std::make_pair(key value)) &#xff0c;它们的区别你了解吗&#xff1f; auto it myMap,find(key) 和 auto value myMap[key] 都可以用于在 C 的 std::map 容器中查找键对应的值&#xff…...

基于springboot+vue的客户关系管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...

【Java 基础篇】Java Stream 流详解

Java Stream&#xff08;流&#xff09;是Java 8引入的一个强大的新特性&#xff0c;用于处理集合数据。它提供了一种更简洁、更灵活的方式来操作数据&#xff0c;可以大大提高代码的可读性和可维护性。本文将详细介绍Java Stream流的概念、用法和一些常见操作。 什么是Stream…...

题解:ABC321A - 321-like Checker

题解&#xff1a;ABC321A - 321-like Checker 题目 链接&#xff1a;Atcoder。 链接&#xff1a;洛谷。 难度 算法难度&#xff1a;C。 思维难度&#xff1a;C。 调码难度&#xff1a;C。 综合评价&#xff1a;见洛谷链接。 算法 模拟。 思路 输入n后从后往前依次抽…...

Zig实现Hello World

1. 什么是zig 先列出一段官方的介绍: Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software. 大概意思就是说&#xff1a; Zig是一种通用编程语言和工具链&#xff0c;用于维护健壮、最佳和可重用的软件。 官…...

Vue3+element-plus切换标签页时数据保留问题

记录一次切换标签页缓存失效问题&#xff0c;注册路由时name不一致可能会导致缓存失效...

前端教程-TypeScript

官网 TypeScript官网 TypeScript中文官网 视频教程 尚硅谷TypeScript教程&#xff08;李立超老师TS新课&#xff09;...

代码随想录算法训练营 动态规划part06

一、完全背包 卡哥的总结&#xff0c;还挺全代码随想录 (programmercarl.com) 二、零钱兑换 II 518. 零钱兑换 II - 力扣&#xff08;LeetCode&#xff09; 被选物品之间不需要满足特定关系&#xff0c;只需要选择物品&#xff0c;以达到「全局最优」或者「特定状态」即可。 …...

能跑通的mmdet3d版本

能跑通的mmdet3d版本 1.0版本 2.0版本 注意&#xff1a;mmdet和mmdet3d简单地运行 pip install -v -e . 将会安装最低运行要求的版本。不要pip install -r requirements.txt安装依赖项&#xff0c;否则依赖库版本不对。 运行mmdet3d时&#xff0c;注释掉以上代码。...

SD-MTSP:萤火虫算法(FA)求解单仓库多旅行商问题MATLAB(可更改数据集,旅行商的数量和起点)

一、萤火虫算法&#xff08;FA&#xff09;简介 萤火虫算法(Firefly Algorithm&#xff0c;FA)是Yang等人于2009年提出的一种仿生优化算法。 参考文献&#xff1a;田梦楚, 薄煜明, 陈志敏, et al. 萤火虫算法智能优化粒子滤波[J]. 自动化学报, 2016, 42(001):89-97. 二、单仓…...

bootstrapv4轮播图去除两侧阴影及线框的方法

文章目录 前言一、前提条件&#xff1a;二、bootstrap文档组件展示与实际应用1.官方文档展示如下&#xff1a;没有阴影2.实际应用情况如下&#xff1a; 三、解决方案 前言 这篇文章主要介绍了bootstrapv4轮播图去除两侧阴影及线框的方法,本文通过示例代码给大家介绍的非常详细…...

python 自建kafka消息生成和消费小工具

要将 Kafka 的消息生产和消费转换为 API 接口&#xff0c;我们可以使用 Python 的 Web 框架。其中 Flask 是一个轻量级且易于使用的选择。下面是一个简单的例子&#xff0c;使用 Flask 创建 API 来生成和消费 Kafka 消息。 1. 安装所需的库&#xff1a; pip install kafka-py…...

Prim算法:经过图中所有节点的最短路径

题目链接&#xff1a;53. 寻宝&#xff08;第七期模拟笔试&#xff09; #include<bits/stdc.h> using namespace std;// v为节点数量&#xff0c;e为边数量 int v, e;// 最小生成树 void prim(vector<vector<int>>& adj) {vector<int> dist(v1, I…...

Linux 信号捕捉函数 signal sigaction

signal函数 #include <signal.h> typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 功能&#xff1a;设置某个信号的捕捉行为 参数&#xff1a; -signum&#xff1a;要捕捉的信号 handler&#xff1a;对捕捉到的信号怎么处理…...

StarRocks操作笔记

最近在使用starRocks&#xff0c;记录一些临时的操作技巧&#xff0c;防止遗忘。 1. 创建表 CREATE TABLE IF NOT EXISTS ODS.T_TEST( pk_day date, pool_address string, code string comment 唯一主键, test1 string, test2 string, test3 string, pk_year varchar(4), pk_m…...

Linux的ls -ld命令产生的信息怎么看

2023年9月24日&#xff0c;周日上午 目录 ls -ld列出的目录或文件的信息含义文件硬链接什么是文件硬链接为什么新建目录的文件硬链接为2举例说明例一例二例三 ls -ld列出的目录或文件的信息含义 第一个字符表示文件类型: d: 目录 -: 普通文件 l: 软链接 b: 块设备文件 c:…...

网站免备案/东莞网站推广企业

低代码开发正在获得组织的认可&#xff0c;这些组织希望非专业开发人员能够创建应用程序&#xff0c;希望消除积压并解决他们面临的程序员短缺问题。 但这些框架和平台的另一个巨大的好处是在移动开发领域。这些现代解决方案已经从过去的RAD工具发展而来&#xff0c;并且正在用…...

响应式网站和/深圳百度代理

13物联网 201306104132 柴铱琳 一、 实验目的 &#xff08;1&#xff09;加深对作业调度算法的理解&#xff1b; &#xff08;2&#xff09;进行程序设计的训练。 二、 实验内容和要求 用高级语言编写一个或多个作业调度的模拟程序。 单道批处理系统的作业调度程序。作业一投入…...

成都公司网页制作/seo高级优化技巧

https://www.leiphone.com/news/201803/Tw0nrq6vGDIvbmXr.html 雷锋网(公众号&#xff1a;雷锋网)获悉&#xff0c;3月13日&#xff0c;东芝发布新AR眼镜dynaEdge AR&#xff0c;其由一个单臂眼镜和一个分离的计算单元组成。眼镜售价1899美元&#xff0c;面向企业级用户。 当所…...

哪个素材网站免费/网站关键词优化方法

1.slice&#xff08;数组&#xff09; 用法&#xff1a;array.slice(start,end) 解释&#xff1a;该方法是对数组进行部分截取&#xff0c;并返回一个数组副本&#xff1b;参数start是截取的开始数组索引&#xff0c;end参数等于你要取的最后一个字符的位置值加上1&#xff08;…...

青岛地产网站建设/内容营销的4个主要方式

游戏运行库大全 游戏运行环境 所有游戏组件 32位 64位系统常用运行库合集 3DM游戏常用运行库安装包 下载 本安装包集成32位和64位运行库&#xff0c;是目前互联网上最新最全的常用运行库合集&#xff0c;能自动识别系统版本以提供适合您的组件&#xff0c;无人值守自动安装。总…...

长沙工程建设管理中心网站/seo专业实战培训

从今年开始除了5G之外&#xff0c;智能手机还迎来了几个重要更新&#xff0c;一个就是UFS 3.1&#xff0c;一个WiFi 6&#xff0c;还有一个就是LPDDR5&#xff0c;也就是第五代低功耗内存版本&#xff0c;手机内存性能提升的同时&#xff0c;大小容量也在不断提升&#xff0c;已…...