【数据结构C语言】【入门】【首次万字详细解析】入门阶段数据结构可能用到的C语言知识,一章让你看懂数据结构!!!!!!!

前言欢迎各位光临本博客,这里小编带你直接手撕入门阶段的数据结构的C语言知识,让你不再看见数据结构就走不动道。文章并不复杂,愿诸君耐其心性,忘却杂尘,道有所长!!!!

目录

 一、数组

1.1一维数组的定义和创建

一维数组数组的创建方式:

创建例子:

图解:(以整型数组为例)

 1.2一维数组的初始化

1.2.1整型数组的初始化

 1.3字符数组的初始化

3.数组的使用

1.数组的引用

2.数组的输入

 3.数组的输出

 数组传参(重点!!!!!)

一维数组传参:

一维数组传参的本质:

二维数组传参的本质:

2.指针变量

2.1内存与地址

1.1内存

二、 指针变量和地址

2.1 取地址操作符(&)

2.2、指针变量和解引用操作符(*)

2.2.2 指针类型的拆分

2.2.3 解引用操作符

2.2.4指针变量的大小

小知识:typedef重命名:

3.动态内存管理

一. malloc 和 free

1. malloc

2. free

二. calloc

三. realloc

情况1:

情况2:

​编辑情况3:

4.结构体

一.结构体

1.结构体的声明

2.结构体变量成员访问操作符

3.结构体传参

4.匿名结构体

5.结构的自引用

结尾祝福语


 一、数组

如有有已经知道基础概念的小伙伴,直接根据目录表跳转到数组函数传参: 

1.1一维数组的定义和创建

数组的定义其实非常非常简单;他就是是一组相同类型的集合不理解也没关系,我给你举个例子就好了。

数组的定义:数组是一组相同类型的集合

给你讲个故事:

我认识一个朋友(纯属虚构),她是一个事业心非常强的人,无论任何东西都会以事业和学业为主,所以大多数有选择时,都会选择先忽略自己的感受的选项,平时也不注重打扮,以至于她的家非常乱,家里杂乱无章,到处找东西找不到。后来她喜欢上一个男生,感觉自己的一股屌丝样子配不上他,所以就开始捯饬自己,她开始把自己的房间整理,把袜子和袜子归类到一起,衣服和衣服归类到一起,所有一切的事物都开始重新归类。stop!!!!!!

对的,这里的袜子和袜子归类到一起,衣服和衣服归类到一起结果就是数组因为他们都是相同类型的集合!!!!!!

后来的后来,她也确实谈上了恋爱,但故事的结局,我相信应该由大家书写!!!

回归正题......................................讲完数组是什么,我们接下来看一下他的初始化创建


1.一维数组数组的创建方式:

type    arr_name[常量值]

   类型+数组名[元素值/下标]

 

创建例子:
                  int arr[10];//创建一个元素数为10的整型数组double arr[10];//创建一个元素数为10的双浮点数数组char arr[10]; //创建一个元素数为10的字符数组
图解:(以整型数组为例)
int arr[10] 详细图解

我们知道一维数组是如何创建的了,那么接下来我们看他如何初始化

数组的初始化种类就好几种,但我会指出最常用的几种,大家记住即可。 


 1.2一维数组的初始化

1.整型数组的初始化

 首先先来说数组的一种初始化,也是最常用的初始化:

指在创建的基础上给一个或多个合理的值;而每个类型的初始化又存在差异

本章的初始化我们就来讨论其差异性,这里没啥大用,做了解即可,看我慢慢给你解释!!!!

数组分为完全初始化不完全初始化


 先来看一个不完全初始化

一维数组的初始化图

解释:arr与[10]结合,说明我们定义了一个空间为10个的数组int表示空间的每个元素是为整型

{1}在这里是什么意思?为什么定义了十个空间,这里只有一个元素?

这里叫做不完全初始化,后面给的初始化元素数少于定义的元素数, 后面的元素要我们在后续的程序中自己定义。同时这里的1赋值给了数组的第一个元素


我们用VS2022编译器进行F11调试,打开监控界面,看一下arr数组的面貌:

这里可以看到,只有第一个元素arr[0]被赋值了,所以数组的赋值从下标由小到大依次赋值的!!!

我们了解完这一个之后,我们可以面对大部分的数组定义了,同时我也列出其他几种常见的类型,这样你就在数组的知识点看懂大部分代码

常见的初始化类型:

  •     int arr1[5] = {1,2,3,4,5};//完全初始化初始化数跟元素数相同,每个数字都有家可寻。
     
  •     int arr2[6] = {1};//不完全初始化第⼀个元素初始化为1,剩余的元素默认初始化为0 
     
  •     int arr3[3] = {1, 2, 3, 4};//错误的初始化 初始化项太多 【违法越界】
     
  •     int arr4[] = {};//错误的写法【初始化和元素数必须要有一个!!!!不然编译器无法识别】
     
  •     int arr5[] = {0};//特殊初始化//通过初始化的个数,判断元素的个数,只有1个元素
     
  •     int arr6[] = {1,2,3};//特殊初始化//有3个元素
     
  •     char arr7[]="abc";//字符数组初始化//字符数组【字符串定义数组】
     

 

 2.字符数组的初始化

下面就是的3种书写方式:

 //   char ch[9] = {0};//不完全初始化
//    char ch2[9] = { 'a','b','c'};//各字符初始化
//    char ch3[9] = "abc";//字符串初始化

 不管如何,字符初始化的就这三种方式,接下来我们来重点看一下两种初始化的对比({ 'a','b','c'}和"abc"):

探索方法:F10进入分布调试页面,F11分步调节,在监视页面,输入数组名,观看其储存形式。

问题一:字符类型的ch数组为首位‘0’但是在内存中其他元素是什么哪?

字符数组ch监视图 

解答:观察监视图可知:字符数组的首元素0以‘/0’的方式储存在内存中。 

问题二:字符类型的数组ch2中的'a','b','c'是怎么储存的,以及ch3和ch2如此相近,是怎么储存的??相同吗?

字符数组ch2和ch3的初始化监视图 

解答:监视图可知,两者书写方式虽然不同,但是储存形式是相同的,所以在初始化书写中,是一样的,但要注意的是ch2中的字符是单引号'a',ch3中的数组是双引号"" 


!!!!!!!!!!!!!!    下    标       !!!!!!!!!!!!!!!!!

这是新手很容易犯的错误知识点,本萌新也是,一定要记得下标从0开始,

   请让我为大家讲解:

                                     int arr[10]={1,2,3,4,5,6,7,8,9,10}
 

                                   元素 1,2,3,4,5,6,7,8,9,10

                                   下标 0,1,2,3,4,5,6,7,8, 9

虽然元素是1—-10,但是下标是0——9,所以在引用是数组是从arr[0]——arr[9],没有arr[10]

这一点一定要注意!!!!!


1.3.数组的使用

能够定义数组,那么就要提到如何使用数组和数组的输入和输出

即三个模块1.一维数组的引用 2.一维数组的输入3.一维数组的输出

1.数组的引用

假设定义了 int arr[10]={1,2,3}

                  int c=0;

引用:c=arr[9];


 

2.数组的输入

因为数组是一个多数字的集合,不可能一次性输完,所以要用到循坏语句进行循环输入,讲每个输入的值储存到对应的数组的元素中,直到达到元素值为止。

一个元素一个元素的输入,中间用空格隔开哈!!!!!

  for (i = 0; i < 10; i++)
   {
     scanf("%d", &arr[i]);
   }


 

 3.数组的输出

与输入类似,在循坏的基础上逐个进行输出,逐个将每个元素进行输出。

一个元素一个元素的有序输出!!!!

  for (i = 0; i < 10; i++)
   {
     printf("%d", arr[i]);
   }


 

 数组传参(重点!!!!!)

这里由于篇幅原因,我们本篇只分析数据结构中常见的几种方式,具体各种数组传参详情请见

【C语言指南】数组传参规则详解_如何传一个实参数组-CSDN博客

1.一维数组传参:
#include<stdio.h>
void Print(int arr2[], int sz)
{int i = 0;for (i = 0;i < sz;i++){printf("%d ", arr2[i]);}
}
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr1) / sizeof(arr1[0]);//求数组元素的个数Print(arr1, sz);return 0;
}

我们可以发现arr1和arr2的地址相同,说明实参传递给形参时,形参并没有开辟新的空间,说明形参和实参是同⼀个数组,同时arr2的类型居然是int*类型(指针变量),其实数组传参,传递的是数组首元素的地址,通过地址可以找到一个个元素。若重新开辟一个新的数组会消耗大量的内存,所以传递的不是数组而是地址!


2.一维数组传参的本质:
#include <stdio.h>
void test(int arr[])//重点
{int sz2 = sizeof(arr) / sizeof(arr[0]);printf("sz2 = %d\n", sz2);
}
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz1 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);//重点return 0;
}

我们发现在函数内部是没有正确获得数组的元素个数,这又是为什么呢?你也许会想,指针怎么这么…(此处省略一万字),要尝试先接受它,以后学习多了自然都解释地清了。

  • 这就要学习数组传参的本质了,上个小节我们学习了:数组名是数组首元素的地址;那么在数组传参的时候,传递的是数组名,也就是说本质上数组传参传递的是数组首元素的地址。所以函数形参的部分理论上应该使用指针变量来接收首元素的地址
     
  • 那么在函数内部我们写sizeof(arr) 计算的是⼀个地址的大小(单位字节)而不是数组的大小(单位字节)。正是因为函数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。
     
  • 那形参为什么可以写成数组的形式呢?这是因为C语言考虑到了学者的感受,在学习数组的时候,如果一来就传地址,形参用指针变量来接收,学者会非常地疑惑的。所以说C语言并不是这么冷若冰霜的。

总结⼀维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。


3.二维数组传参的本质:
#include <stdio.h>
void test(int a[3][5], int r, int c)
{int i = 0;int j = 0;for (i = 0; i < r; i++){for (j = 0; j < c; j++){printf("%d ", a[i][j]);}printf("\n");}
}
int main()
{int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };test(arr, 3, 5);return 0;
}

 这里实参是⼆维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?

重点:

  • 二维数组在内存中是连续存储的。
  • 二维数组可以理解为一维数组的数组,二维数组的每一行可以看作是一个一维数组。
  • 二维数组名也是首元素的地址,这里的首元素是指第一行数组,传过去的是第一行这个一维数组的地址,也就是arr[0]的地址。
  • 第一行的⼀维数组的类型就是 int [5] ,所以第一行的地址的类型就是数组指针类型 int(*)[5] 。

 

⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式,如下:

#include <stdio.h>
void test(int(*p)[5], int r, int c)
{int i = 0;int j = 0;for (i = 0; i < r; i++){for (j = 0; j < c; j++){printf("%d ", *(*(p + i) + j));//等价于p[i][j]}printf("\n");}
}
int main()
{int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };test(arr, 3, 5);return 0;
}
  • p:数组首元素的地址,也就是一维数组arr[0]的地址。
  • p+i:跳过 i 个 int[5] 这样的数组(p的类型是数组指针),指向arr[i],p+i 就是一维数组 arr[i] 的地址。
  • *(p+i):访问一维数组arr[i],等价于一维数组arr[i],而 arr[i] 是数组名,又是数组首元素的地址,也就是 arr[i][0] 的地址。
  • *(p + i) + j:由于 *(p+i)是 arr[i][0] 的地址,所以 +j 跳过 j 个整形(指向整形),也就是 arr[i][j] 的地址。
  • *( *(p + i) + j):由于 *(p + i) + j 是 arr[i][j] 的地址,进行解引用操作,就是找到 arr[i][j]。
  • 最终:*( *(p + i) + j) 等价于 arr[i][j]。

 

如图:
在这里插入图片描述
 

二、指针变量

指针的内容有非常多,所以这里我们只讲常用的,入门的,比较容易理解的,如果各位对指针感兴趣,可以跳转到我的另一篇博客的指针系列【1】,这里有更加详细的内容【超详细指针系列】指针超详细讲解------从入门到应用-----一步一步将你带入深挖指针【1】_指针教程-CSDN博客


 

2.1内存与地址

1.内存

单讲内存和地址太枯燥,来举个例子吧:

一天小玉突然想起了好多年不联系的挚友阿雪,想联络联络感情,所以想要去啊雪家,所以打了电话:

小玉:”牢底,最近怎么样“

阿雪:“你是??”

小玉:“嗯?不至于我的声音都听不出来吧”

阿雪:“哈哈哈,原来是你,怎么了??”

小玉:“好久没见,我可以找你去打CSGO学C语言吗?"

阿雪:"哈哈哈哈,好呀,好久没见你了,对了,你不知道我家地址吧”

阿雪:“我发你”

小玉:”嗯嗯,多时不见,期于君遇“

阿雪:“滚,别在我面前犯二哈哈哈哈哈”

小玉:“哈哈哈哈哈哈”

(对话结束)


!!其实在这里这段对话中,已经显示出了内存的本质

!!小雪给出的地址面向的对象----楼层,其实就是就是内存,内存嘛,其实就是存东西的地方;


可问题来了,小玉到达了小区,看到了小雪的单元楼(内存),而接下来问题就出现了,那间房子是哪,所以小玉问了小雪房间号。。。。。

对!!!,这么大个单元楼,怎么多房间,要怎么分辨小雪的房间哪?

所以我们给这里的每一个内存单元(房间)编制了房间号

一楼:101、102、103、104........

二楼:201、202、203、204........

三楼:.......................

说回正题:

内存相比大家都不陌生,在你买电脑的时候总会了解到电脑是多少g内存的,如4G/8G/16G/32G而这些到底是怎么划分的哪????

其实,内存不是单独的一个大整体,而是一个大的空间被划分为一个一个的小空间,我们把它叫做内存单元,

每个内存单元都是1个字节。1个字节里面8个比特位,每个比特位用2进制表示,所以一个字节可以表示2^8个情况,每个内存单元都有编号,而这些编号,我们也叫做地址

通俗一点来讲:

内存单元就是一个宿舍,8个比特位就是8人间,内存就是整个宿舍楼而每个宿舍的编号==地址,在c语言中我们给地址起了一个新的名字:指针

所以


宿舍编号==地址==指针


2.2 指针变量和地址

1. 取地址操作符(&)

如果说创建变量是向内存申请空间,但是每个空间都有着属于自己的编号

那么取地址操作符就是把这些编号拿出来,在储存到一个新的内存单元中。

但是问题来了:如int向内存申请4个字节,那么取地址操作符难道要每个字节的地址都拿出来吗

不妨做个程序来探究一下

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{int m = 4;printf("%p", &m);return 0;
}

打印的地址是最小的地址(首元素地址)

同时我们也可以发现,内存是连续存放的!!!! 

2.、指针变量和解引用操作符(*)


2.1指针变量     

 当我们用去地址操作符取出地址时如:0x006FFD69,那么我们将这个东西存在哪里哪?

答案是指针变量!!!!

例子:

#include<stdio.h>int main(){int a=0;int*p=&a;//讲取出的a的地址存放到指针变量p中return 0;}

2.2 指针类型的拆分

举个例子:

int a=10;
 

int *p=&a;

 我们已经知道

  • p的类型是int*类型
  • ‘*’是指p的类型是指针
  • int的意思是指p所指向的对象为整型(指向的是整型(int)的对象)
2.3 解引用操作符

对的,解引用操作符也是我们的 老  朋  友   * 号

int a=10;

int *p=&a;//指针变量存放a的地址

*p=10;//解引用操作将p所指向的对象的值改为10

2.4指针变量的大小

先说结论

指针变量的大小只有两个值:4和8;

!在32位的平台上运作时,指针变量的大小为4。

!在64位的平台上运作时,指针变量的大小为8。

为什么捏???

先直观的感受一下指针变量的大小的运作结果:

    printf("%d\n", sizeof(char*));printf("%d\n", sizeof(int*));printf("%d\n", sizeof(double*));printf("%d\n", sizeof(short*));printf("%d\n", sizeof(float*));

我们看一下32位(x86)的平台下运行的结果

结果显示在x86的平台下运行的结果,无论什么类型都是4个字节。

再看一下64位的(x64)的平台下运行的结果 :

 结果显示都是8个字节


简单来说:

  • 32位机器有32根地址总线,将电信号转换为数字信号时,32个二进制产生的序列,我们可以看作位1个地址的产生,那么一个地址是由32个bite位储存的,32bit==4个字节,所以
  • 32位下的指针变量就是4个字节,64位也相同..........................................
  • 注意指针变量的大小和类型是⽆关的,只要指针类型的变量在相同的平台下,大小都是相同的。!!!

以上内容是指针入门级别,先熟知,之后内容包括,指针类型的意义,指针解引用的权重,指针运算。如果感兴趣,请大家去这一章节开头找到我的另一篇博客,去进行详细了解。 

小知识:typedef重命名:

typedef重命名函数
typedef是用来重命名的,可以将复杂的名字简单化规范化 

比如我们命名了一个结构体叫做jinfsjajngijiasogjoiasjda(随便打的)

我们每次调用都要写很长一段复杂的东西,但是有了typedef这个东西,我们可以将它重命名为js,对!就这两个字符,就可以表达这个结构体

typedef unsigned int uint;

//将unsigned int 重命名uint

如果是指针类型,能否重命名呢?其实也是可以的,比如,将 int* 重命名为 ptr_t ,这样写:

typedef int* ptr_t

但是对于数组指针和函数指针稍微有点区别:
比如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:

typedef int(*parr_t)[5]

函数指针类型的重命名也是⼀样的,比如,将 void(*)(int) 类型重命名为 pf_t ,就可以这样写: 

typedef void(*pfun_t)(int);//新的类型名必须在*的右边

那么要简化代码2,可以这样写:

typedef void(*pfun_t)(int);pfun_t signal(int, pfun_t); 

重点

给出typedef命名一个数组的例子:

#include<stdio.h>
int main()
{
typedef int IntArray[5]//定义一个包含5个int类型元素的数组类型IntArray
IntArray arr={1,2,3,4,5};// 使用 IntArray 声明一个数组for(int i=0;i<5;i++)
{
printf("%d",arr[i]);}
return 0;
}

 

3.动态内存管理

前言:

当我们要开辟一块连续的内存空间时,我们第一时间想到的可能是数组。但是一但开辟了数组,数组的大小就确定了,无法调整数组的大小。
有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。
于是动态内存开辟函数(malloc,calloc,realloc,free)应运而生,下文带您一一了解其中的奥秘。


一. malloc 和 free

1. malloc

void* malloc(size_t size);

解释:在堆区中开辟一块大小为 size 个字节的空间,返回指向这块空间的起始地址(泛型指针void*)。

因为这块空间存放的数据类型不知(由程序员自己确定),所以用泛型指针接收该地址,在使用的时候记得养成一个好习惯:

强制类型转换为自己需要的数据类型。

  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个 NULL 指针,因此malloc的返回值一定要做检查。
  • 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候程序员自己来决定。
  • 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

2. free

void free(void* ptr);

解释:free是用来对动态内存的释放和回收的。free 对指针 ptr 指向的内容释放掉,但是指针仍然指向这块空间,若后面不再使用,及时将 ptr 置为 NULL,否则产生野指针。

如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。

如果参数 ptr 是NULL指针,则函数什么事都不做

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{//在堆区申请10个整形空间int* p=(int*)malloc(10*sizeof(int));if (p == NULL){//开辟空间失败perror("malloc");//打印错误信息//printf("%s\n", strerror(errno));//也是打印错误信息return 1;}//使用这块空间int i = 0;for (i = 0; i < 10; i++){*(p + i) = i + 1;}//打印这块空间for (i = 0; i < 10; i++){printf("%d ", *(p + i));}//释放这块空间free(p);//将这块空间还给了操作系统,我们已经没有权限再使用这块空间了//但是p仍然指向那块空间p = NULL;//若不将p置为NULL,那么p就是野指针return 0;
}

总结:

  • 动态内存开辟的函数头文件都是 stdlib.h
  • 如果不释放的话,程序结束的时候也会被操作系统自动释放。
  • 但是为了防止内存泄漏,将其置为NULL。这是一个好习惯。


二. calloc

void* calloc(size_t num, size_t size);

解释:在堆区中开辟一块大小为 num * size 个字节的空间,返回指向这块空间的起始地址,其中 num 为数据的个数size 为单个数据的字节数,同时把申请的空间的每个字节初始化为全为0。

#include<stdio.h>
#include<stdlib.h>
int main()
{//在堆区申请10个整形空间int* p = (int*)calloc(10, sizeof(int));if (p == NULL){perror("calloc");return 1;}//使用空间int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(p + i));}//释放free(p);p = NULL;return 0;
}

 

三. realloc

void* realloc (void* ptr, size_t size);

解释:调整动态内存开辟的空间,ptr 是那块空间的起始地址,size 是调整后的那块空间的字节的个数,返回指向这块空间的起始地址

#include<stdio.h>
#include<stdlib.h>
int main()
{//在堆区申请10个整形空间int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){perror("malloc");return 1;}//调整空间——变成20个整形空间int* ptr = (int*)realloc(p, 20 * sizeof(int));//注意:要用新的指针来接收if (ptr != NULL){p = ptr;}else{//开辟失败return 1;}int i = 0;for (i = 0; i < 20; i++){*(p + i) = i + 1;}for (i = 0; i < 20; i++){printf("%d ", *(p + i));}//释放free(p);p = NULL;return 0;
}

注意:也许有些人有疑问为什么要用新的指针接收返回的地址,直接用原来的指针接收不行吗?答案是不行的,在realloc调整动态内存开辟的空间有3中情况,代码如下:

int main()
{int* p = (int*)malloc(10);//...if (p != NULL){int* ptr = (int*)realloc(p, 20);//...}return 0;
}



情况1:

开辟的空间后面有足够且连续的空间,只需返回空间的起始地址即可。、

情况2:

如果后续的空间不够,realloc 函数直接在堆区找一块新的满足大小的空间,将旧的地址,拷贝到新的地址。
自动释放旧的地址指向的空间,不需要手动 free,返回新的空间的起始地址。


情况3:

堆区已经没有满足情况的连续空间了,返回NULL。

realloc函数也能开辟空间,代码如下:

#include<stdio.h>
#include<stdlib.h>
int main()
{int* p = (int*)realloc(NULL, 10 * sizeof(int));//等价于malloc(40)if (p == NULL){//...}return 0;
}

 

4.结构体

如果我写的不明白,查看对标链接,感谢各位支持!!!!!!

​​​​​​自定义类型:结构体+结构体内存对齐+结构体实现位段_自定义结构体位对齐如何查询-CSDN博客自定义类型:结构体+结构体内存对齐+结构体实现位段_自定义结构体位对齐如何查询-CSDN博客​​​​​​自定义类型:结构体+结构体内存对齐+结构体实现位段_自定义结构体位对齐如何查询-CSDN博客

前言:
学习了数组后发现数组中的元素只能是相同类型的变量,那么有没有可以存放不同类型的变量呢?
结构体:一些值的集合,这些值称为成员变量,结构体的每个成员可以是不同类型的变量



一.结构体


1.结构体的声明

struct tag
{member-list;//结构体成员列表
}variable-list;//结构体变量列表


例如:描述一个人

struct Person {int age;//年龄char name[50];//姓名float height;//身高
};//封号不能丢


2.结构体变量成员访问操作符

结构体变量.结构体成员名。
结构体指针变量->结构体成员名。

#include <stdio.h>
struct Person
{int age;char name[50];float height;
}p1 = { 20,"zhangsan",185.5 }, * ps;//全局变量(*ps:结构体指针ps)int main()
{struct Person p2 = { 18,"lisi",173.2 };//局部变量struct Person p3 = { 19,"wangwu",180.8 };//局部变量ps = &p3;printf("%d %s %.1f\n", p1.age, p1.name, p1.height);//结构体成员访问操作符:.printf("%d %s %.1f\n", p2.age, p2.name, p2.height);printf("%d %s %.1f\n", (*ps).age, (*ps).name, (*ps).height);printf("%d %s %.1f\n", ps->age, ps->name, ps->height);//结构体成员访问操作符:->等价于先*再.return 0;
}



3.结构体传参

  1. 传结构体。
  2. 传结构体的地址。
#include <stdio.h>
struct Person
{int age;char name[50];float height;
};
void test1(struct Person p)//用结构体接收
{printf("%d %s %.1f\n", p.age, p.name, p.height);
}
void test2(struct Person* p)//用结构体指针接收
{printf("%d %s %.1f\n", p->age, p->name, p->height);
}
int main()
{struct Person p1 = { 20,"zhangsan",185.5 };test1(p1);//传结构体test2(&p1);//传结构体的地址return 0;
}

思考:我们发现二者都可以成功访问结构体成员,那二者有什么区别呢?

  1. 传递结构体时:其实函数内部创建了一个临时结构体变量存放传入的结构体,当结构体很大时会额外占用空间不划算。(本质上是值传递)。
  2. 传递结构体地址时:只需创建4个字节结构体指针变量,通过其来访问结构体成员,可以大大节省空间。(本质上是地址/指针传递)。
  3. 推荐传递结构体地址


4.匿名结构体

//匿名结构体类型 

struct//不完全声明,由于没有名字,无法在其之后创建变量
{int age;char name[50];float height;
}s1, s2;//在结构体声明的时候直接创建变量,不能在其之后创建变量了,只能使用一次
int main()
{struct s3;//error
}


当只需使用一次可以使用(在声明结构体时,直接创建变量,不能在其之后创建变量了)。
思考:以下代码行不行

struct
{int age;char name[50];float height;
}s1;
struct
{int age;char name[50];float height;
}*ps;int main()
{    ps = &s1;//?return 0;
}


答案:不行,看似一样,其实这两个结构体是不同类型的,只是成员变量相同的不同结构体类型,二者不兼容。没有名字导致的问题)。


5.结构的自引用

比如:定义一个链表的节点

struct Node
{int data;//存放数据struct Node* next;//存放指针
};

 

结尾祝福语

以上就是数据结构可能有用到的知识点,如果有人能看到这,我真感动呀!!!!!!,累死我了!!!!我知道你对我的文章是非常认可的,当然如果有错误,欢迎随时来指出,我们一起探讨,如果有疑问,直接私信我即可,看到的第一时间我会给你解答!!!!!!!也请各位给我个三连(点赞收藏评论!!!)

怎么说哪?结束了,第一次写万字文章,肯定有写的不好的地方,多多见谅吧!!!!最后祝你们四级全过,天天开心!!!!!!

 

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

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

相关文章

图片马赛克处理(Java)

1.需求 给图片的指定区域打码给整张图片打码马赛克方格取色支持中心点取色和随机取色马赛克支持灰度处理 2.源码 package com.visy.utils;import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOE…

我们如何通过两个关键测试原则,进行自动化 Kubernetes 配置和Secret测试

现如今&#xff0c;一个上规模的应用程序几乎都会使用 Kubernetes 作为管理环境实现自动扩展、负载平衡&#xff08;auto scaling, load balancing &#xff09;等机制。与之相应的&#xff0c;我们通常会使用一个 repository 专门管理一个组织内部各项不同应用程序在各个环境的…

领夹麦克风怎么样,无线领夹麦克风哪个牌子好,家用麦克风推荐

​作为消费类电子产品&#xff0c;麦克风随着市场需求和技术进步&#xff0c;每年都有新产品系列涌现&#xff0c;特别是领夹麦克风&#xff0c;近年来经历了显著的市场变革和技术突破。从早期的新闻采访、节目录制和影视后期录音中常用的无线小蜜蜂话筒&#xff0c;到如今在网…

如何选择公司

前言&#xff1a; 了不起学弟&#xff1a;学长啊&#xff0c;我这手上有几个offer&#xff0c;有几家不同种类的公司&#xff0c;我这该怎么选啊。。。 了不起&#xff1a;这确实是在正在找工作的同学一定会遇到的问题&#xff0c;如何选择公司。那我就给你讲一讲吧。 正文&…

6张图掌握提示词工程师工作范围与工作技巧(提示词原理篇)

在人工智能的疆域中&#xff0c;提示词工程师扮演着至关重要的角色。他们精心设计的话语&#xff0c;是引导AI模型理解人类需求、激发创造力的关键。如同指挥官的号令&#xff0c;提示词工程师的每一个提问&#xff0c;都让AI的潜力得到释放&#xff0c;让技术与智慧的对话更加…

u-code-input结合u-keyboard实现支付密码+数字键盘

u-code-input结合u-keyboard实现支付密码数字键盘 一、需求描述、框架&#xff08;一&#xff09;技术框架&#xff08;二&#xff09;需求 二、效果图三、代码实现&#xff08;一&#xff09;u-code-input组件小改造&#xff08;二&#xff09;功能实现 一、需求描述、框架 &…

java 异常-Exception

异常的概念 Java 语言中&#xff0c;将程序执行中发生的不正常情况称为“异常”。&#xff08;开发过程中的语法错误和逻辑错误不是异常&#xff09; 执行过程中所发生的异常事件可分为两大类 &#xff08;1&#xff09;Error&#xff08;错误&#xff09;&#xff1a;Java 虚…

[Python]案例驱动最佳入门:股票分析 Python数据分析与可视化实战

在股票市场中&#xff0c;价格波动和投资决策紧密相连。通过数据分析&#xff0c;投资者可以识别市场趋势、价格波动背后的规律&#xff0c;并做出明智的投资决策。Python凭借其强大的数据处理和可视化功能&#xff0c;在金融市场分析中被广泛应用。本文将通过一个假设的股票市…

MySQL基础基础篇 - SQL

01 SQL通用语法 02 SQL分类 03 DDL语句 04 DML语句 05 DQL语句(单表查询) 05_01 学习总览 05_02 基本查询 05_03 条件查询 【应用实例】&#xff1a; 05_04 聚合函数 05_05 分组查询 05_06 排序查询 05_07 分页查询 【boss题目】&#xff1a; 05_08 执行顺序 06 DCL语句 【概…

如何设计出一个比较全面的测试用例

目录 1. 测试用例的基本要素(不需要执行结果) 2. 测试用例的给我们带来的好处 3. 用例编写步骤 4. 设计测试用例的方法 4.1 基于需求进行测试用例的设计 4.2 具体的设计方法 1.等价类 2.边界值 3.判定表&#xff08;因果图&#xff09; 4.正交表法 5.场景设计法 6.错误猜测…

JavaSE——String类

一、字符串构造 注意&#xff1a;String是引用类型&#xff0c;内部并不存储字符串本身。 有三种方式&#xff1a; public class Test1 {public static void main(String[] args) {// 使用常量串构造String s1 "hello java";System.out.println(s1);// 直接newSt…

20240921全国计算机二级Python考试(大头博士计算二级)

一、背景需求&#xff1a; 20240921我在上海应用技术大学44号楼考场参加2024年9月的全国计算机二级&#xff08;Python语言程序设计&#xff09;考试。 时隔多年&#xff0c;再次来到大学校园&#xff0c;恍若隔世 扫码找考场在哪里 考场须知 1、进考场&#xff0c;先刷身份证…

局域网内远程桌面怎么设置?3个远程桌面2个小技巧搞定!

在局域网内设置远程桌面&#xff0c;主要可以通过Windows系统自带的远程桌面功能来实现。 同时也可以借助一些专业的远程桌面软件来增强功能和安全性。 以下是详细的设置步骤及两个小技巧&#xff1a; 一、Windows系统自带远程桌面设置 1.启用远程桌面 在被控制的电脑上&am…

剑指offer JZ54 二叉搜索树的第k个节点

描述&#xff1a; 给定一棵结点数为n 二叉搜索树&#xff0c;请找出其中的第 k 小的TreeNode结点值。 1.返回第k小的节点值即可 2.不能查找的情况&#xff0c;如二叉树为空&#xff0c;则返回-1&#xff0c;或者k大于n等等&#xff0c;也返回-1 3.保证n个节点的值不一样 如…

李宏毅机器学习2023HW12—Reinforcement Learning强化学习

文章目录 TaskBaselineSimpleMedium Baseline—Policy GradientStrong Baseline——Actor-CriticBoss Baseline—Mask Task 实现深度强化学习方法: Policy GradientActor-Critic 环境&#xff1a;月球着陆器 Baseline Simple 定义优势函数(Advantage function)为执行完ac…

C++之Person类

首先设置头文件&#xff0c;将题目中的要求完成。 #include <iostream>using namespace std;class Person { public:Person();Person(string name, int id, string address);~Person();void setPerson(string name, int id, string address);void setName(string name);…

Kotlin编程全攻略:从基础到实战项目的系统学习资料

Kotlin作为一种现代、简洁的编程语言&#xff0c;正逐渐成为Android开发的新宠。本文将为您介绍一套全面的Kotlin学习资料&#xff0c;包括学习大纲、PDF文档、源代码以及配套视频教程&#xff0c;帮助您从Kotlin的基础语法到实战项目开发&#xff0c;系统地提升您的编程技能。…

2024年中国研究生数学建模竞赛B题(华为题目)WLAN组网中网络吞吐量建模一

2024年中国研究生数学建模竞赛B题&#xff08;华为题目&#xff09; WLAN组网中网络吞吐量建模 一、背景 无线局域网&#xff08;Wireless Local Area Network&#xff0c;WLAN&#xff09;是一种无线计算机网络&#xff0c;使用无线信道作为传输介质连接两个或多个设备。WL…

什么情况下会导致索引失效?

什么情况下会导致索引失效&#xff1f; 1. 组合索引非最左前缀2. LIKE查询%开头3. 字符串未加引号4. 不等比较5. 索引列运算6. OR连接查询 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1. 组合索引非最左前缀 描述&#xff1a;在组合索引…

Linux之实战命令02:shred应用实例(三十六)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…