尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
第9-10:C语言指针字符串结构体
在指针中我学习了利用指针操作函数查询、存储数据返回多个值将函数结果与计算分开和指针高级指针高级其中有指针运算运算有无意义野指针和悬空指针void类型指针数组指针利用索引遍历数组得二种方法利用指针遍历数组二种方法函数指针。在字符串中我学习了字符串定义格式输入字符串遍历字符串数组字符串常见函数、练习用户登陆和统计次数。在结构体中我学习了结构体定义格式结构体数组起别名结构体作为函数参数传递结构体嵌套指针是什么内存地址指针本质是地址它的核心作用是定位内存作用1. 唯一标识一块内存——通过地址能精确找到某个变量/数据2. 作为参数传递——把地址传给函数让函数能操作原数据3. 作为返回值返回多个值——函数返回地址让调用者访问某块内存4. 构成数据结构——链表节点靠下一个节点的地址串联利用指针同时返回多个值//定义一个函数求数组中的最大值和最小值并进行返回 //1.定义数组 { int arr[] { 12,17,23,549,7,62,4 }; int len sizeof(arr) / sizeof(int); //2.定义变量最大值和最小值 int max arr[0]; int min arr[0]; //3.调用函数 getmaxandmin(arr, len, max, min); printf(数组最大值为%d\n, max); printf(数组最小值为%d\n, min); } void getmaxandmin(int arr[], int len,int* max,int* min) { //求数组的最大值 * max arr[0]; for (int i 1;i len;i) { if (arr[i] * max) { *max arr[i]; } } //求数组的最小值 * min arr[0]; for (int i 1;i len;i) { if (arr[i] *min) { *min arr[i]; } } } */指针变量是什么专门用来保存地址的变量作用1.间接访问-解引用2.函数传地址调用3.动态内存管理4.高效管理数组/字符串5.构建复杂数据结构6.实现多态/回调函数指针利用指针存储数据修改数据//1.定义一个变量 int a 10; //2.定义一个指针去指向变量a int* p a; //3.利用指针去获取变量a中的值 printf(%d\n, *p); //获取a的数据 printf(%d\n, p); //获取a的内存地址 //4.利用指针去存储变量/修改变量 *p 200; //5.输出打印 printf(%d\n, *p); //获取修改后a的数据 printf(%d\n, a); //修改后a的值指针高级指针运算步长指针移动一次的字节个数步长的大小与数据类型有关int -4 char-1 short-2 long-4 longlong-8int a 10; int* p a; //作用;用指针p记录变量a的地址 printf(%p\n, p); //输出是内存地址 printf(%p\n, p1); printf(%p\n, p-1);运算的有意义/无意义操作有意义操作1.指针跟整数进行加、减操作每次移动n个步长2.指针跟指针减操作可知二指针间隔步长int arr[] { 1,2,3,4,5,6,7,8,9,10 }; //获取0索引内存地址 int* p1 arr[0]; //通过指针p获取数据 printf(%d\n, *p1); printf(%d\n, *(p11)); //获取5索引内存地址 int* p2 arr[5]; //p2-p1间隔多少步长 printf(%d\n, p2 - p1); // 五个步长20个字节无意义操作1.指针跟整数进行乘、除操作这是错误的因为此时指针指向不明2.指针跟指针进行加、减、乘、除操作指向不明的指针野指针指针指当空间未分配悬空指针指针指向空间已经分配但是被释放了void类型指针格式void*p这个指针不表示任何类型无步长概念它只可以记录地址值可接受任何地址。不可以获得变量中的数据也不可以进行计算不同类型之间的指尖不可以进行互相赋值但void类型可以void类型可接受任意类型指针记录的内存地址注意事项在 C 语言中void*无类型指针是可以自动转换成任何其他类型的指针的隐式转换 而C 是一种非常严格的强类型语言。为了防止犯错它彻底取消了 void* 到其他具体类型指针的隐式转换 编译器行为一旦 VS 看到 .cpp它就会调用 C 编译器来编译这段代码它也会拿 C 的严格标准来审查你 所以.cpp无法自动转换任何其他类型的指针。如果改成.c再次编译VS 就会调用 C 编译器就可实现自动转换void类型互换数据//1.定义二个变量 int a 10; short b 20; //2.定义二个指针 int* p1 a; short* p2 b; //利用void类型直接互换 void* p3 p1; void* p4 p2;二级指针格式数据类型**指针名其中指针类型跟指向空间数据类型保持一致作用可以操作一级指针记录的地址(可以获取一级指针的地址也可以获取一级指针的数据也可以修改一级指针的内存地址//定义变量 int a 10; int b 20; //定义一级指针 int* p a; //定义二级指针指向一级指针p int** q p; //作用1.利用二级指针修改改一级指针里记录的内存地址 *q b; //作用2.利用二级指针获取变量中记录的值 printf(%p\n, a); printf(%p\n, b); printf(%p\n, p); printf(%p\n, *q); //解引1获取一级指针记录的内存地址 printf(%d\n, **q); //解引2获取到变量中的数据 打印结果数组指针是什么指向数组的指针作用方便操作数组中的各种数据细节arr直接参与计算的时候会自动退化为第一个元素的指针特殊情况1.sizeof参与计算时不会退化还是整体eg算数组长度2.arr获取地址时不会退化int arr[] { 1,2,3,4,5,6,7,8,9, }; int len sizeof(arr) / sizeof(int); //实际上获取的是数组的首地址然后再把首地址赋值给*p int *parr int* p arr[0];细节//sizeof 运算的时候不会退化arr还是整体 printf(%zu\n, sizeof(arr)); //36个字节 //1.首地址对比 printf(%p\n, arr); //一般用该种更多 printf(%p\n, arr); //2.步长对比 printf(%p\n, arr1); //4 printf(%p\n, arr1); //36二维数组是什么把多个小地址放到一个大数组中定义格式int arr[m][n] m是二维数组的长度n是一维数组的长度{成员1 成员2 ...... };利用索引遍历二维数组第一种方法局限于二维数组中每一个一维数组长度相同//1.定义二维数组 int arr[3][5] { { 1,2,3,4,5 }, { 2,3,4,5,6 }, { 3,4,5,6,7 } }; for (int i 0;i 3;i) //i依次表示二维数组中的索引 { //内循环遍历每一个一维数组 for (int j 0;j 5;j) //j依次表示一维数组中的索引 { printf(%d, arr[i][j]); } printf(\n); //当内循环结束表示一维数组遍历完毕 }第二种方法允许每一个一维数组长度不一样核心思路事先先把所有一维数组定义完毕再放入二维数组当中 //1.定义三个一维数组 int arr1[3] { 1,2,3 }; int arr2[4] { 1,2,3,4 }; int arr3[5] { 1,2,3,4,5 }; //预先计算每一个一维数组的长度 int len1 sizeof(arr1) / sizeof(int); int len2 sizeof(arr2) / sizeof(int); int len3 sizeof(arr3) / sizeof(int); //再定义一个数组表示每一个一维数组的长度 int len[3] { len1,len2,len3 }; //2.把三个一维数组放到二维数组中 //数组的数据类型与内部存储的元素类型保持一致 int* arr[3] { arr1,arr2,arr3 }; 指针数组 //此时二维数组存储的是一维数组的int类型的内存地址, arr数组元素类型是指向整数的指针也就是int* for (int i 0;i 3;i) { for (int j 0;j len[i];j) //len[i]依次表示三个一维数组的长度该处可体现长度不一样该如何表示 { printf(%d, arr[i][j]); } printf(\n); }利用指针遍历二维数组第一种方法利用二维数组指针遍历int arr[3][5] { { 1,2,3,4,5 }, { 2,3,4,5,6 }, { 3,4,5,6,7 } };//获取二维数组的指针 int(* p)[5] arr; 最关键的一步数组----指针//数组指针类型是与数组内部存储元素的数据类型保持一致 //二维数组里存的是一维数组且一维数组的数据类型是int又一维数组有5个int元素 for (int i 0;i 3;i) { //内循环遍历一维数组 for (int j 0;j 5;j) { printf(%d, *(*p j)); } printf(\n); //移动二维数组的指针继续遍历下一个一维数组 //(如果不移动指针遍历的一直都是第一个一维数组 p; }第二种方法指针数据指针遍历//1.定义三个一维数组 int arr1[3] { 1,2,3 }; int arr2[3] { 2,3,4 }; int arr3[3] { 3,4,5 }; //2,把这三个一维数组的内存地址放到二维数组当中 int* arr[3] { arr1,arr2,arr3 }; //此时这个二维数组是一个指针数组三个元素都是指针int*//3.获取指针 int* * p arr; 此时arr已经是指针了所以这里是二级指针//与方法一唯一不同就是获取指针----int(*p)[5] arr; //4.循环遍历数组 for (int i 0;i 3;i) { for (int j 0;j 3;j) { printf(%d, *(*p j)); } //换行 printf(\n); //移动指针 p; }函数指针定义格式返回值类型*指针名形参函数名 调用函数格式:1.调用无参函数指针名 2.调用有参函数数据类型 变量名指针名实参 这里定义变量是用来接受返回值作用利用函数指针动态调用函数//1.定义二个指针指向二个函数 void (*p1) () method1; int (*p2) (int, int) method2; //2.利用函数指针调用函数 p1(); int nump2(10,20); //定义一个临时变量接住它 printf(%d\n, num); } void method1() //无返回值 { printf(method1\n); } int method2(int num1, int num2) //有返回值 { printf(method2\n); return num1 num2;字符串获取字符串二种格式1.字符数组双引号char str1[4] abc;利用该方法可以修改字符串的内容2.指针双引号char* str2 abcd;利用该方法会把底层的字符放在只读常量区只读常量区特点1.内容不可以修改2.里面定义的字符串是可以复用的eg创建abc时会检查只读常量区有无abc如有不会创建新的而是进行复用 日无则会创建新的字符串数组在这里我们需要注意当我们遍历的是字符串数组时利用%s%s是专门用来接收地址的当在打印printf时printf拿到地址会自己去找需要的字符串根本不需要解引用*如加了*是会报错的 普通整数 (int) %d 具体的数字值 需要 (*p) 单个字符 (char) %c 具体的字符值 可加可不加 (c 或 *p 都行) 整个字符串 (char*) %s 字符串的首地址指针 绝对不能加 (str)//定义一个二维数组用来存储多个学生的名字,并遍历// 第一种方式二维数组char strarr[5][100] { xuanqi, xuanqii, xuuanqi, xuannqi, xuanqiqi }; //2.遍历二维数组 for (int i 0;i 5;i) //i表示二维数组中每一个索引 {char* str strarr[i];//strarr[i]表示二维数组中每i行一维数组的首地址利用char*str指针来接住这个地址printf(%s\n,str); //数组strarr是一块连续的500字节的内存每次i在内存中的跨度是100个字节 }//第二种方式//把五个字符串的指针放到一个数组中----(指针数组)char* strarr2[] { xuanqi, xuanqii, xuuanqi, xuannqi, xuanqiqi }; for (int i 0;i 5;i) //i表示二维数组中每一个索引 {char* str strarr2[i];//strarr2[i]表示指针数组中第i个元素这个元素本身就是一个char*类型的指针printf(%s\n,str); //数组strarr2是一块连续的40字节的内存存储5个指针每次i在内存中的跨度是8个字节 }字符串常见函数(在使用以下函数时应添加头文件string.hstrlen-获取字符串长度strcpy-复制字符串strcat-拼接二个字符串strcmp-比较二个字符串strlwr-字符串小写str字符串大写char* str1 abc; //底层会把字符数组放在只读常量区1.只可读不可修改2.复用 char str2[100] abcd; //可修改字符串内容 char str3[5] { x , a ,q,i,\0 };需要注意每个函数的使用格式和细节结构体结构体是什么可理解为自定义的数据类型由一批数据组合而成的结构性数据里面的每一个数据都是结构体的成员。书写位置函数里面--局部只可在本函数中使用---函数外面--全局在所有函数中均可使用格式struct 结构体名字 { 成员1 成员2 ..... }struct friend{ char name[100]; int age; char gender; double height; }; int main() { //使用结构体 //1.定义一个朋友类型的变量struct friendf1; //2.给朋友赋值strcpy(f1.name, 徐安琪1);//name是一个数组数组不可以整体进行赋值只能挨个元素赋值或者进行字符串函数复制 f1.age 18; f1.gender F; //在这里定义gender没有方括号只是一个普通的字符变量可以直接进行赋值 f1.height 1.68; //3.输出打印 printf(朋友的名字叫做%s\n,f1.name); printf(朋友的年龄为%d\n,f1.age); printf(朋友的性别为%c\n,f1.gender); printf(朋友的身高为%.2f\n,f1.height);结构体数组struct student { char name[100]; int age; }; int main() { //1.定义三个学生并直接进行赋值struct student stu1 { xuanqi,18 }; struct student stu2 { xuanqii,19 }; struct student stu3 { xuaanqi,20 }; //2.把三个学生放入到数组中struct student arr[3] { stu1,stu2,stu3 }; //3.遍历数组 for (int i 0;i 3;i) { struct student temp arr[i]; printf(学生信息为姓名为%s 年龄为%d\n,temp.name, temp.age); } }在上述二个代码中体现了二种结构体给变量赋值1.一个一个赋值struct friend stu1; strcpy(f1.name, 徐安琪1); stu1.age 18; stu1.gender F;2.批量赋值struct student stu1 { xuanqi,18 };起别名格式 typedef struct 结构体名字 { 成员1 成员2 ... }别名当起了别名在主函数定义的时候可以省略 struct 结构体名字直接使用别名即可结构体最为函数参数传递二种情况1.传递结构体中的数据值2.传递结构体的地址值传递推荐用指针传递传地址---因为利用指针传递结构体地址可以直接在函数内修改原结构体且避免拷贝typedef struct student { char name[100]; int age; }S; void method(S* p); //使用函数且函数里面使用到结构体所以申明必须写在结构体下面 int main() { //1.定义一个学生 S stu1; //2.给学生赋初始值 strcpy(stu1.name, aaa); stu1.age 0; //3.输出打印 printf(学生的初始数据姓名为%s 年龄为%d\n, stu1.name, stu1.age); //4.调用函数 method(stu1); //5.输出打印 printf(学生的修改后姓名为:%s 年龄为:%d\n, stu1.name, stu1.age); } void method(S*p)//S*pS*stu1,二者都是指针变量//无论是p还是stu1都是用来存内存地址的所以哪种表达都可以看自己那种可以理解 { printf(接收到main函数中学生的初始数据为:%s,%d\n,(*p).name,(*p).age); //为什么这里*p要小括号因为.成员访问运算符优先级高于*解引用运算符 //我们应先执行*p先把指针p指向的结构体变量stu1取出来 //再对这个取出来的结构体变量stu1中成员name进行访问 //修改 printf(请输入要修改学生的名字\n); scanf(%s, (*p).name);//(*p).name:表示先通过指针找到原结构体然后提取出这个结构体中name的成员printf(请输入要修改学生的年龄\n); scanf(%d, ((*p).age)); printf(在method函数中修改完的学生名字%s 年龄为%d\n, (*p).name, (*p).age); }结构体嵌套什么意思如果结构体成员的类型是其他结构体那么就用到了结构体嵌套struct message { char phone[12]; char mail[100]; }; struct student { char name[20]; int age; char gender; double height;struct message msg;//此时该成员ms的类型是其他结构体的}; //定义变量 struct student stu; //给成员赋值赋值时也有一点小区别 1.一个一个赋值strcpy(stu.msg.phone, 12345678910); strcpy(stu.msg.mail, 123456789qq.com);2.批量赋值(结构体嵌套在赋值时也要用大括号嵌套 struct student stu2 { 徐安琪,18,F,1.68,{ 12345678910,123456789qq.com }};
RELATED

相关推荐

Spring系列02:BeanFactory 与 ApplicationContext

Spring系列02:BeanFactory 与 ApplicationContext

这题你大概率只答了一半 面试八成会被问:「BeanFactory 和 ApplicationContext 有什么区别?」 多数人的回答停在「ApplicationContext 更高级、功能更多」,再往下问「多在哪」「为什么 Boot 默认用它」「两者启动行为有啥不同」,就…

📅 2026/11/5 7:56:55
金价跌破4000美元,年初高位买的金条和金饰,现在该怎么办?

金价跌破4000美元,年初高位买的金条和金饰,现在该怎么办?

这一轮金价下跌,最让人想不通的地方在于:中东在打仗,全球股市在跌,按理说最该涨的“避险资产”,却跌得最狠。 10月23日,国际金价盘中跌破每盎司4000美元,最低3986美元,是7月以来第一次。国内上海金交所的黄金9999,盘中跌破每克860元。按媒体的统计,从今年年初的高点…

📅 2026/11/5 7:56:55
HashMap、Hashtable、ConcurrentHashMap 区别详解

HashMap、Hashtable、ConcurrentHashMap 区别详解

在 Java 集合框架中,HashMap、Hashtable、ConcurrentHashMap 都是基于哈希表实现的键值对容器,三者最核心的差异集中在线程安全机制、锁粒度设计、并发性能三个维度,也是 Java 并发面试的高频基础考点。本文从底层实现到特性差异逐一拆解&…

📅 2026/11/5 7:56:55
MORE NEWS

更多资讯

📰

如何用AI新闻雷达Horizon打造你的专属信息过滤系统

如何用AI新闻雷达Horizon打造你的专属信息过滤系统 【免费下载链接】Horizon 📡 Your own AI-powered news radar. Generates daily briefings in English & Chinese. | 用 AI 构建你专属的新闻雷达 项目地址: https://gitcode.com/GitHub_Trending/horizon39…

📰

SuperSplat 新手排错指南:3D Gaussian Splat 编辑器常见 5 类坑一次讲清

SuperSplat 新手排错指南:3D Gaussian Splat 编辑器常见 5 类坑一次讲清 【免费下载链接】super-splat 3D Gaussian Splat Editor 项目地址: https://gitcode.com/gh_mirrors/su/super-splat 第一次把 SuperSplat 跑起来就卡住了?先别急着怀疑人生…

📰

Windows 多出一块虚拟屏:Virtual-Display-Driver 安装与配置实用清单

Windows 多出一块虚拟屏:Virtual-Display-Driver 安装与配置实用清单 【免费下载链接】Virtual-Display-Driver Add virtual monitors to your windows 10/11 device! Works with VR, OBS, Sunshine, and/or any desktop sharing software. 项目地址: https://git…

📰

【亲测免费】 探索PySceneDetect:高效视频剪辑与分割工具

探索PySceneDetect:高效视频剪辑与分割工具 【免费下载链接】PySceneDetect :movie_camera: Python and OpenCV-based scene cut/transition detection program & library. 项目地址: https://gitcode.com/GitHub_Trending/py/PySceneDetect 是一个开源的…

📰

3分钟给Windows加块虚拟显示器:Virtual-Display-Driver完整上手指南

3分钟给Windows加块虚拟显示器:Virtual-Display-Driver完整上手指南 【免费下载链接】Virtual-Display-Driver Add virtual monitors to your windows 10/11 device! Works with VR, OBS, Sunshine, and/or any desktop sharing software. 项目地址: https://gitc…

📰

中望3D草图歪斜全解析:视图与几何偏移的区分与修复

1. 草图视图歪了到底歪的是什么很多人第一次遇到这个问题时的反应是“视图转一下不就行了”,结果鼠标中键按住转了三百六十度,草图还是斜的,标注尺寸也跟着歪,最后画出来的线全是斜的。这里得先把一个概念掰清楚:中望3…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬