C语言——指针完全版

目录

一、指针的运算

1.1指针 +- 整数

1.2指针 - 指针

二、指针遍历数组

2.1指针遍历数组

1.了解数组名称的含义(&数组名和数组名的区别)。

2.用指针遍历数组 

三、指针数组、数组指针、函数指针

3.1指针数组

3.1.1指针数组的形式

3.1.2指针数组的使用案例

3.2数组指针

3.3函数指针

三、函数指针数组

3.1函数指针数组的形式

3.2函数指针数组的用途:转移表

四、数组作为函数体参数

五、函数中用数组作为返回值

5.1返回静态局部数组的地址

5.2返回文字常量区的字符串的地址

5.3返回堆内容的地址

5.4总结


一、指针的运算

1.1指针 +- 整数

总结:指针的类型决定了指针向前或者向后走一步有多大(距离)。

1.2指针 - 指针

int main()
{int arr[10] = { 0 };printf("%d\n", &arr[9] - &arr[0]);return 0;
}

当我们想用两个指针相减时,从上图我们可以猜测一下,输出的到底是两地址之间的元素个数还是两个地址的差值(byte)呢?

通过运行我们可以知道,指针 - 指针的结果:指针和指针之间的元素个数。

那么任何两个指针都可以相减吗?从上面的答案我们就可以得到:

指针 - 指针的前提两个指针指向同一块区域,指针类型是相同的。

那么如果用小指针 - 大指针,我们的结果就成了 -9 .

所以我们可以得到更严谨一些的结论:

指针 - 指针的结果:其绝对值为指针与指针之间的元素个数。

二、指针遍历数组

2.1指针遍历数组

1.了解数组名称的含义(&数组名和数组名的区别)。

数组名是数组首元素的地址。有两个例外:
1. sizeof( 数组名 ) ,计算整个数组的大小, sizeof 内部单独放一个数组名,数组名表示整个数组。
2. & 数组名,取出的是数组的地址。 & 数组名,数组名表示整个数组。
除此 1,2 两种情况之外,所有的数组名都表示数组首元素的地址。

我们来看一段代码了解&数组名和数组名的差别。

int main()
{int arr[10] = { 0 };printf("arr = %p\n", arr);printf("&arr= %p\n", &arr);printf("arr+1 = %p\n", arr+1);printf("&arr+1= %p\n", &arr+1);return 0;
}

根据上面的代码我们发现,其实 &arr arr ,虽然值是一样的,但是意义应该不一样的。
实际上: &arr 表示的是 数组的地址 ,而不是数组首元素的地址。
数组的地址 +1 ,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是 40.

2.用指针遍历数组 

既然可以把数组名当成地址存放到一个指针中,我们使用指针来访问一个就成为可能。

#include <stdio.h>
int main()
{int arr[] = {1,2,3,4,5,6,7,8,9,0};int *p = arr; //指针存放数组首元素的地址int sz = sizeof(arr)/sizeof(arr[0]);//数组元素个数for(int i = 0; i < sz; i++){printf("&arr[%d] = %p   <====> p+%d = %p\n", i, &arr[i], i, p+i);}return 0;
}

 可以看到,我们可以通过指针遍历数组的每一个元素:

p+i 其实计算的是数组 arr 下标为i的地址
那么 *(p+1) 就可以访问数组 arr 的每一个元素

三、指针数组、数组指针、函数指针

3.1指针数组

指针数组是存放指针的数组。

3.1.1指针数组的形式

我们先来看一下指针数组的样子(其他类型同理):

int* arr[5];    //数组名称:arr  数组元素个数:5  数组元素的类型:int* 

那么知道指针数组有什么用呢?

3.1.2指针数组的使用案例

我们已经知道数组名在绝大多数情况下表示的是数组首元素的地址,那么我们就可以用指针数组模拟出一个二维数组。

int arr1[] = { 0,1,2,3,4,5 };
int arr2[] = { 1,2,3,4,5,6 };
int arr3[] = { 2,3,4,5,6,7 };int* arr[] = { arr1,arr2,arr3 };

那我们应该怎么去使用这个模拟出来的二维数组呢?

我们直接可以用它的下标进行访问。

区别:

我们模拟出这个数组和真正二维数组的区别其实就是真实的二维数组每个元素的元素地址都是连续的,而模拟出的二维数组它的每个一维数组地址并不是连续的。

3.2数组指针

数组指针是指向数组的指针。

int(*p)[10];/*解释:p先和 * 结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。*///这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

我们知道整形、全精度浮点型等等都有一个数据类型,int、double......                                            当然在这里,数组指针也有它的数据类型。

3.3函数指针

我们可以先来看一段代码

#include <stdio.h>
void test()
{printf("hello!\n");
}
int main()
{printf("%p\n", test);//打印test地址printf("%p\n", &test);//打印&test地址return 0;
}

从运行结果我们可以看出来,函数名和数组名其实具有一样的效果,但是,为了便于观察,以后在我们需要函数地址的时候,我们还是会用 &函数名 来实现

如果我们想用指针存储上面的test函数,我们应该怎么办呢?

void(*pf)() = &test;
pf1先和*结合,说明pf1是指针,指针指向的是一个函数,指向的函数无参数,返回值类型为void。

由上面我们可以得到:

int max(int a, int b)
{if (a > b)return a;elsereturn b;
}
int main()
{int (*pf2)(int, int) = &max;return 0;
}

这样我们就可以清楚的了解到函数指针的形式。

三、函数指针数组

3.1函数指针数组的形式

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,
比如:
int * arr [ 10 ];
// 数组的每个元素是 int*
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
(以上述 int max(int a, int b) 为例,假设函数指针数组存放的都是此类函数)
int( * parr[ 10])( int, int);
parr先和 [ ] 结合,表面parr是个数组。
数组的内容是什么呢?
int (*)(int, int) 类型的函数指针。

3.2函数指针数组的用途:转移表

我们以一个简单的计算器为例,来看看使用函数指针数组的便捷。
准备内容:
未使用函数指针数组时,我们需要根据用户输入情况,一个函数一个函数的调用:
使用函数数组后,我们只需要把准备阶段的四个函数全部封装到一个函数指针数组中:

再接下来使用的时候,我们就可以灵活运用:

是不是简化了非常多!

四、数组作为函数体参数

定义函数体时,当参数为数组时,既可以用数组接收,也可以用指针接收,其中用数组接收与数组的元素下标无关。

五、函数中用数组作为返回值

返回数组其实就是返回指针。

char* fun()
{char str[100] = "hello world!";return str;
}
int main()
{char* pf;pf = fun();printf("%s\n", pf);return 0;
}

5.1返回静态局部数组的地址

上述代码有个明显缺陷:

str 在 fun 函数体内创建,为临时变量,当我们在 main 函数中想用pf接收的时候, str 就已经被销毁了,这时候,我们就可以在 fun 函数内用 static 修饰我们的 str ,来延长它的生命周期。

运行如图:

5.2返回文字常量区的字符串的地址

常量字符串是个常量,一直存在。

5.3返回堆内容的地址

malloc的头文件:

#include<stdlib.h>

malloc 为 str 动态申请内存,大小为100,单位是字节。                                                                动态申请的内存在函数结束后也不会被释放。

堆区内容一直存在,直到 free 才释放。

5.4总结

返回的地址,地址指向的内存的内容得存在,才有意义。

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

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

相关文章

【自学笔记】如何在 Python 中使用 YAML 文件? 了解 YAML 格式和规范

文章目录 如何在 Python 中使用 YAML 文件YAML 的格式、规范和需要注意的点YAML 的缩进对象块其语法规范在 Python 中使用 PyYAML 模块安装 PyYAML 模块使用 PyYAML 模块读取和写入 YAML 文件读取 YAML 文件写入 YAML 文件load() 和 safe_load() 的区别总结如何在 Python 中使用…

day33 List接口

List实现类 java.util.ArrayList&#xff1a; 底层通过数组保存数据 &#xff0c; 查询快&#xff0c;增删慢 java.util.LinkedList&#xff1a; 底层通过链表保存数据&#xff0c; 查询慢&#xff0c;增删快 如果对操作性能没有特殊要求&#xff0c;我们一般选择ArrayList…

【力扣每日一题】2023.9.7 修车的最少时间

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们一个数值&#xff0c;数组里每个元素表示一个老师傅&#xff0c;老师傅修车花费的时间等于数值乘上车辆数的平方。 问我们修理…

vue中使用tailwindcss

Tailwind CSS with Vue tailwindcss官方文档 创建Vue项目 npm create vitelatest my-project -- --template vue cd my-project安装Tailwind CSS&#xff0c;创建tailwind.config.js和postcss.config.js npm install -D tailwindcss postcss autoprefixer npx tailwindcss …

编程语言排行榜

以下是2023年的编程语言排行榜&#xff08;按照流行度排序&#xff09;&#xff1a; Python&#xff1a;Python一直以来都是非常受欢迎的编程语言&#xff0c;它简洁、易读且功能强大。在数据科学、机器学习、人工智能等领域有广泛应用。 JavaScript&#xff1a;作为前端开发…

你不知道的JavaScript---对象

1.语法 对象可以通过两种方式定义&#xff1a;一种是对象字面量形式&#xff0c;一种是构造形式 对象字面量&#xff1a; var muObject {key: value }构造形式的&#xff1a; var myObject new Object() myObject.key value不管是使用对象字面量形式还是构造形式创建出来…

idea:java: Compilation failed: internal java compiler error

java: Compilation failed: internal java compiler error错误 检查下面2个即可&#xff1a;

docker 生成镜像的几个问题

docker 生成镜像的几个问题 根据jdk8.tar.gz 打包Jdk8 镜像失败运行镜像报错差不多是网络ip错误,在网上说重启docker即可解决运行mysql5.7.25 镜像失败向daemon.json文件添加内容导致docker重启失败docker run 命令常用参数根据jdk8.tar.gz 打包Jdk8 镜像失败 首选做准备工作…

卡牌类游戏推荐,卡牌类三国手游排行榜

以下是小编要推荐给大家的关于卡牌类三国手游排行榜的内容。这里有来自各个历史阶段的名将和美女&#xff0c;让你体验最真实的三国战役。你可以将各种战略思维运用到其中&#xff0c;感受步步为营的喜悦&#xff0c;最终赢得战火纷飞的三国&#xff0c;如果想了解每个游戏的具…

浅谈安科瑞ADL200仪表在爱尔兰工厂的应用

摘要&#xff1a;用户端消耗着整个电网80%的电能&#xff0c;用户端智能化用电管理对用户可靠、安全、节约用电有十分重要的意义。构建智能用电服务体系&#xff0c;推广用户端智能多功能仪表、智能用电管理终端等设备用电管理解决方案&#xff0c;实现电网与用户的双向良性互动…

失效的访问控制及漏洞复现

文章目录 渗透测试漏洞原理失效的访问控制1. 失效的访问控制1.1 OWASP TOP 101.1.1 A5:2017-Broken Access Control1.1.2 A01:2021-Broken Access Control 1.2 失效的访问控制类别1.2.1 水平越权1.2.2 垂直越权 1.3 攻防案例1.3.1 DVWA越权 1.4 相关漏洞1.4.1 目录遍历1.4.2 未…

【Redis】Bitmap 使用及应用场景

前言&#xff1a;bitmap 占用空间小&#xff0c;查询效率高&#xff0c;在一些场景中使用 bitmap 是一个很好的选择。 一、bitmap 相关命令 SETBIT - 设置指定位置的比特值&#xff0c;可以设为 1 或 0 例如 SETBIT key 10 1&#xff0c;将在 key 对应的 bitmap 中第10位设置为…

explain 实战-----查看hive sql执行计划

目录 1.join/left join/full join 语句会过滤关联字段 null 的值吗&#xff1f; &#xff08;1&#xff09;join &#xff08;2&#xff09; left join /full join 2.group by 分组语句会进行排序吗&#xff1f; 1.join/left join/full join 语句会过滤关联字段 null 的值吗…

【java】【SSM框架系列】【一】Spring

目录 一、简介 1.1 为什么学 1.2 学什么 1.3 怎么学 1.4 初识Spring 1.5 Spring发展史 1.6 Spring Framework系统架构图 1.7 Spring Framework学习线路 二、核心概念&#xff08;IoC/DI&#xff0c;IoC容器&#xff0c;Bean&#xff09; 2.1 概念 2.2 IoC入门案例 …

docker安装RabbitMQ教程

可以通过Docker来安装RabbitMQ&#xff0c;具体步骤如下&#xff1a; 安装Docker&#xff1a;请参考官方文档进行安装。 拉取RabbitMQ镜像&#xff1a;通过以下命令拉取最新版本的RabbitMQ镜像。 docker pull rabbitmq:latest运行RabbitMQ容器&#xff1a;通过以下命令运行Rab…

简单YUV数据转换

YUV是一种亮度信号Y和色度信号U、V是分离的色彩空间&#xff0c;它主要用于优化彩色视频信号的传输&#xff0c;使其向后相容老式黑白电视。其中“Y”表示明亮度&#xff08;Luminance或Luma&#xff09;&#xff0c;也就是灰阶值&#xff1b;而“U”和“V”表示的则是色度&…

vue3 defineExpose 显示的指定组件需要暴露的属性

简介&#xff1a; 是 vue3 新增的一个 api &#xff0c;用在 <script setup>中使用&#xff0c;用于显示的把组件的属性和方法暴露出来。可用于父子组件通信&#xff0c;子组件使用 defineExpose 将自身的方法或者属性暴露出去&#xff0c;父组件中通过 ref 获取子组件 D…

Android12.0首次开机默认授予app运行时权限(去掉运行时授权弹窗)第二种方法

1.概述 在12.0的系统产品开发中,在6.0以后对于权限的申请,都需要动态申请,所以会在系统首次启动后,在app的首次运行时,会弹出授权窗口,会让用户手动授予app运行时权限,在由于系统产品开发需要要求默认授予app运行时权限,不需要用户默认授予运行时弹窗,所以需要在首次开…

CH06_第一组重构(上)

提取函数&#xff08;Extract Function |106&#xff09; 曾用名&#xff1a;提炼函数&#xff08;Extract Function&#xff09; 反向重构&#xff1a;内联函数&#xff08;115&#xff09; 示例代码 function printOwing(invoice) {printBanner();let outstanding calcul…

API安全学习 - crAPI漏洞靶场与API测试思路

crAPI漏洞靶场与解题思路 1. 前置基础1.1 认识crAPI1.2 环境搭建1.3 API的分类与鉴别 2. 漏洞验证2.1 失效的对象级别授权挑战1&#xff1a;访问其它用户车辆的详细信息挑战2&#xff1a;访问其它用户的机械报告 2.2 失效的用户身份验证挑战3&#xff1a;重置其它用户的密码 2.…