C语言学习(6)—— 指针

一、指针

(1)指针是内存的地址;指针变量是保存了内存地址的变量。

(2)在声明指针变量时,如果没有确切的地址赋值,则声明为空指针:int *ptr = NULL

(2)获取变量的地址用 &,比如: int num = 10,获取num的地址:&num

(3)指针变量存的是一个地址,这个地址指向的空间存的才是值,比如: int *ptr = # ptr 就是指向int类型的指针变量,即 ptr 是 int* 类型,ptr存的是num的地址,该地址存的是num的值。

(4)获取指针变量所指向的值使用 *,比如: int *ptr = &num,使用*ptr获取ptr指向的值,即num的值。

(5)指针是一个变量,其值为另一个变量的地址。就像其他变量或常量一样,在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为: ① int *ip:一个整型的指针;(2) double *dp:一个 double 型的指针;③ float *fp:一个浮点型的指针;(4)char *ch:一个字符型的指针。

#include<stdio.h>   int main(){int num = 2;// 获取变量的值printf("num的地址为: %p \n", &num);                            // num的地址为: 000000000061FE1C// int* 表示类型为 整数指针类型// ptr是一个 int* 类型,指向了int类型变量 num 的地址int *ptr = &num;// 获取指针变量的地址(&ptr):指针变量本身也有地址printf("ptr的地址为: %p \n", &ptr);                             // ptr的地址为: 000000000061FE10// 获取指针变量存储的内容(ptr):指针变量存储的是 num 的地址printf("ptr存储的内容为: %p \n", ptr);                          // ptr存储的内容为: 000000000061FE1C// 获取指针变量指向的值(*ptr):指针变量指向的值是 num 的值printf("ptr指向的值为: %d \n", *ptr);                           // ptr指向的值为: 2return 0;
}

二、指针的算数运算

1. 自增和自减

#include<stdio.h>   int main(){int arr[] = {10, 12, 15};int *ptr = arr;              // ptr是一个 int* 类型,指向了数组的首地址,即arr[0]的地址// 通过指针来获取数组中每一个元素的值for(int i = 0; i < 3; i++){printf("arr[%d]的地址: %p \n", i, ptr);       // ptr 指向数组元素的地址printf("arr[%d] = %d \n", i, *ptr);     // *ptr 是 ptr 指向地址对应的值 ptr++;              // ptr = ptr + 1(1个 int 长度的字节数),ptr指向了数组的下一个地址}return 0;
}
#include<stdio.h>   int main(){int arr[] = {10, 12, 15};int *ptr = &arr[2];              // ptr是一个 int* 类型,指向了数组 arr[2] 的地址for(int i = 2; i >= 0; i--){printf("arr[%d]的地址: %p \n", i, ptr);printf("arr[%d] = %d \n", i, *ptr);     ptr--;              // ptr = ptr - 1(1个 int 长度的字节数),ptr指向了数组的前一个地址}return 0;
}

2. 加法和减法

#include<stdio.h>   int main(){int arr[] = {10, 12, 15};int *ptr = arr;              // ptr是一个 int* 类型,指向了数组的首地址,即arr[0]的地址// 通过指针来获取数组元素的值ptr += 2;       // ptr = ptr + 2(2个int长度的字节数),ptr指向了当前地址 +2 后的地址printf("%d \n", *ptr);     // 15ptr -= 1;printf("%d \n", *ptr);     // 12return 0;
}

三、指针函数

当函数的形参类型是指针类型时,需要传递指针(变量的地址 或 数组)。

1. 函数形参是指针

#include<stdio.h>  void add(int *num){   //    num 等于 变量 的地址(*num)++;         //    *num 指向 变量 的值
}int main(){int n = 3;int *ptr = &n;add(&n);printf("n = %d \n", n);   // n = 4add(ptr);printf("n = %d \n", n);   // n = 5return 0;
}
// 数组#include<stdio.h>  // 通过下标的方式:该方式不会使 arr 的值发生变化
int getSum1(int *arr, int size){      //  arr 指向 数组 的首地址int sum = 0;for(int i = 0; i < size; i++){sum += arr[i];}return sum;
}// 通过自增修改地址的方式:该方式会使 arr 的值发生变化
int getSum2(int *arr, int size){      //  arr 指向 数组 的首地址int sum = 0;for(int i = 0; i < size; i++){sum += *arr;                  //  *arr 为当前地址的值arr++;                        //  arr++ 指向 数组的下一个地址}return sum;
}// 通过移动地址的方式:该方式不会使 arr 的值发生变化
int getSum3(int *arr, int size){      //  arr 指向 数组 的首地址int sum = 0;for(int i = 0; i < size; i++){sum += *(arr + i);                  //  *(arr + i) 为 arr[i] 地址的值}return sum;
}int main(){int nums[] = {1, 2, 3, 4};int sum;sum = getSum1(nums, 4);printf("sum = %d \n", sum);   // sum = 10sum = getSum2(nums, 4);printf("sum = %d \n", sum);   // sum = 10sum = getSum3(nums, 4);printf("sum = %d \n", sum);   // sum = 10return 0;
}

2. 函数返回值是指针

指针函数:函数的返回值是一个指针(地址)

#include<stdio.h>  int *add(int *arr1, int len, int *arr){ for(int i = 0; i < len; i++){arr[i] = arr1[i] + 2;}return arr;
}int main(){int nums1[] = {1, 2, 3};int *nums;int *result;result = add(nums1, 3, nums);for(int i = 0; i < 3; i++){printf("%d  ", result[i]);   // 3  4  5}return 0;
}
#include<stdio.h> 
#include<string.h> char *getLong(char *string1, char *string2){  if(strlen(string1) > strlen(string2)){return string1;}else{return string2;}
}int main(){char str1[] = "ABCD";char str2[] = "EF";char *str;str = getLong(str1, str2);printf("%s", str);    // ABCDreturn 0;
}

 (1)用指针作为函数返回值时,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形参,函数返回的指针不能指向这些数据;(2)函数运行结束后会销毁所有的局部数据,这里所谓的销毁并不是将局部数据所占用的内存全部清零,而是程序放弃对它的使用权限,后面的代码可以使用这块内存;

// 错误示例#include<stdio.h>  int *getSum(int *arr, int size){    int sum = 0;        // 局部变量,在getSum返回时就会销毁for(int i = 0; i < size; i++){sum += arr[i];}return &sum;        // 错误:不能返回局部变量的地址,因为该地址在调用完函数时会销毁
}int main(){int nums[] = {1, 2, 3, 4};int *ptr;int sum;ptr = getSum(nums, 4);sum = *ptr;printf("sum = %d \n", sum); return 0;
}

(3)C语言不支持在调用函数时返回局部变量的地址,如果确实有这样的需求,需要定义局部变量为 static变量。

#include<stdio.h>  int *getSum(int *arr, int size){    static int sum = 0;        // 静态变量存储在静态存储区,该区域的不会由于函数执行情况被回收for(int i = 0; i < size; i++){sum += arr[i];}return &sum;        
}int main(){int nums[] = {1, 2, 3, 4};int *ptr;int sum;ptr = getSum(nums, 4);sum = *ptr;printf("sum = %d \n", sum); return 0;
}
#include<stdio.h>  int *add(int *arr1){static int arr[3];for(int i = 0; i < 3; i++){arr[i] = arr1[i] + 2;}return arr;
}int main(){int nums1[] = {1, 2, 3};int *result;result = add(nums1);for(int i = 0; i < 3; i++){printf("%d  ", result[i]);   // 3  4  5}return 0;
}

四、函数指针

一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。把函数的这个首地址(或称入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。

函数指针定义:函数返回值类型  (*指针名称) (函数参数列表);

注意:(1)参数列表中可以同时给出参数的类型和名称,也可以只给出参数的类型,省略参数的名称;(2)第一个括号不能省略,如果写作函数返回值类型  *指针名称 (函数参数列表)就成了函数原型。

#include<stdio.h>  int getMax(int x, int y){return x > y ? x : y;
}int getMin(int x, int y){return x > y ? y : x;
}int main(){int a = 2;int b = 3;int maxValue;int minValue;// ptrFun是函数指针的名称,int表示函数返回值为int,(int, int)表示该函数指针指向的函数形参是两个intint (*ptrFun)(int num1, int num2);ptrFun = getMax;                 // ptrFun指向函数getMax的地址maxValue = ptrFun(a, b);      // 调用函数getMaxprintf("%d", maxValue);          // 3ptrFun = getMin;                // ptrFun指向函数getMin的地址    minValue = ptrFun(a, b);     // 调用getMinprintf("%d", minValue);         // 2return 0;
}

五、回调函数(函数挂钩)

函数指针变量可以作为某个函数的形参,回调函数就是一个通过函数指针调用的函数。 

将自己编写的钩子函数挂在已经声明的函数指针上

#include<stdio.h>  int getMax(int x, int y){return x > y ? x : y;
}int getMin(int x, int y){return x > y ? y : x;
}// ptrFun是回调函数,进行函数挂钩
int function(int (*ptrFun)(int num1, int num2), int x, int y){int result = ptrFun(x, y);return result;
}int main(){int a = 2;int b = 3;int maxValue;int minValue;maxValue = function(getMax, a, b);      // 挂getMax的钩子函数,调用getMaxprintf("%d \n", maxValue);              // 3minValue = function(getMin, a, b);     // 挂getMin的钩子函数,调用getMinprintf("%d \n", minValue);             // 2return 0;
}

六、多重指针

多重指针:定义一个指向指针的指针,第一个指针包含了第二个指针的地址,第二个指针指向实际值的地址。

数据类型  **指针名

#include<stdio.h>   int main(){int num = 2;int *ptr = &num;        // 一级指针,指向 num值 的地址int **pptr = &ptr;      // 二级指针,指向 ptr 的地址printf("num = %d,  num的地址: %p \n", num, &num);printf("num = %d,  num的地址: %p,  ptr的地址: %p \n", *ptr, ptr, &ptr);printf("num = %d,  num的地址: %p,  ptr的地址: %p,  pptr的地址: %p \n", **pptr, *pptr, pptr, &pptr);
}

七、指针数组

指针数组:数组中的元素 是 基本数据类型 的地址(指针)

数据类型  *指针数组名[大小]

int *ptr[3]:ptr是一个指针数组,包含3个整数指针,其中每个元素都是一个指向int类型值 的 指针

#include<stdio.h>   int main(){int arr[] = {10, 12, 15};int *ptr[3];              // ptr是一个指针数组// 给指针数组赋值,指针数组的每个元素都是一个指向值的指针for(int i = 0; i < 3; i++){ptr[i] = &arr[i];       // ptr 的每一个元素都是 数组arr每一个元素的地址,该地址(指针)指向了arr每个元素的值}// 通过指针数组来获取 数组元素的值for(int i = 0; i < 3; i++){printf("%d \n", *ptr[i]);   // *ptr[i] 是 ptr[i] 指针 对应的值    }return 0;
}
// 字符串指针数组#include<stdio.h>   int main(){char *ptr[] = {"ABC", "DEF", "GHI"};  // 通过指针数组来获取 数组元素的值for(int i = 0; i < 3; i++){printf("%s \n", ptr[i]);     }return 0;
}

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

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

相关文章

91.网游逆向分析与插件开发-游戏窗口化助手-游戏窗口化助手的UI设计

内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;项目需求与需求拆解-CSDN博客 码云地址&#xff08;游戏窗口化助手 分支&#xff09;&#xff1a;https://gitee.com/dye_your_fingers/sro_-ex.git 码云版本号&#xff1a;e512d44da1b7e6a8726b1be0…

【Linux取经路】进程控制——程序替换

文章目录 一、单进程版程序替换看现象二、程序替换的基本原理三、程序替换接口学习3.1 替换自己写的可执行程序3.2 第三个参数 envp 验证四、结语一、单进程版程序替换看现象 #include <stdio.h> #

低代码平台四大常见用例开发:简化企业数字化进程

在数字化时代&#xff0c;企业面临着不断提升竞争力、降低成本、提高效率的压力。为了应对这些挑战&#xff0c;低代码平台应运而生&#xff0c;它能够帮助企业快速搭建所需应用&#xff0c;简化开发流程&#xff0c;降低开发成本。本文将介绍四大常见低代码平台用例&#xff0…

【Linux进程间通信】匿名管道

【Linux进程间通信】匿名管道 目录 【Linux进程间通信】匿名管道进程间通信介绍进程间通信目的进程间通信发展进程间通信分类 管道用fork来共享管道原理站在文件描述符角度——深度理解管道站在内核角度——管道本质 匿名管道在myshell中添加管道的实现&#xff1a;管道读写规则…

河道多参数浮标水质监测站在线分析仪

产品简介 浮标水质监测站是设立在河流、湖泊、水库、近岸海域等流域内进行现场水质自动监测的监测仪器&#xff0c;是以水质监测仪为核心&#xff0c;运用传感器技术&#xff0c;结合浮标体、电源供电系统、数据传输设备组成的放置于水域内的小型水质监测系统。用于连续自动监测…

【linux】文件修改记录

是的&#xff0c;在Linux上&#xff0c;您可以使用’ find 命令检查最近修改的文件。此实用程序可以搜索在指定天数内修改过的文件。你可以这样使用它: 查找主目录中最近24小时(1天)内修改过的文件。 find ~ -type f -mtime -1命令说明: -“~”表示您的主目录。 ’ -type f…

jss/css/html 相关的技术栈有哪些?

js 的技术组件有哪些&#xff1f;比如 jQuery vue 等 常见的JavaScript技术组件&#xff1a; jQuery&#xff1a; jQuery是一个快速、小巧且功能丰富的JavaScript库&#xff0c;用于简化DOM操作、事件处理、动画效果等任务。 React&#xff1a; React是由Facebook开发的用于构…

STM32--HAL库定时器学习记录(易懂)--持续学习

一、什么是定时器 定时器就是计数器&#xff0c;通过计数完成一系列功能。 二、定时器的分类 定时器分为基本定时器、通用定时器、高级定时器。级别不同&#xff0c;功能不同。级别越高&#xff0c;功能越强。 三、定时器&#xff08;计数器&#xff09;三个重要寄存器 预分…

五种主流数据库:排行榜与分页查询

默认情况下&#xff0c;查询语句会返回满足过滤条件的所有数据。但是&#xff0c;有些时候我们只需查看其中的部分结果&#xff0c;常见的这类应用场景包括 Top-N 排行榜和数据分页查询。 本文比较五种主流数据库限定查询结果数量的实现和差异&#xff0c;包括 MySQL、Oracle、…

问题:金属电化学反应的实质是氧化还原反应,被腐蚀金属发生还原反应( ) #知识分享#知识分享#媒体

问题&#xff1a;金属电化学反应的实质是氧化还原反应&#xff0c;被腐蚀金属发生还原反应(  ) A、正确 B、错误 参考答案如图所示

Ainx框架实现 一

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d7;本文收录于Ainx系列&#xff0c;大家有兴趣的可以看一看 &#x1f4d8;相关专栏Rust初阶教程、go语言基础系列…

Powershell Install 一键部署Prometheus

前言 Prometheus是一个开源的系统监控和报警系统,现在已经加入到CNCF基金会,成为继k8s之后第二个在CNCF托管的项目,在kubernetes容器管理系统中,通常会搭配prometheus进行监控,同时也支持多种exporter采集数据,还支持pushgateway进行数据上报,Prometheus性能足够支撑上…

计算机毕业设计 | vue+SpringBoot图书借阅管理系统(附源码)

1&#xff0c; 概述 1.1 课题背景 随着现在科学技术的进步&#xff0c;人类社会正逐渐走向信息化&#xff0c;图书馆拥有丰富的文献信息资源&#xff0c;是社会系统的重要组成部分&#xff0c;在信息社会中作用越来越重要&#xff0c;在我国图书馆计算机等 信息技术的应用起步…

docker安装-centos

Docker CE 支持 64 位版本 CentOS 7&#xff0c;并且要求内核版本不低于 3.10 卸载旧版本Docker sudo yum remove docker \ docker-common \ docker-selinux \ docker-engine使用yum安装 yum 更新到最新版本: sudo yum update执行以下命令安装依赖包&#xff1a; sudo yum…

PyTorch 2.2 中文官方教程(十五)

&#xff08;beta&#xff09;计算机视觉的量化迁移学习教程 原文&#xff1a;pytorch.org/tutorials/intermediate/quantized_transfer_learning_tutorial.html 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 提示 为了充分利用本教程&#xff0c;我们建议使用这个C…

算法刷题day05

目录 引言一、连号区间二、递增三元组三、特别数的和四、错误票据五、回文日期六、归并排序七、总结 引言 这些题其实之前是已经写过了的&#xff0c;但还是会有一些问题&#xff0c;比如说思路不清楚了&#xff0c;细节没有处理好&#xff0c;模板没有记熟都是问题&#xff0…

vue中使用html2canvas配合jspdf导出pdf(以及在导出时遇到的导出样式问题)

指定页面中导出为pdf格式并打包&#xff0c;使用html2canvas先转为图片格式&#xff0c;在利用jspdf转为pdf&#xff0c;最后下载打包为本地压缩包 yarn add html2canvas yarn add jspdf1. 注册一个插件并挂载 import html2Canvas from html2canvas import JsPDF from jspdf …

记一次生产系统每隔10小时(36000000毫秒)固定进行一次Full GC排查思路

一、 背景描述 某个应用在生产环境通过系统监控发现&#xff0c;应用每隔10小时就会触发一次Full GC&#xff0c;该系统当时承接的业务量并不大&#xff0c;而且固定10小时就会进行Full GC&#xff0c;通过监控时间轴发现Full GC频率很规律&#xff0c;直觉告诉我这不是JVM自身…

js实现根据字符串生成颜色

在JavaScript中&#xff0c;你可以根据给定的字符串生成一种颜色。这种操作通常需要将字符串转换为颜色代码&#xff0c;如十六进制颜色代码。下面是一个简单的示例&#xff0c;我们使用字符串的字符码来生成颜色&#xff1a; function stringToColor(str) {let hash 0;for (…

寒假 day1

1、请简述栈区和堆区的区别? 2、有一个整形数组:int arr[](数组的值由外部输入决定)&#xff0c;一个整型变量: x(也 由外部输入决定)。要求: 1)删除数组中与x的值相等的元素 2)不得创建新的数组 3)最多只允许使用单层循环 4)无需考虑超出新数组长度后面的元素&#xff0c;所以…