【数据布局&&C语言】【入门】【首次万字具体分析】入门阶段数据布局可能用 ...

莱莱  论坛元老 | 2024-9-22 10:24:55 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1870|帖子 1870|积分 5610

前言:欢迎各位光临本博客,这里小编带你直接手撕入门阶段的数据布局的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[常量值]

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

   
创建例子:

  1.                   int arr[10];//创建一个元素数为10的整型数组
  2.                   double arr[10];//创建一个元素数为10的双浮点数数组
  3.                   char arr[10]; //创建一个元素数为10的字符数组
  4.  
复制代码
图解:(以整型数组为例)

   
     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);
   }
  
 
 3.数组的输出

与输入类似,在循坏的基础上逐个举行输出,逐个将每个元素举行输出。
一个元素一个元素的有序输出!!!!
     for (i = 0; i < 10; i++)
   {
     printf("%d", arr);
   }
  
 
 数组传参(重点!!!!!)

这里由于篇幅原因,我们本篇只分析数据布局中常见的几种方式,具体各种数组传参详情请见
【C语言指南】数组传参规则详解_怎样传一个实参数组-CSDN博客
1.一维数组传参:

  1. #include<stdio.h>
  2. void Print(int arr2[], int sz)
  3. {
  4.     int i = 0;
  5.     for (i = 0;i < sz;i++)
  6.     {
  7.         printf("%d ", arr2[i]);
  8.     }
  9. }
  10. int main()
  11. {
  12.     int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  13.     int sz = sizeof(arr1) / sizeof(arr1[0]);//求数组元素的个数
  14.     Print(arr1, sz);
  15.     return 0;
  16. }
复制代码

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

2.一维数组传参的本质:

  1. #include <stdio.h>
  2. void test(int arr[])//重点
  3. {
  4.         int sz2 = sizeof(arr) / sizeof(arr[0]);
  5.         printf("sz2 = %d\n", sz2);
  6. }
  7. int main()
  8. {
  9.         int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  10.         int sz1 = sizeof(arr) / sizeof(arr[0]);
  11.         printf("sz1 = %d\n", sz1);
  12.         test(arr);//重点
  13.         return 0;
  14. }
复制代码
我们发现在函数内部是没有正确获得数组的元素个数,这又是为什么呢?你大概会想,指针怎么这么…(此处省略一万字),要实行先担当它,以后学习多了自然都解释地清了。


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

3.二维数组传参的本质:

  1. #include <stdio.h>
  2. void test(int a[3][5], int r, int c)
  3. {
  4.         int i = 0;
  5.         int j = 0;
  6.         for (i = 0; i < r; i++)
  7.         {
  8.                 for (j = 0; j < c; j++)
  9.                 {
  10.                         printf("%d ", a[i][j]);
  11.                 }
  12.                 printf("\n");
  13.         }
  14. }
  15. int main()
  16. {
  17.         int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };
  18.         test(arr, 3, 5);
  19.         return 0;
  20. }
复制代码
 这里实参是⼆维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?
重点:


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

 
⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式,如下:
  1. #include <stdio.h>
  2. void test(int(*p)[5], int r, int c)
  3. {
  4.         int i = 0;
  5.         int j = 0;
  6.         for (i = 0; i < r; i++)
  7.         {
  8.                 for (j = 0; j < c; j++)
  9.                 {
  10.                         printf("%d ", *(*(p + i) + j));//等价于p[i][j]
  11.                 }
  12.                 printf("\n");
  13.         }
  14. }
  15. int main()
  16. {
  17.         int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };
  18.         test(arr, 3, 5);
  19.         return 0;
  20. }
复制代码


  • p:数组首元素的地址,也就是一维数组arr[0]的地址。
  • p+i:跳过 i 个 int[5] 如许的数组(p的类型是数组指针),指向arr,p+i 就是一维数组 arr 的地址。
  • *(p+i):访问一维数组arr,等价于一维数组arr,而 arr 是数组名,又是数组首元素的地址,也就是 arr[0] 的地址。
  • *(p + i) + j:由于 *(p+i)是 arr[0] 的地址,所以 +j 跳过 j 个整形(指向整形),也就是 arr[j] 的地址。
  • *( *(p + i) + j):由于 *(p + i) + j 是 arr[j] 的地址,举行解引用操作,就是找到 arr[j]。
  • 终极:*( *(p + i) + j) 等价于 arr[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个字节,那么取地址操作符岂非要每个字节的地址都拿出来吗
不妨做个程序来探究一下
  1. #define _CRT_SECURE_NO_WARNINGS 1
  2. #include<stdio.h>
  3. int main()
  4. {
  5.         int m = 4;
  6.         printf("%p", &m);
  7.     return 0;
  8. }
复制代码


打印的地址是最小的地址(首元素地址)
同时我们也可以发现,内存是一连存放的!!!! 

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


2.1指针变量     

 当我们用去地址操作符取出地址时如:0x006FFD69,那么我们将这个东西存在哪里哪?
答案是指针变量!!!!
例子:
  1. #include<stdio.h>
  2. int main()
  3. {
  4. int a=0;
  5. int*p=&a;//讲取出的a的地址存放到指针变量p中
  6. return 0;
  7. }
复制代码


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。
  为什么捏???
先直观的感受一下指针变量的大小的运作结果:
  1.     printf("%d\n", sizeof(char*));
  2.     printf("%d\n", sizeof(int*));
  3.     printf("%d\n", sizeof(double*));
  4.     printf("%d\n", sizeof(short*));
  5.     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 ,就可以如许写: 
  1. typedef void(*pfun_t)(int);//新的类型名必须在*的右边
复制代码
那么要简化代码2,可以如许写:
  1. typedef void(*pfun_t)(int);
  2. pfun_t signal(int, pfun_t); 
复制代码
重点
给出typedef命名一个数组的例子:
  1. #include<stdio.h>
  2. int main()
  3. {
  4. typedef int IntArray[5]//定义一个包含5个int类型元素的数组类型IntArray
  5. IntArray arr={1,2,3,4,5};// 使用 IntArray 声明一个数组
  6.  
  7. for(int i=0;i<5;i++)
  8. {
  9. printf("%d",arr[i]);
  10.  
  11. }
  12. return 0;
  13. }
复制代码

 
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指针,则函数什么事都不做
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<string.h>
  4. int main()
  5. {
  6.     //在堆区申请10个整形空间
  7.     int* p=(int*)malloc(10*sizeof(int));
  8.     if (p == NULL)
  9.     {
  10.         //开辟空间失败
  11.         perror("malloc");//打印错误信息
  12.         //printf("%s\n", strerror(errno));//也是打印错误信息
  13.         return 1;
  14.     }
  15.     //使用这块空间
  16.     int i = 0;
  17.     for (i = 0; i < 10; i++)
  18.     {
  19.         *(p + i) = i + 1;
  20.     }
  21.     //打印这块空间
  22.     for (i = 0; i < 10; i++)
  23.     {
  24.         printf("%d ", *(p + i));
  25.     }
  26.     //释放这块空间
  27.     free(p);//将这块空间还给了操作系统,我们已经没有权限再使用这块空间了
  28.             //但是p仍然指向那块空间
  29.     p = NULL;//若不将p置为NULL,那么p就是野指针
  30.     return 0;
  31. }
复制代码
总结:


  • 动态内存开发函数头文件都是 stdlib.h
  • 假如不释放的话,程序竣事的时间也会被操作系统自动释放。
  • 但是为了防止内存走漏,将其置为NULL。这是一个好风俗。


二. calloc


   void* calloc(size_t num, size_t size);
  解释:在堆区中开发一块大小为 num * size 个字节的空间,返回指向这块空间的起始地址,此中 num 为数据的个数size 为单个数据的字节数,同时把申请的空间的每个字节初始化为全为0。
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. int main()
  4. {
  5.     //在堆区申请10个整形空间
  6.     int* p = (int*)calloc(10, sizeof(int));
  7.     if (p == NULL)
  8.     {
  9.         perror("calloc");
  10.         return 1;
  11.     }
  12.     //使用空间
  13.     int i = 0;
  14.     for (i = 0; i < 10; i++)
  15.     {
  16.         printf("%d ", *(p + i));
  17.     }
  18.     //释放
  19.     free(p);
  20.     p = NULL;
  21.     return 0;
  22. }
复制代码

 
三. realloc

   void* realloc (void* ptr, size_t size);
  解释:调解动态内存开发的空间,ptr 是那块空间的起始地址,size 是调解后的那块空间的字节的个数,返回指向这块空间的起始地址
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. int main()
  4. {
  5.     //在堆区申请10个整形空间
  6.     int* p = (int*)malloc(10 * sizeof(int));
  7.     if (p == NULL)
  8.     {
  9.         perror("malloc");
  10.         return 1;
  11.     }
  12.     //调整空间——变成20个整形空间
  13.     int* ptr = (int*)realloc(p, 20 * sizeof(int));//注意:要用新的指针来接收
  14.     if (ptr != NULL)
  15.     {
  16.         p = ptr;
  17.     }
  18.     else
  19.     {
  20.         //开辟失败
  21.         return 1;
  22.     }
  23.     int i = 0;
  24.     for (i = 0; i < 20; i++)
  25.     {
  26.         *(p + i) = i + 1;
  27.     }
  28.     for (i = 0; i < 20; i++)
  29.     {
  30.         printf("%d ", *(p + i));
  31.     }
  32.     //释放
  33.     free(p);
  34.     p = NULL;
  35.     return 0;
  36. }
复制代码
注意:大概有些人有疑问为什么要用新的指针接收返回的地址,直接用原来的指针接收不行吗?答案是不行的,在realloc调解动态内存开发的空间有3中情况,代码如下:
  1. int main()
  2. {
  3.     int* p = (int*)malloc(10);
  4.     //...
  5.     if (p != NULL)
  6.     {
  7.         int* ptr = (int*)realloc(p, 20);
  8.         //...
  9.     }
  10.     return 0;
  11. }
复制代码



情况1:


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

情况2:

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

情况3:


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

realloc函数也能开发空间,代码如下:
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. int main()
  4. {
  5.     int* p = (int*)realloc(NULL, 10 * sizeof(int));//等价于malloc(40)
  6.     if (p == NULL)
  7.     {
  8.         //...
  9.     }
  10.     return 0;
  11. }
复制代码

 
4.布局体

假如我写的不明确,检察对标链接,感谢各位支持!!!!!!
​​​​​​自定义类型:布局体+布局体内存对齐+布局体实现位段_自定义布局体位对齐怎样查询-CSDN博客自定义类型:布局体+布局体内存对齐+布局体实现位段_自定义布局体位对齐怎样查询-CSDN博客​​​​​​自定义类型:布局体+布局体内存对齐+布局体实现位段_自定义布局体位对齐怎样查询-CSDN博客
   前言:
学习了数组后发现数组中的元素只能是雷同类型的变量,那么有没有可以存放不同类型的变量呢?
布局体:一些值的集合,这些值称为成员变量,布局体的每个成员可以是不同类型的变量
  

一.布局体



1.布局体的声明


  1. struct tag
  2. {
  3.     member-list;//结构体成员列表
  4. }variable-list;//结构体变量列表
复制代码

比方:描述一个人
  1. struct Person {
  2.     int age;//年龄
  3.     char name[50];//姓名
  4.     float height;//身高
  5. };//封号不能丢
复制代码


2.布局体变量成员访问操作符


布局体变量.布局体成员名。
布局体指针变量->布局体成员名。
  1. #include <stdio.h>
  2. struct Person
  3. {
  4.     int age;
  5.     char name[50];
  6.     float height;
  7. }p1 = { 20,"zhangsan",185.5 }, * ps;//全局变量(*ps:结构体指针ps)
  8. int main()
  9. {
  10.     struct Person p2 = { 18,"lisi",173.2 };//局部变量
  11.     struct Person p3 = { 19,"wangwu",180.8 };//局部变量
  12.     ps = &p3;
  13.     printf("%d %s %.1f\n", p1.age, p1.name, p1.height);//结构体成员访问操作符:.
  14.     printf("%d %s %.1f\n", p2.age, p2.name, p2.height);
  15.     printf("%d %s %.1f\n", (*ps).age, (*ps).name, (*ps).height);
  16.     printf("%d %s %.1f\n", ps->age, ps->name, ps->height);//结构体成员访问操作符:->等价于先*再.
  17.     return 0;
  18. }
复制代码



3.布局体传参



  • 传布局体。
  • 传布局体的地址。
  1. #include <stdio.h>
  2. struct Person
  3. {
  4.     int age;
  5.     char name[50];
  6.     float height;
  7. };
  8. void test1(struct Person p)//用结构体接收
  9. {
  10.     printf("%d %s %.1f\n", p.age, p.name, p.height);
  11. }
  12. void test2(struct Person* p)//用结构体指针接收
  13. {
  14.     printf("%d %s %.1f\n", p->age, p->name, p->height);
  15. }
  16. int main()
  17. {
  18.     struct Person p1 = { 20,"zhangsan",185.5 };
  19.     test1(p1);//传结构体
  20.     test2(&p1);//传结构体的地址
  21.     return 0;
  22. }
复制代码
思考:我们发现二者都可以乐成访问布局体成员,那二者有什么区别呢?

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


4.匿名布局体


//匿名布局体类型 
  1. struct//不完全声明,由于没有名字,无法在其之后创建变量
  2. {
  3.     int age;
  4.     char name[50];
  5.     float height;
  6. }s1, s2;//在结构体声明的时候直接创建变量,不能在其之后创建变量了,只能使用一次
  7. int main()
  8. {
  9.     struct s3;//error
  10. }
复制代码

当只需使用一次可以使用(在声明布局体时,直接创建变量,不能在其之后创建变量了)。
思考:以下代码行不行
  1. struct
  2. {
  3.     int age;
  4.     char name[50];
  5.     float height;
  6. }s1;
  7. struct
  8. {
  9.     int age;
  10.     char name[50];
  11.     float height;
  12. }*ps;
  13. int main()
  14. {    
  15.     ps = &s1;//?
  16.     return 0;
  17. }
复制代码

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

5.布局的自引用

好比:定义一个链表的节点
  1. struct Node
  2. {
  3.      int data;//存放数据
  4.      struct Node* next;//存放指针
  5. };
复制代码

 
结尾祝福语

以上就是数据布局可能有用到的知识点,假如有人能看到这,我真感动呀!!!!!!,累死我了!!!!我知道你对我的文章是非常认可的,当然假如有错误,欢迎随时来指出,我们一起探讨,假如有疑问,直接私信我即可,看到的第一时间我会给你解答!!!!!!!也请各位给我个三连(点赞收藏评论!!!)
怎么说哪?竣事了,第一次写万字文章,肯定有写的不好的地方,多多见谅吧!!!!末了祝你们四级全过,天天开心!!!!!!

 



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

莱莱

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表