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,一经查实,立即删除!

相关文章

【windows】win10提示‘adb‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。

问题日志 adb devices adb 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件 解决方案 下载adb&#xff0c;将adb放到如下目录 将 adb.exe AdbWinApi.dll AdbWinUsbApi.dll 文件放到以下目录 C:\Windows\SysWOW64 C:\Windows\System32 测试验证 adb An…

被DNS污染劫持,怎么见招拆招?

在畅游互联网的海洋时&#xff0c;我们可能会遭遇DNS污染劫持这一 “暗礁”&#xff0c;它就像一个隐藏在暗处的 “海盗”&#xff0c;干扰着我们正常的网络航行。那么&#xff0c;当遇到DNS污染劫持时&#xff0c;我们该如何见招拆招呢&#xff1f; 首先&#xff0c;我们要明…

JAVA面向对象的四大特征是什么?

目录 1. 封装&#xff08;Encapsulation&#xff09; 特点&#xff1a; 代码示例&#xff1a; 解释&#xff1a; 2. 继承&#xff08;Inheritance&#xff09; 特点&#xff1a; 代码示例&#xff1a; 解释&#xff1a; 3. 多态&#xff08;Polymorphism&#xff09; …

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

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

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

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

TensorFlow 的核心概念

TensorFlow 是一个开源的机器学习框架&#xff0c;由 Google 开发和维护。它提供了一个强大的工具集&#xff0c;用于构建和训练各种机器学习模型。 TensorFlow 的核心概念是计算图&#xff08;Computational Graph&#xff09;。计算图由节点&#xff08;Nodes&#xff09;和…

QT 鼠标和键盘事件

在Qt中&#xff0c;可以使用事件处理机制来监听和处理鼠标事件和键盘事件。具体来说&#xff0c;重载事件处理函数或者使用事件过滤器是最常见的方法。以下是一些常用的事件处理函数以及如何监听鼠标事件和键盘事件的示例。 1. 处理鼠标事件 要处理鼠标事件&#xff0c;可以重…

ES6基础知识

一、定义变量的关键字let和const 1. let 定义变量的语法&#xff1a; let 变量名 值; 2. 和var定义变量的区别 1. 是否支持同一个作用域变量同名 var支持&#xff0c;let不支持 2. 是否支持预解析 var支持&#xff0c;let不支持 3. 是否会挂载在window对象…

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 则更进一步&#…

Git-本地项目同步到远程仓库

一、Git初始化项目 git initgit init 是 Git 中用于初始化一个新仓库的命令&#xff0c;通常用来在现有目录中创建一个 Git 仓库或重新初始化一个现有的 Git 仓库。通过 git init&#xff0c;你可以将一个普通的目录转变为 Git 受控的项目目录。 在当前目录中初始化一个新的 …

CesiumLab介绍

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

【JavaSE】图书系统

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

electron-vite_6js-cookie失效

我们项目是用了js-cookie&#xff0c;后续集成的时候发现&#xff0c;无法进入首页&#xff1b;经过排查是js-cookie无法使用&#xff0c;可能是electron打包后的项目运行的时候是file:/// 猜测原因&#xff1a;因为Cookie是与域名相关联的&#xff0c;而file:///协议没有域名&…

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曲线显示组件

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

2.1.ReactOS系统中中断描述符表进行初始化

2.&#xff11;.ReactOS系统中中断描述符表进行初始化 2.&#xff11;.ReactOS系统中中断描述符表进行初始化 文章目录 2.&#xff11;.ReactOS系统中中断描述符表进行初始化 VOID INIT_FUNCTION NTAPI KeInitExceptions(VOID) {ULONG i;USHORT FlippedSelector;extern KIDTEN…