C语言 | 第十五章 | 指针函数 函数指针 内存分配 结构体

P 141 返回指针的函数 2023/2/16

一、基本介绍

C语言 允许函数的返回值是一个指针(地址),这样的函数称为指针函数

二、入门案例

案例:请编写一个函数 strlong(),返回两个字符串中较长的一个。

#include<stdio.h>
#include<string.h>char *strlong(char *str1, char *str2){ //函数返回的char * (指针)printf("\nstr1的长度%d str2的长度%d", strlen(str1), strlen(str2));if(strlen(str1) >= strlen(str2)){return str1;}else{return str2;}
}int main(){char str1[30], str2[30], *str;   // str是一个指针类型,指向一个字符串 printf("\n请输入第1个字符串");gets(str1);  // 输入字符或者字符串printf("\n请输入第2个字符串");gets(str2);str = strlong(str1, str2);printf("\nLonger string: %s \n", str);getchar();return 0;
}

三、指针函数注意事项和细节

  1. 用指针作为函数返回值时需要注意,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针不能指向这些数据【案例演示】。

  2. 函数运行结束后会销毁该函数所有的局部数据,这里所谓的销毁并不是将局部数据所占用的内存全部清零,而是程序放弃对它的使用权限,后面的代码可以使用这块内存

#include<stdio.h>int *func(){int n = 100;  //  局部变量;在func返回时就会销毁return &n;
}int main(){int *p = func();  // func返回指针int n;// printf("okokok~");  //但是这里如果在赋值之前有其他的语句执行也会占用空间,这里面原先int n =100可能就会发生改变n = *p;//  思考:是否能够输出100?不一定// 依然输出了100,因为将100赋值给n了,直接就输出了,前面没有占用之前int n = 100的空间printf("\nvalue = %d\n", n);  getchar();return 0;
}
  1. C 语言不支持在调用函数时返回局部变量的地址,如果确实有这样的需求,需要定义局部变量为 static 变量 【案例演示】
#include<stdio.h>int *func(){//int n = 100;  //  局部变量;在func返回时就会销毁static int n = 100;// 如果这个局部变量是static性质,那么n存放数据的空间在 静态数据区(前面C程序布局图)// 静态数据区是不会被其他的变量来占用了return &n;
}// 下面代码同上!

四、案例练习

案例一:编写一个函数,它会生成 10 个随机数,并使用表示指针的数组名(即第一个数组元素的地址)来返回它们。

#include<stdio.h>
#include<stdlib.h>
// 编写一个函数,返回一个一维数组
int * f1(){static int arr[10]; //  必须加上static,让arr的空间在静态数据区分配int i = 0;for(i = 0; i < 10; i++){  //  数组初始化赋值arr[i] = rand();}return arr;
}void main(){int *p;int i;p = f1();  // p指向f1中生成的数组首地址(即第一个元素的地址) for(i = 0; i < 10; i++){printf("\n%d",*(p+i));}getchar();
}

P 142 函数指针和内存布局 2023/2/26

一、基本介绍

1、一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。

可以比作将函数名看成一个首地址,将地址付给函数指针)。

2、把函数的这个首地址(或称入口地址)赋予一个指针变量使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针

二、函数指针定义

returnType (*pointerName)(param list);

  1. returnType 为函数返回值类型

  2. pointerName 为指针名称

  3. param list 为函数参数列表

  4. 参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称

  5. 注意( )的优先级高于*,第一个括号不能省略,如果写作returnType *pointerName(param list);就成了函数原型,它表明函数的返回值类型为returnType *

三、案例演示

#include<stdio.h>// 说明:
// 1.max函数
// 2.接收两个int,返回较大数
int max(int a, int b){return a>b ? a : b;
}
//int max(int a, int b);
int main(){int x, y, maxVal;//说明:  函数指针// 1. 函数指针名字pmax// 2. int表明该函数指针指向的函数是返回int类型// 3. (int,int)表示该函数指针指向的函数形参是接收两个int// 4. 在定义函数指针时,也可以写上形参名int (*pmax)(int a, int b) = max; int (*pmax)(int, int) = max; //   *pmax就相当于找到了函数,然后传入实参printf("Input two numbers:");scanf("%d %d", &x, &y);// (*pmax)(x, y) 通过函数指针去调用函数maxVal = (*pmax)(x, y);// 当然指针有存放的地址和本身的地址:可输出查看,printf("Max value: %d\n pmax=%p pmac本身地址%p %p", maxVal,pmax,&pmax);getchar();getchar();return 0;
}

四、内存布局图

image-20230226154844947

P 143 回调函数 2023/2/26

一、基本介绍

  1. 函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。
  1. 简单的讲:回调函数是由别人的函数执行时调用你传入的函数(通过函数指针完成)。

二、应用实例

#include <stdlib.h> 
#include<stdio.h>
// 回调函数
// 1.  int (*f)(void)
// 2.f 就是函数指针,它可以接收函数是返回(int,没有形参的函数)
// 3.f 在这里被intArray调用,充当了回调函数的角色
void initArray(int *array, int arraySize, int (*f)(void)) { // 对地址进行操作就是修改了main里面的array
// 下面传了一个函数名给函数指针,然后通过函数指针调用getNextRandomValueint i ;for ( i=0; i<arraySize; i++){array[i] = f();   // 通过函数指针调用了 getNextRandomValue}	//  调用方式2:(*f)()
}// 获取随机值
int getNextRandomValue(void) {return rand();  // rand系统函数,会返回一个随机数
}int main(void) {int myarray[10],i;  // 定义了一个数组和int类型// 说明:// 1.调用intArray函数// 2.传入了一个函数名,getNextRandomValue(地址),需要使用函数指针接收initArray(myarray, 10, getNextRandomValue);for(i = 0; i < 10; i++) {printf("%d ", myarray[i]);}printf("\n");getchar();return 0;
}

P 144 空指针的使用 2023/3/11

一、指针注意的事项和细节

  1. 指针变量存放的是地址,从这个角度看指针的本质就是地址。(能够使用到地址的地方都可以使用指针)
  2. 变量声明的时候,如果没有确切的地址赋值,为指针变量赋一个 NULL 值是好的编程习惯。
  3. 赋为 NULL 值的指针被称为空指针,NULL 指针是一个定义在标准库 <stdio.h>中的值为零的常量 #define NULL 0 [案例]
#include<stdio.h>void main(){int *p = NULL; // 空指针;给空值是一个好习惯// 空指针就是一个为0的常量int num =34;   p = &num;   printf("*p=%d",*p);  // 34getchar();
}

P 145 动态内存分配机制和案例 2023/3/12

一、动态内存分配

  • C程序中,不同数据在内存中分配说明
    1. 全局变量——内存中的静态存储区。
    2. 非静态的局部变量——内存中的动态存储区——stack 栈。
    3. 临时使用的数据—建立动态内存分配区域,需要时随时开辟,不需要时及时释放——heap 堆。
    4. 根据需要向系统申请所需大小的空间,由于未在声明部分定义其为变量或者数组,不能通过变量名或者数组名来引用这些数据只能通过指针来引用)。

image-20230312151510973

二、内存动态分配的相关函数

  1. 头文件 #Include <stdlib.h> 声明了四个关于内存动态分配的函数。

  2. 函数原型 :void * malloc(usigned int size) //memory allocation(内存分配)

    • 作用——在内存的动态存储区(堆区)中分配一个长度为size的连续空间
    • 形参size的类型为无符号整型,函数返回值是所分配区域的第一个字节的地址,即此函数是一个指针型函数,返回的指针指向该分配域的开头位置。
    • malloc(100); 开辟100字节的临时空间,返回值为其第一个字节的地址。
  3. 函数原型: void *calloc(unsigned n,unsigned size)

    • 作用——在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组。
    • 用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。
    • 函数返回指向所分配域的起始位置的指针;分配不成功,返回NULL。
    • p = calloc(50, 4); //开辟 50*4 个字节临时空间,把起始地址分配给指针变量 p。
  4. 函数原型:void free(void *p)

    • 作用——释放变量p所指向的动态空间,使这部分空间能重新被其他变量使用。
    • p是最近一次调用calloc或malloc函数时的函数返回值。
    • free函数无返回值。
    • free§; // 释放p 所指向的已分配的动态空间。
  5. 函数原型:void *realloc(void *p,unsigned int size)

    • 作用——重新分配malloc或calloc函数获得的动态空间大小,将p指向的动态空间大小改变为size,p的值不变,分配失败返回NULL。
    • realloc(p, 50); // 将 p 所指向的已分配的动态空间 改为50字节。
  6. 返回类型说明:

image-20230312154544316

三、void指针类型

image-20230312155026917

image-20230312161435137

四、案例说明

#include<stdio.h>#include <stdlib.h>
int main() {void check(int *);   //函数声明int * p,i;p = (int *)malloc(5*sizeof(int));// 5*4 开辟20字节的临时空间,(void*)强制转成(int*),赋给p;5个int类型空间for( i = 0; i < 5; i++){scanf("%d", p + i);  // 数据就会填入到堆区里面去}check(p);  // 函数调用free(p);   // 销毁堆区p指向的空间;相当于被回收了,回收后是不能被使用的getchar();getchar();return 0;
}void check(int *p) {int i;printf("\n不及格的成绩 有: ");for(i =0; i < 5; i++){if(p[i] < 60) {
// *(p+1) 也是取值,p[i]则是取数组的元素,p[下标]取相应的值printf(" %d ", p[i]);}}
}

五、内存布局图

image-20230314162330868

P 146 动态内存分配注意事项 2023/3/14

一、动态分配内存的基本原则

  1. 避免分配大量的小内存块。分配堆上的内存有一些系统开销,所以分配许多小的内存块比分配几个大内存块的系统开销大。
  2. 仅在需要时分配内存。只要使用完堆上的内存块,就需要及时释放它(如果使用动态分配内存,需要遵守原则:谁分配,谁释放), 否则可能出现内存泄漏。
	free(p);   // 销毁堆区p指向的空间;相当于被回收了,回收后是不能被使用的// 如果我们使用完后不释放,这块内存会一直存放在我们的堆区,下面函数也不使用和释放,一直存在,这就相当于内存泄漏。
  1. 总是确保释放以分配的内存。在编写分配内存的代码时,就要确定在代码的什么地方释放内存。
  2. 在释放内存之前,确保不会无意中覆盖堆上已分配的内存地址,否则程序就会出现内存泄漏。在循环中分配内存时,要特别小心

二、指针使用一览总结

image-20230314161655712

P 147 为什么需要结构体 2023/3/14

一、结构体引入

引入:张老太养了两只猫猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年100岁,花色。请编写一个程序,当用户输入小猫
的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名错误,则显示 张老太没有这只猫猫。

  • 传统技术解决

    1. 单独定义变量解决(需要定义多个变量,猫的颜色,年龄,名字)
    2. 我们学习了数组,它是一组具有相同类型的数据的集合。在编程中,我们往往还需要一组类型不同的数据,例如猫的名字使用字符串、年龄是int,因为数据类型不同,不能用一个数组来存放。
  • 现有技术缺点分析:不利于数据的管理和维护,因为本身猫的三个属性,是一个整体,传统方法是将其分解。

// 定义变量
char cat1Name[10] = "小白";
int cat1Age = 3;
char cat1Color[10] = "白色";// 使用数组
char *catsName[2] = {
"小白",
"小花"
}
char *catAge[2] = {1,2};
// 我们还要考虑第一只猫通过下标为0来取,都是非常的麻烦

P 148 结构体快速入门 2023/3/14

一、结构体入门

  • 结构体与及结构体变量关系示意图

image-20230314170317017

二、猫猫问题结构体解决

#include<stdio.h>
void main(){
/*张老太养了两只猫猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年100岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名错误,则显示 张老太没有这只猫猫。分析:1. 猫猫有三个成员(变量 )组成2. 使用结构体解决
*/// 创建结构体Cat【是数据类型】struct Cat{  // 结构体名Cat,Cat就是我们自己构造的数据类型char * name;  // 名字,使用指针,指向一个字符串int age;char * color;};// 使用Cat结构创建对应的,变量struct Cat cat1;  // cat1就是struct Cat的一个变量 int numstruct Cat cat2;  // cat2就是struct Cat的一个变量 int num// 给 cat1 里面的各个成员赋值cat1.name = "小白";cat1.age = 3;cat1.color = "白色";// 给 cat2 里面的各个成员赋值cat2.name = "小花";cat2.age = 100;cat2.color = "花色";// 输出两只猫的信息;会发现这样更易于管理,将猫的属性管理在一起printf("\n 第一只猫 name=%s age=%d color=%s",cat1.name,cat1.age,cat1.color);printf("\n 第二只猫 name=%s age=%d color=%s",cat2.name,cat2.age,cat2.color);
}

P 149 结构体变量内存布局 2023/3/14

一、结构体和结构体变量的区别和联系

通过上面的案例和讲解我们可以看出:

  1. 结构体是自定义的数据类型,表示的是一种数据类型
  2. 结构体变量代表一个具体变量,好比:
int num1 ; // int 是数据类型, 而num1 是一个具体的int变量struct Cat cat1; // Cat 是结构体数据类型, 而cat1 是一个Cat变量

3.Cat 就像一个“模板”,定义出来的结构体变量都含有相同的成员。也可以将结构体比作“图纸”,将结构体变量比作“零件”,根据同一张图纸生产出来的零件的特性都是一样的。

二、结构体在内存的布局

image-20230314194230626

P 150 结构体成员 2023/3/14

一、如何声明结构体

语法:

struct 结构体名称 { // 结构体名首字母大写,比如Cat, Person
成员列表; 
};// 举例:
struct Student{char *name; //姓名int num; //学号int age; //年龄char group; //所在学习小组float score; //成绩//成员也可以是结构体
};

二、成员基本介绍

  1. 从叫法上看:有些书上称为成员, 有些书说 结构体包含的变量

  2. 成员是结构体的一个组成部分,一般是基本数据类型、也可以是数组、指针、

    结构体等 。比如我们前面定义Cat结构体 的 int age 就是一个成员。

三、注意事项和细节说明

  1. 成员声明语法同变量,示例: 数据类型成员名。
  2. 字段的类型可以为:基本类型、数组或指针、结构体等。
  3. 在创建一个结构体变量后,需要给成员赋值,如果没有赋值(初始化)就使用可能导致程序异常终止。[ 案例演示 ]:
#include<stdio.h>
void main() {struct Cat{ // 结构体名字建议首写字母大写char *name; //名字 , 这里需要使用指针类型int age; //年龄char *color; // 颜色}; struct Cat cat1;   // 没有赋值就可以以上变量指向的全是一些垃圾值,导致程序异常终止printf("\n名字=%s 年龄=%d 颜色=%s ", cat1.name, cat1.age, cat.color);getchar();
}
  1. 不同结构体变量的成员是独立,互不影响,一个结构体变量的成员 更改,不影响另外一个。[案例演示+图(Monster)]
// cat1和cat2是独立的,互不影响struct Cat cat1;  // cat1就是struct Cat的一个变量 int numstruct Cat cat2;  // cat2就是struct Cat的一个变量 int num// 给 cat1 里面的各个成员赋值cat1.name = "小白";cat1.age = 3;cat1.color = "白色";// 给 cat2 里面的各个成员赋值cat2.name = "小花";cat2.age = 100;cat2.color = "花色";

内存布局图:

image-20230314203020917

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

区块链技术与农产品溯源:实现透明供应链的关键

引言 随着食品安全问题和消费者对产品质量要求的提升&#xff0c;农产品溯源变得越来越重要。消费者希望知道他们购买的农产品从何而来&#xff0c;经历了哪些过程以及是否符合安全标准。区块链技术因其去中心化、不可篡改和透明的特点&#xff0c;成为实现农产品溯源的理想选…

如何解决与kernel32.dll相关的常见错误:详细指南解析kernel32.dll文件缺失、损坏或错误加载问题

当你的电脑中出现错误kernel32.dll丢失的问题&#xff0c;会导致电脑不能出现正常运行&#xff0c;希望能够有效的帮助你有效的将丢失的kernel32.dll文件进行修复同时也给大家介绍一些关于kernel32.dll文件的相关介绍&#xff0c;希望能够有效的帮助你快速修复错误。 kernel32.…

Unity RPG梦幻场景素材(附下载链接)

Unity RPG梦幻场景素材 点击下载资源 效果图&#xff1a; 资源链接

OpeneBayes 教程上新 | 打败 GPT-4V?超强开源多模态大模型 LLaVA-OneVision 正式上线!

大语言模型&#xff08;Large Language Model&#xff0c;简称 LLM&#xff09;与多模态大模型&#xff08;Large Multimodal Model&#xff0c;简称 LMM&#xff09;是人工智能领域的两个核心发展方向。 LLM 主要致力于处理和生成文本数据&#xff0c;而 LMM 则更进一步&#…

CesiumLab介绍

软考鸭小程序 学软考,来软考鸭! 提供软考免费软考讲解视频、题库、软考试题、软考模考、软考查分、软考咨询等服务 CesiumLab是一个围绕Cesium平台设计的完整易用的数据预处理工具集&#xff0c;它旨在最大化提升三维数据可视化效率。本文将详细介绍CesiumLab的安装、主要功能…

【JavaSE】图书系统

目录 当我们学习完Java的语法后&#xff0c;可以写一个简单的项目进行总结梳理一下&#xff0c;这个项目也会用到我们所学过的Java所有的语法知识&#xff1a;目录是咱们用文件夹包装起来的类。 1.book 在面向对象体系中&#xff0c;提出了一个软件包的概念&#xff0c;即&am…

k8s微服务

一 、什么是微服务 用控制器来完成集群的工作负载&#xff0c;那么应用如何暴漏出去&#xff1f;需要通过微服务暴漏出去后才能被访问 Service是一组提供相同服务的Pod对外开放的接口。 借助Service&#xff0c;应用可以实现服务发现和负载均衡。 service默认只支持4层负载均…

斯坦福大学提出电影剧本可视化工具ScriptViz:能够根据剧本中的文本和对话自动检索相关的电影画面,帮助剧作家更好地构思和调整剧情

title:斯坦福大学提出电影剧本可视化工具ScriptViz&#xff1a;能够根据剧本中的文本和对话自动检索相关的电影画面&#xff0c;帮助剧作家更好地构思和调整剧情 斯坦福大学的研究者们开发了一个电影剧本可视化工具ScriptViz工具&#xff0c;ScriptViz的工作原理可以简单地理解…

基于java SpringBoot和Vue校园食堂网站管理系统设计

摘要 本文旨在探讨一种基于Java Spring Boot和Vue框架的校园食堂网站管理系统的设计。首先&#xff0c;介绍了系统开发的背景及意义&#xff0c;即为了提高校园食堂的管理效率和改善学生的就餐体验。接着&#xff0c;详细阐述了系统的技术选型&#xff0c;包括后端采用Spring …

vue+ElementUI—实现基础后台管理布局(sideBar+header+appMain)(附源码)

后台管理的模板很多&#xff0c;vue本身就提供了完整的vue-template-admin&#xff0c;vue-admin-beautiful等后台管理系统化框架&#xff0c;但是这些框架正是因为成体系而显得繁重。假如你想搭建一个静态的后台管理模板页面和几个单独的菜单页面&#xff0c;直接就上框架是否…

C#源码安装ZedGraph曲线显示组件

在软件开发里,数据的显示,已经是软件开发的大头。 如果让数据更加漂亮地、智能地显示,就是软件的核心价值了。 因为不管数据千万条,关键在于用户看到图。因为一个图表,就可以表示整个数据的趋势, 或者整个数据的走向,数据频度和密码。所以图表显示是软件的核心功能,比如…

【计网】从零开始理解UDP协议 --- 理解端口号和UDP结构

我依旧敢和生活顶撞&#xff0c; 敢在逆境里撒野&#xff0c; 直面生活的污水&#xff0c; 永远乐意为新一轮的月亮和日落欢呼。 --- 央视文案 --- 从零开始理解UDP协议 1 再谈端口号2 理解UDP 报头结构3 UDP 的特点4 UDP 的缓冲区5 UDP 使用注意事项 1 再谈端口号 之前我…

Ubuntu 24.04 在 BPI-F3 上通过 SD 卡安装并从 NVME 运行

github 代码&#xff1a; https://github.com/rcman/BPI-F3 Ubuntu 24.04 现在正在我的 BPI-F3 上运行。很快会为 YouTube 制作一个视频。 这应该适用于任何版本的 Linux&#xff0c;仅在 Ubuntu 24.04 上测试过 入门 下载 Bianbu映像并使用您最喜欢的工具将其映像到微型 SD 卡…

【win10】VMware Workstation 16安装win10专业版及安装VMware Tools操作说明

参考链接 VMware虚拟机安装win10系统教程&#xff08;巨细&#xff09;_vmware安装win10-CSDN博客https://blog.csdn.net/gdidea/article/details/129523700 win10专业版安装说明 下载win10安装包 百度网盘 链接: https://pan.baidu.com/s/1kf4ORdXYgcqwAz2j86LSZw?pwdk4…

MySQL-数据库的基础操作 o(´^`)o

文本目录&#xff1a; ❄️一、数据库操作&#xff1a; ☑ 1、查看所有的数据库&#xff1a; ☑ 2、创建数据库&#xff1a; ☑ 3、使用数据库&#xff1a; ☑ 4、删除数据库&#xff1a; ❄️二、常用的数据类型&#xff1a; ➷ 1、数值类型&#xff1a; ➷ 2、字符串类型&a…

【2D/3D-Lidar-SLAM】 Cartographer详细解读

【2D/3D-Lidar-SLAM】 Cartographer详细解读 1. 摘要2. Cartographer系统数据处理流程2.1. 数据获取&#xff08;Input Sensor Data&#xff09;2.2 姿态外推器&#xff08;PoseExtrapolator&#xff09;2.3 局部建图&#xff08;Local SLAM&#xff09; 3. 关键模块实现 3.1 局…

MyBatis XML映射文件

XML映射文件 XML映射文件的名称与Mapper接口名称一致&#xff0c;并且将XML映射文件和Mapper接口放置在相同包下&#xff08;同包同名&#xff09;XML映射文件的namespace属性为Mapper接口全限定名一致XML映射文件中SQL语句的id与Mapper接口中的方法名一致&#xff0c;并保持返…

某知名国企面试题

引言 金九银十&#xff0c;求职热潮再度来袭。最近&#xff0c;有位同学去一家知名国企应聘&#xff0c;回来后带回了一套面试题。这套面试题非常典型&#xff0c;其中包含了许多供应链金融方面的典型问题。这些问题很有分享的价值&#xff0c;大家也可以先自己独立思考一下&a…

Chromium cookies数据存储位置介绍c++

一、cookies数据库存储位置&#xff1a; C:\Users\Administrator\AppData\Local\Chromium\User Data\Default\Network\Cookies 二 、数据库操作类&#xff1a; net\extras\sqlite\sqlite_persistent_cookie_store.cc net\extras\sqlite\sqlite_persistent_cookie_store.h …

003 Springboot操作RabbitMQ

Springboot整合RabbitMQ 文章目录 Springboot整合RabbitMQ1.pom依赖2.yml配置3.配置队列、交换机方式一&#xff1a;直接通过配置类配置bean方式二&#xff1a;消息监听通过注解配置 4.编写消息监听发送测试5.其他类型交换机配置1.FanoutExchange2.TopicExchange3.HeadersExcha…