c语言结构体学习整理(结构体初始化,结构体指针)

article/2025/8/30 6:08:11

渣渣c的c语言学习之路

1.关于c语言的结构体:

首先我们为什么要用到结构体,我们都已经学了很多int char …等类型还学到了同类型元素构成的数组,以及取上述类型的指针,在一些小应用可以灵活使用,然而,在我们实际应用中,每一种变量进行一次声明,再结合起来显然是不太实际的,类如一位学生的信息管理,他可能有,姓名(char),学号(int)成绩(float)等多种数据。如果把这些数据分别单独定义,就会特别松散、复杂,难以规划,因此我们需要把一些相关的变量组合起来,以一个整体形式对对象进行描述,这就是结构体的好处。

2首先我们要了解一些小知识

2.1**只有结构体变量才分配地址,而结构体的定义是不分配空间的。**
2.2结构体中各成员的定义和之前的变量定义一样,但在定义时也不分配空间。
2.3结构体变量的声明需要在主函数之上或者主函数中声明,如果在主函数之下则会报错
2.4c语言中的结构体不能直接进行强制转换,只有结构体指针才能进行强制转换
2.5相同类型的成员是可以定义在同一类型下的
列如


struct Student
{ int number,age;//int型学号和年龄char name[20],sex;//char类型姓名和性别float score;
}

最后的分号不要忘了 有的编译器会自动加上,因此有的同学就会不注意。

3关于结构体变量的定义和引用

在编译时,结构体的定义并不分配存储空间,对结构体变量才按其数据结构分配相应的存储空间

struct Book{ char title[20];//一个字符串表示的titile 题目char author[20];//一个字符串表示的author作者float value;//价格表示 };//这里只是声明 结构体的定义 
struct Book book1,book2;//结构体变量的定义 分配空间book1.value;//引用结构体变量

定义结构体变量以后,系统就会为其分配内存单元,比如book1和book2在内存中占44个字节(20+20+4)具体的长度你可以在你的编译器中使用sizeof关键字分别求出来。
列如

当然,要注意一点:用sizeof关键字求结构体长度时,返回的最大基本类型所占字节的整数倍 比方说我们上面求得的为44 为 float(4个字节)的整数倍,
但是我们把title修改为title[22]; 这时正常长度为46 ,但是你会发现实际求得的为48,(4的整数倍)

这就涉及到结构体的存储

1结构体整体空间是占用空间最大的成员(的类型)所占字节数的整数倍。

2.结构体的每个成员相对结构体首地址的偏移量(offset)都是最大基本类型成员字节大小的整数倍,如果不是编译器会自动补齐,

关于这个我们简单介绍下:

1.偏移量----偏移量指的是结构体变量中成员的地址和结构体变量首地址的差。即偏移字节数,结构体大小等于最后一个成员的偏移量加上他的大小,第一个成员的偏移量为0,

struct S1
{char a;int b;double c;
};

这里char a 偏移量为1 之后为int b 因为偏移量1不为int(4)的整数倍,所以会自动补齐,而在 double c 时,偏移量为8 是double(8)的整数倍,所以不用自动补齐 最后求得结构体得大小为 16

具体看下图:
在这里插入图片描述
通过上面的代码同学们应该会有一个简单的认知

4结构体变量的初始化

结构体的初始化有很多需要注意的地方,这里我们说明下
首先是几种初始化的方法
ps在对结构体变量初始化时,要对结构体成员一一赋值,不能跳过前面成员变量,而直接给后面成员赋初值,但是可以只赋值前面几个,对与后面未赋值的变量,如果是数值型,则会自动赋值为0,对于字符型,会自动赋初值为NULL,即‘\0’

4.1定义时直接赋值

struct Student
{ char name[20];char sex;int number;
}stu1={"zhaozixuan",'M',12345};
//或者
struct Student
{ char name[20];char sex;int number;
}struct Student stu1={"zhaozixuan",'M',12345};

注意字符为‘ ’ 字符串为""
4.2定义结构体之后逐个赋值

stu1.name="王伟";
stu1.sex='M';
stu1.number=12305;
//也可用strcpy函数进行赋值
strcpy(stu1.name,"王伟");

4.3定义之后任意赋值

 struct Student stu1={.name="Wang",.number=12345,.sex='W', };//可以对任意变量赋值

这样写的好处时不用按照顺序来进行初始化,而且可以对你想要赋值的变量直接进行赋值,而不想赋值的变量可以不用赋值

需要注意的是如果在定义结构体变量的时候没有初始化,那么后面就不能全部一起初始化了;

等下结构体数组初始化时我们还会有一个讲解

这里我们顺带提一下typedef说明结构体类型


这里的BOOK就相当于struct book的一个别名一样,用它来定义结构体变量非常简便
主要也是考二级要用到,所以我们简单介绍下

5结构体变量的引用(输出和输入)

5.1结构体变量的赋值用scanf赋值和printf输出时跟其他变量操作一样
但是有几点需要注意
(1) .是运算符,在所有运算符优先级中最高
(2)如果结构体的成员本身是一个结构体,则需要继续用.运算符,直到最低一级的成员。

struct Student
{	char name[20];char sex;int number;struct Date{int year;int month;int day;}birthday;}stu1;
printf("%d",stu1.birthday);//这样子是错误的,因为birthday也是一个结构体变量
scanf("%d",&stu1.birthday.month);//正确

(3)可以引用接头体变量成员的地址,也可以引用结构体变量的地址:

printf("%o", student);(输出student的首地址)(%o 按八进制输出)

6结构体数组及其初始化(重点)

这里我们简单说下,具有相同类型的结构体变量组成数组就是结构体数组

结构体数组与结构体变量区别只是将结构体变量替换为数组

struct Student
{ char name[20];char sex;int number;
}stu1[5]={{"zhaozixuan",'M',12345},{"houxiaohong",'M',12306},{"qxiaoxin",'W',12546},{"wangwei",'M',14679},{"yulongjiao",'W',17857}
};
stu1[3].name[3]//表示stu1的第三个结构变量中姓名的第五个字符
//若初始化时已经是结构体数组全部元素[]中的数可以不写如stu1[]=

注意结构体数组要在定义时就直接初始化,如果先定义再赋初值是错误的
比如:

struct Student stu1;
stu1[3]={{"zhaozixuan",'M',12345},{"houxiaohong",'M',12306},{"qxiaoxin",'W',12546}};

这样子是错误的,

这里我在写的时候遇到一些问题,还是结构体数组初始化的问题,折腾了下解决了,给大家分享下
对于数组初始化时
比如

char str[20];
str="I love you";/* 这样会修改数组的地址,但是数组的地址分配之后是不允许改变的 */

在第一条语句中 str就已经被定义成数组而在C99标准中不允许将字符串(实际上是一个指针变量) 赋值给数组,所以如果我们直接赋值是错误的

那么怎么弄呢
这里提供3种方法

1.定义数组时直接定义
char str[20]=“I love you”;

2.用strcpy或者memset函数进行复制
char str[20];
strcpy(str,“I love you”);
再用到memset函数时,出现了一些问题
对于memcset函数简单介绍下

memset
void *memset(void *s,int c,size_t n)
作用:将已开辟内存空间s的首n个字节的值设为值c。

char str[20];
memset(str,'a',20);

如果是字符类型数组的话,memset可以随便用,但是对于其他类型的数组,一般只用来清0或者填-1,如果是填充其他数据就会出错

int str[10];
memset(str,1,sizeof(str));//这样是错误的

这里我们说下这个错误,

首先我们要知道memset在进行赋值时,是按字节为单位来进行赋值的,每次填充的数据长度为一个字节,而对于其他类型的变量,比如int,占4个字节 所以sizeof(str)=40; 而用memset赋值时,将会对指向str地址的前40个字节进行赋值0x01(00000001) 的操作,把0x00000000赋值4次0x01操作变为0x01010101(00000001000000010000000100000001)

相当于给“前10个int”进行了赋值0x01010101的操作 对应十进制的16843009
所以会出很大的错误

这里请务必要注意,但是如果是清零一个数组用memset还是很方便的
简单使用的话同学们用strcmp函数就行

3用指针(注意内存分配)
char *str;
str=“I love you”;

这两句话的本质是,在内存中开辟一段内存空间,把"I love you"放进这段内存空间,然后把这段内存空间的地址交给str,由于str是变量,所以给它赋值是合法的。

请注意,在我们进行数组初始化的时候如果定义的数组过长,而我们只初始化了一部分数据,对于未初始化的数据如果是数值型,则会自动赋值为0,对于字符型,会自动赋初值为NULL,即‘\0’ 即不足的元素补以默认值
这里我们在4小节中也提到了
比如

int str[10]={1};//这里只是把str的第一个元素赋值为1,其他元素默认为0



7结构体与指针

我们知道,指针指向的是变量所占内存的首地址,在结构体中,指针指向的是结构体变量的起始地址,当然也可指向结构体变量的元素
这里我们分为三部分
7.1指向结构体变量的指针

定义形式一般为
struct 结构体名* 指针名;
比如: struct Student* p;

struct Student
{	char cName[20];int number;char csex;  
}student1;
struct Student*p;
p=&student1;
//若为结构体数组则
struct Student stu1[5];
struct Student*p;
p=stu1;//因为stu1为结构体数组而p=stu1直接是指向stu1的首地址,就不用再加&符

用结构体指针变量访问结构体变量成员有以下两种方式:
(*p).cName //这里的括号不能少,在5.1中有提到
p->cName

简单来说以下三种形式是等价的

p->cName
(*p).cName 
student1.cName
p->cName //可以进行正常的运算

p->number++; 是将结构体变量中number的值进行运算,然后再加一,
这里要注意下,等下在7.2中会有比较

7.2指向结构体数组的指针

7.1中我们已经提到结构体数组指针的命名,这里我们仅对一些知识点做下介绍
这里我们接着来说结构体数组指针
在我们想要用指针访问结构体数组的第n个数据时可以用

struct Student stu1[5];
struct Student*p;
p=stu[n];
(++p).number//是指向了结构体数组下一个元素的地址

7.3结构体成员是指针类型变量
比如

struct Student
{char* Name;//这样防止名字长短不一造成空间的浪费int number;char csex;  
}student1;

在使用时可以很好地防止内存被浪费,但是注意在引用时一定要给指针变量分配地址,如果你不分配地址,结果可能是对的,但是Name会被分配到任意的一的地址,结构体不为字符串分配任何内存存储空间具有不确定性,这样就存在潜在的危险,

struct Student
{char* Name;int number;char csex;  
}stu,*stu;stu.name=(char*)malloc(sizeof(char));//内存初始化

这里我们说一下,同学们看书的时候一般不会看到,
如果我们定义了结构体指针变量,他没有指向一个结构体,那么这个结构体指针也是要分配内存初始化的,他所对应的指针类型结构体成员也要相应初始化分配内存

struct Student
{char* Name;int number;char csex;  
}stu,*stu;
stu = (struct student*)malloc(sizeof(struct student));./*结构体指针初始化*/stu->name = (char*)malloc(sizeof(char));/*结构体指针的成员指针同样需要初始化*/  

7.4二叉树遍历算法
二叉树的二叉链表类型定义如下:
typedef struct btnode {
datatype data;
struct btnode *lchild,*rchild;
};
这里我们仅仅提出以下,因为涉及到链表,感兴趣的同学可以去学习下(二级要用),
7.5结构体作为函数参数

首先我们要注意的一点,使用结构体变量作为函数参数的时候,采取的是值传递的方式,将结构体所占内存单元的内容全部传递给形参,并且形参必须也要是同类型的结构体变量,在使用时,会自动创建一个结构体变量作为原变量的副本,并且也需要占内存,并且在调用期间如果修改(形参)结构体中成员的值,修改值是无效的

而如果用指针作为实参,传递给函数的形参,这时候传递的是结构体的地址,形参所指向的地址就是结构体变量的地址,这时候进行修改的话是可以修改的,这正是指针的精华所在

在这里我们再提供几种互换两个结构体的方法

struct Student
{char cName[20];int number;char csex;  
}student1,student2;
struct Student student1={"Wang",12345,'W'};
struct Student student2={"Zhao",54321,'M'}; 
struct Student*stu1=&student1;
struct Student*stu2=&student2;struct Student *student3;
student3=stu1;
stu1=stu2;
stu2=student3;//互换地址2对于同类型结构体直接互换值就行
struct stu student3;
student3=student1;
student1=student2;
student2=student3;
//这里也可以写成应strcmp函数互换3memcpy()函数进行互换4比较笨的方法: 用for循环互换

最后提下memset清空结构体

struct Student
{char cName[20];int number;char csex;  
}stu1;一般情况下,清空str的方法:str.cName[0]='\0';str.csex='0';str.number=0;但是我们用memset就非常方便:memset(&str,0,sizeof(struct Student));如果是数组:struct Student stu[10];就是memset(stu,0,sizeof(struct Student)*10);

整理不易,点个赞再走呗!


http://chatgpt.dhexx.cn/article/IErM9sPl.shtml

相关文章

C语言----初始结构体详解

此文是作者初次学习C语言的时候,写的笔记博文,如有错误请及时指正,谢谢!!! 目录 1,什么是结构体 2. 结构体的声明 2,1结构体声明 2,2结构体成员的类型 2,…

openVAS 介绍

含义及作用: OpenVAS是开放式漏洞评估系统,也可以说它是一个包含着相关工具的网络扫描器。其核心部件是一个服务器,包括一套网络漏洞测试程序,可以检测远程系统和应用程序中的安全问题。 OpenVAS 的功能包括非认证测试、认证测…

040 OpenVAS的下载与安装

文章目录 一:OpenVAS的下载二:OpenVAS的安装2.1:中文版本步骤2.2:英文版本步骤 一:OpenVAS的下载 OpenVAS是开源的网络漏洞扫描器,自从Nessus收费了之后,分支出来的免费的 官网:htt…

Linux 漏洞扫描 openvas

什么是OpenVAS OpenVAS是一款开源的漏洞扫描攻击,主要用来检测网络或主机的安全性。其强大的扫描能力来自于集成数万个漏洞测试程序,这些测试程序以插件的形式提供,可以从官方网站免费更新. 一套完整的OpenVAS系统如下图显示 ① 客户层组件 …

关于openvas

目录 一:什么是openvas 二:作者 三:系统简介 四:建立架构 五:openvas功能 六:OpenVAS的核心组件 七:OpenVAS部署 八:openvas的组件构成 一:什么是openvas OpenV…

openvas

目录 一、openvas的简介 1、简介 2、建立的构架 二、openvas的环境 三、openvas的组件构成 四、openvas安装及使用 一、openvas的简介 1、简介 OpenVAS是开放式漏洞评估系统,也可以说它是⼀个包含着相关⼯具的⽹络扫描器。其核⼼部件是⼀个服务器,包括…

风险评估系统OpenVAS配置使用教程说明

penVAS是一款免费的开放式风险评估工具,可以检测远程系统和应用程序中的安全问题。最初作为Nessus一个子工具,被称为 GNessUs。其特点就是允许继续免费开发。最早是由Portcullis Computer security公司的渗透测试人员发布的,之后由Slashdot网…

OpenVas 漏洞扫描器使用教程

Openvas简介 Openvas是开源的,是Nessus项目分支,用于管理目标系统的漏洞,检测目标网络或主机的安全性。它的评估能力来源于数万个漏洞测试程序,openvas 早起版本还有一个客户端,现在的版本已经不提供客户端程序&#…

OpenVAS介绍

一、背景: OpenVAS(Open Vulnerability Assessment System)是开放式漏洞评估系统,其核心部分是一个服务器。该服务器包括一套网络漏洞测试程序,可以检测远程系统和应用程序中的安全问题。OpenVAS不同与传统的漏洞扫描…

openVAS简介

文章目录 openVAS一、openVAS开发团队二、openVAS背景三、openVAS作用四、openVAS部署kali部署部署openVAS镜像简单使用新建目标(target)新建任务(tasks)开始扫描扫描详情下载报告 openVAS O p e n V A S 是 开 放 式 漏 洞 评 估…

OpenVAS 安装及基本配置

OpenVAS 安装及基本配置 1 .概要 系统简介 OpenVAS是开放式漏洞评估系统,也可以说它是一个包含着相关工具的网络扫描器。其核心部件是一个服务器,包括一套网络漏洞测试程序,可以检测远程系统和应用程序中的安全问题。建立架构 OpenVAS是…

5.数据验证

数据验证 1、数据绑定失败:比如需要数字却输入了字母; 2、数据不合法:可以认为是业务错误,通过自定义验证器验证,如用户名长度必须在5-20之间,我们却输入了100个字符等; 3、错误对象&#xff1a…

认证篇——消息认证码

目录 一、消息认证码介绍 1)消息认证码的使用步骤: 2)消息认证码的密钥配送问题: 3)消息认证码的实例: 4)消息认证码的实现: 二…

获得验证信息

用的最多的验证信息为URL,TEXT,TITLE 以百度为例 from selenium import webdriverurl "https://www.baidu.com/" driver webdriver.Chrome() driver.get(url) driver.maximize_window() ### 获取当前页面的title title driver.title prin…

密码框验证信息

效果展示&#xff1a; 代码展示 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><link rel"stylesheet" type"text/css" href"fontss2/iconfont.css" /><style&g…

信息认证技术

这个章节会介绍一些比较有意思的东西&#xff0c;例如哈希函数&#xff0c;数字签名&#xff0c;身份认证等&#xff01; 1.一个安全的认证 系统应满足什么样的条件&#xff1f; 合法的接受者能够检验所接受消息的合法性和真实性 合法的发送方对所发送的消息无法进行否认 除了合…

验证登录信息

验证登录信息 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 开发工具与关键技术&#xff1a;Visual Studio 作者&#xff1a;李继金 撰写时间&#xff1a;2019年 5月24日 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 每个项目都会有登录页面&#xff0c;…

如何识别验证码

http://www.jianblog.com/2009/02/09/574/ 周俭 Blog 基于OpenCV的PHP图像人脸识别技术 二月 9, 2009 at 10:13 上午 由 catch Filed under PHP, 编程 今天无意中发现用OpenCV可以实现人脸识别。找个时间研究下用来识别验证码看可行不&#xff1f;呵呵。。 安装测试了一下&am…

功能验证流程

下图显示了功能验证流程&#xff1a; 这个验证过程可以被分解成三个主要阶段&#xff1a; 制定验证策略和验证计划&#xff1b; 创建验证平台&#xff0c; 运行和调试&#xff1b; 覆盖率分析和回归测试&#xff1b; &#xff11; 制定验证策略和验证计划阶段 制定验证策略和验…

Javascript正则表达式实现输入框验证信息

1.了解正则表达式可以: 测试字符串的某个模式。例如&#xff0c;可以对一个输入字符串进行测试&#xff0c;看在该字符串是否存在一个电话号码模式或一个信用卡号码模式。这称为数据有效性验证替换文本。可以在文档中使用一个正则表达式来标识特定文字&#xff0c;然后可以全部…