我记不住的那些C语言的struct知识

news2024/11/17 21:17:05

背景: 最近在重学C语言,目的是为了能看懂操作系统的底层代码,也为后续使用C语言开发一个类似redis数据库的中间件做准备,于是又重新踏上了学习C语言的道路,早在上学期间就学习过C语言,但是很久都不用了,语法和技巧都忘记了。计算机的知识真是浩如烟海,用进废退。

本次学习struct,C语言没有对象object也没有类class,只有struct提供了对象object和类class的相似功能。 struct用于 封装一个复杂物体的各个变量,例如: 学生,有姓名、学号、班级、年龄、性别等等参数,他们非常相关; 另外一个用途是 某些函数需要传入多个参数,我们把这些多个参数封装到一个结构体中,再进行传入这个结构体或结构体的指针,这样方便快捷。 结构体所表示的一条数据,类似数据库学生表的一行记录。

1. 定义一个结构体:

struct student {
    char* name;
    int num;
    int grade;
    int age;
    char sex;
};

// 声明变量的同时,对变量进行赋值
struct student {
    char* name;
    int num;
    int grade;
    int age;
    char sex;
} s1 = {"xiaolong",1000,1,6,'m'},s2 = {"xiaopeng",1010,1,6,'m'};

2. 定义结构体变量 并对结构体进行设置:

// 声明自定义类型的变量时,类型名前面,不要忘记加上struct关键字
struct student xiaoming;

xiaoming.name = "xiaoming";
xiaoming.num = 1001;
xiaoming.grade = 1;
xiaoming.age = 6;
xiaoming.sex = 'm';

// 这种方式需要注意 大括号里面 值的顺序,必须与struct声明时属性的顺序保持一致
struct student xiaohong = {"xiaohong",1002,1,6,'f'};

// 否则需要为每个值指定属性名
struct student lihua = { .num=1003, .name="lihua", .sex='m', .age=7, .grade=2};

// 可以修改
lihua.age = 8;


// 也可以使用typedef命令

typedef struct student {
    char* name;
    int num;
    int grade;
    int age;
    char sex;
} stu;

// stu就是 struct student的别名
stu xiaohua = {"xiaohua",1020,1,6,'m'};

还有就是 多个结构体嵌套,嵌套的初始化和设置与上面一致,这里不再赘述。

3. 构体的复制

赋值运算符( = )可将 struct 结构每个属性的值,一模一样的复制一份,拷贝给另外一个struct变量,与数组不同(使用赋值运算符复制数组,不会复制数据,只会共享地址)

如果 结构体里面 有指针类型的变量,那么复制的是 指针的值,也就是说 两个结构体 指向的是同一个内容,修改一个会影响另外一个的值。

如果 结构体里面 没有指针类型的变量,那么 复制的是 数据的值,是两份毫无关系的数据,各自修改不影响另外一个的值。

char* name ; 这种字符指针所指向的字符串是不能修改的,只能重新指向一个新的字符串

struct student { char name[30]; short age; } a, b;

strcpy(a.name, "xiaohu");
a.age = 18;

b = a;
b.name[0] = ' ';
b.name[1] = 'l';

printf("%s\n", a.name); // xiaohu
printf("%s\n", b.name); //  laohu


struct student2 { char name[30]; short age; } a2, b2;

a2.name = "xiaohu"
a2.age = 18;

b2 = a2;

b2.name = " laohu"       //  内存新建了" laohu"字符串,b2的指针这次指向" laohu"

// 也就是说  a2.name 和 b2.name 不再指向同一块地址空间

printf("%s\n", a2.name); // xiaohu
printf("%s\n", b2.name); //  laohu  这是为什么




struct student3 { char* name; short age; } a3, b3;

a3.name = "xiaohu";
a3.age = 18;

b3 = a3;

// 这是为了 修改字符串所做的测试, 结论是 无法修改,虽然能通过编译,但是运行失败,显示段错误。
*(b3.name+0) = ' ';
*(b3.name+1) = 'l';

printf("%s\n", a3.name);
printf("%s\n", b3.name); 

4. 结构体数组 和 结构体指针

如果将 struct 变量作为参数 传入函数,则进行了 结构体的复制,一是会占用内存,二是函数内修改只是修改副本,而不会影响函数外部的原始数据,所以 一般情况下,我们 使用 struct 指针 作为参数 传入函数中。 

void happy(struct student* s){
    (*s).age = (*s).age + 1;   // . 优先级比 * 高,所以需要使用()
}

happy(&xiaoming);


void happy2(struct student* s){
    s->age = s->age + 1 ;   // 或  s->age++; 自增
}

void happy2(struct student* s){
    s->age++;   // 自增 ,  -> 优先级比 ++ 高
}

对于 struct 的变量 使用 . 获取属性,对于 struct的变量指针 使用 -> 获取属性。

xiaoming.age  ==  (*ptr).age == ptr->age

5. 通过 malloc来构造链表

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

struct node* head;

// 生成一个三个节点的列表 (11)->(22)->(33)
head = malloc(sizeof(struct node));

head->data = 11;
head->next = malloc(sizeof(struct node));

head->next->data = 22;
head->next->next = malloc(sizeof(struct node));

head->next->next->data = 33;
head->next->next->next = NULL;

// 遍历这个列表
for (struct node *cur = head; cur != NULL; cur = cur->next) {
  printf("%d\n", cur->data);
}

6. 空间对齐

struct所占用的空间不是各个属性存储空间的总和,而是最大内存占用属性的存储空间倍数,其他属性会添加空位阈值对齐。

#include<stdio.h>

int main()
{
    struct student {
        char* name;  // 8 字节
        int num;     // 4 字节
        int grade;   // 4 字节
        int age;     // 4 字节
        char sex;    // 1 字节
    };
    printf("student %d\n",sizeof(struct student));

    struct student_other1 {
        char sex;    // 1 字节
        int num;     // 4 字节
        int grade;   // 4 字节
        int age;     // 4 字节
        char* name;  // 8 字节
    };
    printf("student_other1 %d\n",sizeof(struct student_other1));

    struct student_other2 {
        char sex;    // 1 字节
        char* name;  // 8 字节
        int num;     // 4 字节
        int grade;   // 4 字节
        int age;     // 4 字节
    };
    printf("student_other2 %d\n",sizeof(struct student_other2));
    return 0;
}

64位计算机 

student:  8+ 4 + 4 +  4 + 4 = 24   ,最后一个 char sex 本身占用1个字节,但是为了补齐,和前面的int age 组合成为了一个 8 字节。

student_other1 : 4 +4+4+4+8 =24 , 第一个char sex 本身占用1个字节,但是为了补齐,和后面的int num 组合成为了一个 8 字节

student_other2:  8 +  8 + 8 + 8 =32 , 第一个 char sex本身占用1个字节,但是为了补齐7个字节,这次单独占用8个字节;  最后一个 int age 4字节无法和其他人凑成 8字节的倍数了,只能被空位填充,补齐4个字节,也就变成了8。

字节对齐是为了 提高读写效率,是存储空间的倍数, 示例中 最后一个是 把 占空间最大的指针放在了 属性顺序的中部,导致前面的属性和后面的属性都需要补空位才能实现对齐,而不是第一个和第二个可与其他属性拼凑来实现字节对齐,所以 第三种是32个字节。

结论是: 不要把 本身所占空间大的属性 写在 中间,而是按照各属性大小,从小到大进行书写,这样最大的写在尾部,即 存储空间递增的顺序来书写定义每个属性,这样能节省空间。

7.位字段

位字段其实就是 二进制的位,比如: 在TCP/IP协议中,有一些标志位如下图所示:

 URG/ACK/RST/PSH/SYN/FIN 等标志位

下面代码仅做示例,不是真实的Linux tcp的代码:

struct flag {
  unsigned int urg:1;
  unsigned int ack:1;
  unsigned int rst:1;
  unsigned int psh:1;
  unsigned int syn:1;
  unsigned int fin:1;
  unsigned int    :6;
  unsigned int head:4;
} flag;

flag.urg = 0;
flag.ack = 1;

printf("flag %d\n",sizeof(struct flag));

每个属性后面的:1 表示指定这些属性只占用 一个二进制位,  :6代表保留的6位,:4代表是头部4位。

8. 弹性数组成员

如果在struct中声明数组成员,但我一开始不知道有多少,如果给定一个很大的数会浪费空间。

struct vstring {
  int len;
  char chars[];
};
struct vstring* str = malloc(sizeof(struct vstring) + n * sizeof(char));
str->len = n;

弹性成员的数组,必须是 struct 结构的最后一个属性。另外,除了弹性数组成员,struct 结构必须至少还有一个其他属性。

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

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

相关文章

ssm学习-spring01

Spring_day01 今日目标 掌握Spring相关概念完成IOC/DI的入门案例编写掌握IOC的相关配置与使用掌握DI的相关配置与使用1,课程介绍 对于一门新技术,我们需要从为什么要学、学什么以及怎么学这三个方向入手来学习。那对于Spring来说: 1.1 为什么要学? 从使用和占有率看 Spri…

使用 ChatGPT API 构建系统(一):分类

今天我学习了DeepLearning.AI的 Building Systems with the ChatGPT API 的在线课程&#xff0c;我想和大家一起分享一下该门课程的一些主要内容。 下面是我们通过Openai API来访问ChatGPT模型的主要代码&#xff1a; import openai#您的openai的api key openai.api_key YOUR…

chatgpt赋能python:Python删除节点:从入门到实践

Python删除节点&#xff1a;从入门到实践 在任何编程语言中&#xff0c;删除节点都是一个极为常见的操作。在Python中&#xff0c;它同样非常重要&#xff0c;因为我们通常会使用Python处理各种数据结构&#xff0c;诸如树、链表等等。但是&#xff0c;删除节点并不总是一件容…

C++类和对象 -- 知识点补充

补充 const成员函数static成员友元内部类匿名对象拷贝对象时的一些编译器优化 const成员函数 将const修饰的成员函数称为const成员函数&#xff0c;const修饰类成员函数&#xff0c;实际是修饰该成员函数隐含的this指针&#xff0c;表明在该成员函数中不能对类的成员进行修改。…

javaWeb ssh自习室管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh自习室管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S 模式开发。开发环境为TOMCAT7.0,…

预报名通道已开启,2023第11届国际生物发酵展,8月4-6日上海见!

新机遇、新挑战、新发展 同期展会&#xff1a;酵素产品与益生产品展 制药机械与包装技术展 生化仪器及实验室设备展 合成生物技术与生物制造展 展会时间&#xff1a; 2023年8月4日 9:00-17:00 2023年8月5日 9:00-17:00 2023年8月6日 9:00-15:00 展会地点&#xff1a…

【数据结构】栈和队列选择题和面试编程题

目录 一、选择题 二、栈和队列的面试题 1、括号匹配问题 1.1 题目说明 1.2 题目解析 2、用队列实现栈 2.1 题目说明 2.2 题目解析 3、用栈实现队列 3.1 题目说明 3.2 题目解析 一、选择题 1、若进栈序列为 1,2,3,4 &#xff0c;进栈过程中可以出栈&#xff0c;则下列不可能的…

软考A计划-电子商务设计师-信息安全知识

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

安全防御——IDS(入侵检测系统)

安全防御——IDS&#xff08;入侵检测系统&#xff09; IDS介绍为什么需要IDSIDS的工作原理IDS的工作过程第一步&#xff1a;信息收集第二步&#xff1a;数据分析 IDS的主要检测方法1、模式匹配&#xff08;误用检测&#xff09;2、统计分析&#xff08;异常检测&#xff09;3、…

chatgpt赋能python:Python创建venv的完全指南

Python创建venv的完全指南 在Python开发中&#xff0c;虚拟环境是一个非常有用的工具。它可以让我们在同一台计算机上拥有多个Python环境&#xff0c;而不会互相干扰。在本文中&#xff0c;我们将介绍如何使用Python创建venv&#xff08;虚拟环境&#xff09;。 什么是venv&a…

4-5.配置信息和路由信息

一、配置信息 app.run()的参数 参数1&#xff1a;host&#xff0c;如果我们不指定&#xff0c;默认值是127.0.0.1。参数2&#xff1a;port&#xff0c;如果我们不指定&#xff0c;默认值是5000。参数3&#xff1a;debug&#xff0c;调试模式&#xff0c;如果不指定&#xff0…

chatgpt赋能python:Python创建画布语句

Python 创建画布语句 在数据可视化的领域&#xff0c;画布&#xff08;Canvas&#xff09;是一个重要的概念。画布可以视为一个空白的像素或向量画布&#xff0c;用于绘制图表、图形、图像和动画等。Python 提供了多种创建画布的方式&#xff0c;其中包括使用第三方库和内置库…

哲学家就餐问题(死锁)

本文主要讲述死锁的一个经典案例—哲学家就餐问题&#xff0c;并对该问题进行修复。 1. 问题描述 看上图&#xff0c;有五位哲学家&#xff0c;每天循环做两件事&#xff1a;思考&#xff0c;吃面。吃面时每人面前都有一个盘子&#xff0c;盘子左边和右边都有一根筷子&#xff…

5.3 树和二叉树的抽象数据类型定义

博主简介&#xff1a;一个爱打游戏的计算机专业学生博主主页&#xff1a; 夏驰和徐策所属专栏&#xff1a;算法设计与分析 1.什么是树的抽象数据类型定义 树的抽象数据类型定义是指对树这种数据结构的一种抽象描述&#xff0c;其中包括了树的基本操作和性质。它定义了树作为一…

基于深度学习的目标姿态检测方法_kaic

目录 摘要 第1章 引言 1.1 研究背景和意义 1.2 国内外研究现状 1.3 主要内容 第2章 单目相机的目标姿态检测技术 2.1单目相机的工作原理 2.2目标姿态检测 2.3已有的目标姿态检测方法及其局限性 2.4本章总结 第3章 构建数据集 3.1 数据集来源 3.2数据集标注 3.3数据集分析 3.4本…

基于Springboot的社区论坛系统(源代码+数据库)055

部分代码地址 https://gitee.com/ynwynwyn/forum-public 基于Springboot的社区论坛系统(源代码数据库) 一、系统介绍 前台&#xff1a; 话题列表&#xff0c;搜索话题&#xff0c;发布话题通过标签筛选话题个人设置&#xff1a;修改个人信息&#xff0c;查看发布话题记录&a…

FPGA设计的指导性原则 (三)

例12. 在SDC文件中附加syn_ramstyle综合约束属性,指定综合存贮单元的类型 SDC文件是Synplicity综合工具通用的综合约束属性文件,其扩展名为”sde”。在SDC 指定 syn_ramstyle的语法格式为: define_attribute (signal_name [bit_range)) syn_ramstyle (atring) 其中,黑体…

1.Python高频函数—数据合并merge()

前言 数据处理中经常对多个表的数据进行合并处理&#xff0c;python 提供两个十分好用的函数处理。merge() 、 concat() merger函数是Python里的数据分析工作中最常见的函数之一&#xff0c;主要应用场景是&#xff1a;针对同一个主键存在两张不同字段的表。&#xff08;这里强…

《Kali渗透基础》05. 主动信息收集(二)

kali渗透 1&#xff1a;端口扫描2&#xff1a;UDP 扫描2.1&#xff1a;Scapy2.2&#xff1a;nmap 3&#xff1a;半开放扫描3.1&#xff1a;Scapy3.2&#xff1a;nmap3.3&#xff1a;hping3 4&#xff1a;全连接扫描4.1&#xff1a;Scapy4.2&#xff1a;nmap4.3&#xff1a;dmit…

前端web入门-CSS-day04

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 复合选择器 后代选择器 子代选择器 并集选择器 交集选择器 伪类选择器 伪类-超链接&#xff08;拓…