C语言——指针完全版

news2024/9/21 22:36:04

一、指针的运算

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;
	else
		return 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.coloradmin.cn/o/986767.html

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

相关文章

【100天精通Python】Day57:Python 数据分析_Pandas数据描述性统计,分组聚合,数据透视表和相关性分析

目录 1 描述性统计&#xff08;Descriptive Statistics&#xff09; 2 数据分组和聚合 3 数据透视表 4 相关性分析 1 描述性统计&#xff08;Descriptive Statistics&#xff09; 描述性统计是一种用于汇总和理解数据集的方法&#xff0c;它提供了关于数据分布、集中趋势和…

怎么把pdf转换成jpg图片?

怎么把pdf转换成jpg图片&#xff1f;在工作中&#xff0c;如果我们收到无法修改编辑的PDF文件&#xff0c;可能会遇到一些困难。尤其是当平台或网站只支持JPG图片格式&#xff0c;而领导又要求我们将pdf文件改为JPG格式时&#xff0c;情况就更为棘手了。这对于我们打工一族来说…

二、模型驱动测试设计

如果能够提升抽象层级&#xff0c;测试设计师会更加有效和有效率。 完全改正软件是不可能到达的&#xff0c;其原因是可以以形式化的方式来表述的而且是富有哲理的。聪明的软件工程师不再追求软件的完全正确&#xff0c;而是试着评判软件的行为来决定其是否为可接受的。**包括可…

Linux之SELinux

目录 概述 定义 作用 SELinux与传统的权限区别 SELinux工作原理 名词解释 主体&#xff08;Subject&#xff09; 目标&#xff08;Object&#xff09; 策略&#xff08;Policy&#xff09; 安全上下文&#xff08;Security Context&#xff09; 文件安全上下文查看 …

【MySQL基础】事务隔离03

目录 隔离性与隔离级别事务隔离的实现事务的启动方式MySQL事务代码示例 在MySQL中&#xff0c;事务支持是在引擎层实现的。MySQL是一个支持多引擎的系统&#xff0c;但并不是所有的引擎都支持事务。比如 MySQL 原生的 MyISAM 引擎就不支持事务&#xff0c;这也是 MyISAM 被 Inn…

永安通配符和多域名SSL证书的区别

随着互联网的快速发展&#xff0c;现在大多数人都已经习惯在网上交流、购物、学习&#xff0c;因此互联网上的各种类型的网站越来越多&#xff0c;不仅是企事业单位创建各种类型的网站&#xff0c;个人开发者创建的网站也越来越多&#xff0c;一张单域名SSL就不能满足个人或者企…

Windows云服务器 PHP搭建网站外网无法访问的问题

前言&#xff1a;本人在华为云上租了一台windows的云主机&#xff0c;可以远程访问桌面的那种&#xff0c;然后想搭个网站&#xff0c;最开始想到的是IIS&#xff0c;测试了下用html的文件&#xff0c;没有问题。但是&#xff0c;php文件却不能用&#xff0c;因为少了PHP环境。…

【LeetCode - 每日一题】2594. 修车的最少时间(23.09.07)

2594. 修车的最少时间 题意 给定每个师傅修车的时间和需要修的车辆总数&#xff0c;计算修理所有汽车需要的最少时间。师傅可以同时修车。 解法 二分 看到题目没有任何头绪&#xff0c;直接查看题解。 至于为什么用二分做呢&#xff0c;讨论区有个友友这么说到&#xff1a…

【Linux】LVM原理及核心概念

LVM是什么&#xff1f;LVM核心概念LVM的优势在Linux上使用LVM感谢 &#x1f496; LVM是什么&#xff1f; LVM是一种高级的磁盘管理工具&#xff0c;用于在Linux和其他类Unix操作系统中管理磁盘存储。它的核心思想是将底层物理存储抽象为逻辑存储单元&#xff0c;从而提供了更大…

如何使用HTTP代理爬虫,防止对网站造成负面影响

在当今大数据时代&#xff0c;爬虫技术已经成为了获取数据的重要手段之一。但是&#xff0c;由于爬虫程序的高频访问容易对目标网站造成负面影响&#xff0c;如增加服务器负载、影响网站性能等&#xff0c;因此&#xff0c;如何使用HTTP代理爬虫防止对网站造成负面影响成为了一…

idea中mapper直接跳转到xml的插件

一.点击File | Settings | Plugins&#xff0c;下载插件 二、重启idea

Shopify电子邮件营销方法?邮件营销的技巧?

Shopify电子邮件营销怎么操作&#xff1f;独立站如何做邮件营销? Shopify电子邮件营销是一种强大的工具&#xff0c;可帮助电商企业与其客户建立联系并提高销售。蜂邮EDM将探讨一些有效的Shopify电子邮件营销方法&#xff0c;以帮助您最大限度地利用这一策略。 Shopify电子邮…

suning苏宁API接入说明(苏宁商品详情+关键词搜索商品列表)

API地址:https://o0b.cn/anzexi 调用示例&#xff1a;https://api-gw.onebound.cn/suning/item_get/?keytest_api_key& &num_iid0070134261/703410301&&langzh-CN&secret 参数说明 通用参数说明 version:API版本key:调用key,测试key:test_api_keyapi_na…

攻防世界-WEB-ics-05

打开靶机 只有设备维护中心可以点开 点标签得到新的url pageindex 想到文件包含漏洞&#xff08;URL中出现path、dir、file、pag、page、archive、p、eng、语言文件等相关关键字眼 利用php伪协议查看源码 出现一段base64源码&#xff0c;进行转码得出源码 ?pagephp://filter…

Java-day13(IO流)

IO流 凡是与输入&#xff0c;输出相关的类&#xff0c;接口等都定义在java.io包下 1.File类的使用 File类可以有构造器创建其对象&#xff0c;此对象对应着一个文件(.txt,.avi,.doc,.mp3等)或文件目录 File类对象是与平台无关的 File中的方法仅涉及到如何创建&#xff0c;…

数据挖掘的学习路径

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

DGIOT-Modbus-RTU控制指令05、06的配置与下发

[小 迪 导 读]&#xff1a;伴随工业物联网在实际应用中普及&#xff0c;Modbus-RTU作为行业内的标准化通讯协议。在为物联网起到采集作用的同时&#xff0c;设备的控制也是一个密不可分的环节。 场景解析&#xff1a;在使用Modbus对设备进行采集后&#xff0c;可以通过自动控制…

nested exception is java.io.FileNotFoundException

完整的错误信息&#xff1a; [main] ERROR o.s.boot.SpringApplication - Application run failed org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.heima.article.ArticleApplication]; nested exception is java…

Android 网络配置

adb root adb shell 改变网卡网址 ifconfig eth0 192.168.0.167 up 添加虚拟网卡 ifconfig eth0:0 192.168.10.10 up 以上的命令就可以在eth0网卡上创建一个叫eth0:0的虚拟网卡,他的地址是:192.168.10.10 删除虚拟网卡 ifconfig eth0:0 down ip route 查看路由表的内容 …

如何选择合适的HTTP代理服务器

HTTP代理服务器是一种常见的网络代理方式&#xff0c;它可以帮助用户隐藏自己的IP地址&#xff0c;保护个人隐私和安全。然而&#xff0c;选择合适的HTTP代理服务器并不容易&#xff0c;需要考虑多个因素。本文将介绍如何选择合适的HTTP代理服务器。 了解代理服务器的类型 HTT…