虽然大胆尝试是件好事,但是我们还是得先来补充一下我们在传参部分遗留的问题
我们已经了解到的传参方式有

值传递
实参将自身存储的数据拷贝给形参,同类型形参仅仅操作与外部相同数据
地址传递
实参将自身地址传递给形参,同类型形参通过该地低访问并修改外部数据
数组传递

  • 整型数组传递数组名与数组长度,形参接收首地址,通过该地址访问与操作数组内部相关元素
  • 字符型数组传递数组名(字符型数组读取时遇到’\0’时会停止读取,不需传递数组长度),形参接收首地址,通过该地址问与操作字符型数组内部相关元素

已经都这么多的方式了难道说还有其他的????
答案是肯定的,技多不压身,我们来看看这其他的

主函数传参

通过指令将终端上的字符串数据直接传递给main函数
主要形式:

  • int main(int argc,const char *argv[i])
  • int main(int argc,const char **argv)
  • argc表示传入数据的个数
  • argv表示传入的所有参数

操作系统会记录并存储我们输入的每个字符串的首地址,我们可以通过指针数组的形式记录并存储这些字符串的首地址,再通过指针数组内部的指针变量首地址访问并操作对应的字符串

好了补充完这些知识,来看看我们今天的重头戏吧
就这几种普通数据类型哪哪够用啊??我也要发明个属于我的独一无二的数据类型!!!
其实Dannis Ritchie 早就预料到了后来的我们会这样想,所以他早已经为我们设计好了一个struct关键字,来帮助我们构造数据类型

构造数据类型:

用struct关键字构造的数据类型我们把它称为结构体
先来了解一下结构体的定义吧
结构体类型定义:

  •  struct 结构体名 
    
  •  {
    
  •  	数据类型1 成员变量1;
    
  •  	数据类型2 成员变量2;
    
  •  	数据类型3 成员变量3;
    
  •  	···
    
  •  	};
    

结构体类型的定义也是可以嵌套的,我们可以在定义结构体类型时,引入我们构造过的结构体类型

在这里插入图片描述

构造体其实就是由不同的基本数据类型组成的,不同的数据类型有不同数据类型的特点,所以相对应的我们的构造体也会有内部成员变量对应数据类型的特点

再来看看我们结构体变量的定义:
与我们基本数据类型相同的:

  •  数据类型 变量名;
    

变量需要初始化,那我们的构造体在使用前也需要初始化
1.全部初始化:

  • struct student a = {“zhangsan”,‘f’,17,80};
    • 赋值不同的成员变量用,间隔开

当结构体类型是嵌套定义的时

  • struct datetime dt = {{2026,1,22},[20,50,21}}
    • 用{}中的{}分别给其中的不同结构体赋值

我们需要根据成员变量类型决定初始化时{}内赋给他们的值

那我猜有全部初始化的话就一定有局部初始化吧
猜对了这个我们还真有,C语言之父还真是有说法的

2.局部初始化

  •   struct student a = {
    
  •   .name = {"zhangsan"}
    
  •   .sore = 100,
    
  •   };
    

嵌套结构初始化:

  •  struct datetime dt = {
    
  •  .d = {
    
  •  	.year = 2026,
    
  •  	},
    
  •  .t = {
    
  •  	.hour = 14,
    
  •  	},
    
  •  };
    

给其中的成员变量赋值时需要在对应成员变量前加.
不同的成员变量应该用,隔开

这这么多大括号简直要看花眼了
可千万不看看错,不同的结构体是在不同的{}中初始化的

结构体和我们的变量一样在定义时也是会开辟存储空间的去存储数据的,接下来就来看看我们是怎么样访问结构体的成员变量的吧

结构体成员变量的访问:
我们是需要根据结构体内不同的成员变量的类型去访问他们的,访问结构体变量其实就是访问其中的成员变量
我们的访问方式主要就有以下两种类型

1.变量名访问

  • struct stduent
  • {
  • char name[32];
  • char sex;
  • int age;
  • };
  • struct stdent a = {“zhangsan”,‘f’,12}
  • printf(“%s\n”,a.name)
    • a.name 就相当于我们直接通过struct student 类型变量 a的变量名 找到它的存储空间,然后找到了中的成员变量name,它的类型就是name 对应的数据类型

2.指针访问

  • struct stdent *p = NULL;
  • p = &a;
  • printf (“%s\n”,p->name)
    • p->name 就是我们通过struct student * 类型的指针指向了struct student类型变量a的首地址,找到了a 中对应的成员变量name,它的类型也还是name对应的数据类型

接下来就来看看结构体数据是怎么存储的吧

因为结构体是由很多不同或相同类型的基本数据类型的变量组成的,所以结构体的存储空间就是从其首地址开始对应的 成员变量的存储空间组成的‘

但是我们应该注意!!!

32位的操作系统一次只能读取四个字节空间的变量,为了提高读取效率,我们的成员变量需要存在可以被一次读取的位置

这就是我们的内存对齐:

  1. 结构体成员变量只能存放在内存地址为自身基本类型长度的整数倍的内存单元中
  2. 结构体存储空间的大小必须是其中成员变量中存储空间最大的基本类型长度的整数倍

我们的结构体变量也可以在不同的函数之间以参数的形式传递
结构体变量在传参时也分为值传递与值传递

1.值传递
我们的struct student 类型的实参直接将它存储空间中的数据拷贝给形参

2.址传递
我们直接对struct student类型的变量a取址,将他在内存中存储的首地址传递给被调函数中的struct student*类型的指针p,然后我们通过这个指针变量p去访问a 中的不同成员变量

那这俩不都是传递吗,到底有啥区别呢???

  1. 与我们普通变量相同的,结构体变量传参时,值传递还是无法改变函数外部的数据,仅仅是存储空间拷贝后拿到了相同的数据进行操作,最后需要返回值不然存储的数据是局部变量会被回收空间

  2. 而且如果结构体存储空间很大时,拷贝空间速度相对较慢,我们程序的运行效率会降低

  3. 地址传递时,因为我们传递的是struct student类型变量a 存储的首地址,我们的被调函数形参指针变量接收到了这个地址,就可以通过这个地址直接访问结构体的存储空间,对存储空间中的数据直接进行操作

  4. 并且地址在传递时,只需要拷贝八个字节的空间,这样可以提高我们程序运行的效率

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐