C语言---------深入理解指针

目录

一、字符指针

二、指针数组:

三、数组指针:

1、定义:

2、&数组名和数组名区别:

3、数组指针的使用:

四、数组参数,指针参数:

1、一维数组传参:

2、二维数组传参:

3、一级指针传参:

4、二级指针传参:

五、函数指针:

1、定义:

2、函数名和&函数名:

3、函数指针的调用:

六、函数指针数组:

七、指向函数指针数组的指针


一、字符指针

定义:字符指针就是指向一个字符串的指针。指针类型为char*

int main()
{
	char c = 'z';
	char* pc = &c;
	printf("%c\n", c);
	return 0;
}

或者:

int main()
{
	const char* pc = "abcdef";
	printf("%s\n", pc);
	return 0;
}

这里因为是个常量字符串,所以可以用const修饰来达到后面不会被修改的问题。

上面const char* pc = "abcdef"这里并不是将abcdef赋给pc的指针变量里面,而是将字符串的首字符的地址放在pc中。

接下来看一个经典题目:

#include <stdio.h>
int main()
{
    char str1[] = "hello ppr.";
    char str2[] = "hello ppr.";
    char *str3 = "hello ppr.";
    char *str4 = "hello ppr.";
    if(str1 ==str2)
 printf("str1 and str2 are same\n");
    else
 printf("str1 and str2 are not same\n");
       
    if(str3 ==str4)
 printf("str3 and str4 are same\n");
    else
 printf("str3 and str4 are not same\n");
       
    return 0;
}

这串代码的意思是:

将hello ppr.给到数组str1和str2中,判断二者是否相等。

将hello ppr.的地址给到str3和str4中,判断二者是否相等。

那么为什么会出现这样的结果呢?

对于str1和str2,这两个是开辟了两块不同的空间,比较数组名就是比较首元素的地址,毕竟空间不同肯定比较出来就会不同。

对于str3和str4

首先要知道这是一个常量字符串是不能够被修改的,所以在内存就没有必要存储两份了,那么这两个字符指针就都会指向那同一块空间。

二、指针数组:

定义:存放指针的数组就是指针数组。

int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	int arr4[5] = { 0,0,0,0,0 };

	int* arr[4] = {arr1,arr2,arr3,arr4};

	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			//printf("%d ", arr[i][j]);
			printf("%d ", *(*(arr + i) + j));
		}
		printf("\n");
	}
	return 0;
}

如上代码所示:

arr先和右边的[]结合,就是一个数组,然后里面每一个元素都是int* ,这就是一个存放整形指针的数组。

例如:

int* arr1[10]//这是一个整形指针的数组,里面有10个元素。

char* arr2[10]//这是一个这是一个字符指针的数组,里面有10个元素。

char** arr3[10]这是一个二级字符指针的数组,里面有10个元素。

三、数组指针:

1、定义:

定义:这是一个指针,是能够指向数组的指针。

写法:int* p1[10]       or        int (*p2)[10]

这两种写法是哪一种呢?

很显然是后面的,因为前一个和指针数组一样。

因为p2和[]结合的优先级高于*,所以需要用小括号来改变其结合的优先顺序。

2、&数组名和数组名区别:

例如:

对于int arr[10];

arr和&arr分别有啥区别?

同样从代码入手:

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("arr = %p\n", arr);
	printf("(arr+1) = %p\n", arr + 1);
	printf("&arr = %p\n", &arr);
	printf("(&arr + 1) = %p\n", &arr + 1);
	return 0;
}

如上图所示,如果只看arr和&arr的话,二者是相等的,但是如果对二者都进行指针运算+1后,arr跳过了增加了4,跳过了一个整型,而&arr增加了28(十六进制),换算成十进制为40,所以就是跳过了10个整型,也就是这个数组。

故有结论:arr是这个数组的首元素的地址

                &arr是这整个数组的地址,所以+1是跳过这个数组的大小

3、数组指针的使用:

数组指针大多时候是在二维数组的传参中使用的,在一维数组中的作用不大。

依然从代码入手:

void print_arr1(int arr[3][5], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
void print_arr2(int(*arr)[5], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { 1,2,3,4,5,6,7,8,9,10 };
	print_arr1(arr, 3, 5);
	printf("\n");
	//数组名arr,表示首元素的地址
	//但是二维数组的首元素是二维数组的第一行
	//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
	//可以数组指针来接收
	print_arr2(arr, 3, 5);
	return 0;
}

如上代码所示:

这里用了自己创建的两个打印二维数组的函数,在其中唯一不同的是形参的接收不同,在print_arr1中,用传统的二维数组来接收,而在print_arr2中,用数组指针(指向数组的指针)来接收是一样的效果。

拓展:int (*parr[10])[5] 这是个啥?

首先parr和[10]结合成为一个数组,所以parr是个数组,在这个数组里面有10个元素,

其中每个元素的类型是int(*)[5]这个数组指针

四、数组参数,指针参数:

1、一维数组传参:

#include <stdio.h>
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int* arr)
{}
void test2(int* arr[20])
{}
void test2(int** arr)
{}
int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);
	test2(arr2);
}

如上代码中,所有传参均是可以的,用数组接收或者用指针接收。

2、二维数组传参:

3、一级指针传参:

当一个函数的参数部分为一个一级指针的时候,函数能接收的参数有:

void test(int* a)
{}

int main()
{
	int a = 10;
	int* pa = &a;
	int arr[10];


	test(&a);
	test(pa);
	test(arr);
	return 0;
}

4、二级指针传参:

当一个函数的参数部分为一个二级指针的时候,函数能接收的参数有:

void test(int** a)
{}

int main()
{
	int** a ;
	int* pa ;
	int* arr[10];


	test(a);
	test(&pa);
	test(arr);
	return 0;

}

五、函数指针:

1、定义:

类比一下:

数组指针是指向数组的指针,函数指针就是指向函数的指针。

(类比于数组指针)

那么函数指针该怎么写呢?

例如有一个函数int Add(int x,int y);

假设我要定义一个函数指针变量名为ppr只想Add函数

那么首先要是一个指针就需要用()来进行:(*ppr)

然后需要是个函数里面写形参的类型就是:(*ppr)(int,int)

最后看返回值:int (* ppr)(int,int)= &Add;        

2、函数名和&函数名:

函数名和&函数名在C语言中大多数情况下都是等效的,都表示函数的地址。然而,使用&可以使代码更加清晰明确。

3、函数指针的调用:

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	//int (*pf)(int, int) = &Add;
	int (* pf)(int, int) = Add;
	//int ret = Add(2, 3);
	int ret = pf(2, 3);
    int ret = (*pf)(2, 3);
	printf("%d\n", ret);
	return 0;
}

如上代码所示:

将Add赋给pf后pf就可以像Add函数一样地调用了,也可以对pf解引用后调用,语法上可以这样理解。

六、函数指针数组:

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

写法:

在函数指针的基础上改变:

如上所示:

int (* ppr)(int,int)这是一个函数指针,

int (* ppr[5])(int,int)此时ppr会和[5]先结合,就是一个数组,去掉数组名和个数剩下的

int (*)(int,int)这个就是函数指针类型。

应用场景:转移表

七、指向函数指针数组的指针

我们从函数指针数组入手:

int (* pr[5])(int,int)这是一个函数指针,

现在我想要一个指向函数指针数组的指针,所以就可以将ppr与一个*结合使其成为指针

int (* (*ppr)[5])(int,int) = &pr;

这个就成为了 指向函数指针数组的指针

这个的函数指针类型就是去掉 (*ppr)

这个int (* [5] )(int,int)就是一个函数指针类型

接下来画个图来理解:


 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

单列集合顶层接口Collection及五类遍历方式(迭代器)

collection add方法细节&#xff1a; remove方法细节&#xff1a; contains方法细节&#xff1a; 如果集合中存储的是自定义对象, student之类的, 也想通过contains进行判断, 就必须在javaBean中重写equals方法 contains在arrayList中源代码&#xff1a;在底层调用了equals方…

对候选人得票的统计程序

一个结构体变量中可以存放一组数据&#xff08;如一个学生的学号、姓名、成绩等数据&#xff09;。如果有10个学生的数据需要参加运算&#xff0c;显然应该用数组&#xff0c;这就是结构体数组。结构体数组与以前介绍过的数值型数组不同之处在于&#xff1a;每个数组元素都是一…

认识Redis 主从同步、事务和Memcached的区别

08- 什么是 Redis 主从同步&#xff1f; Redis 的主从同步(replication)机制&#xff0c;允许 Slave 从 Master 那里&#xff0c;通过网络传输拷贝到完整的数据备份&#xff0c;从而达到主从机制。 主数据库可以进行读写操作&#xff0c;当发生写操作的时候自动将数据同步到从…

React+TS前台项目实战(十)-- 全局常用组件CopyText封装

文章目录 前言CopyText组件1. 功能分析2. 代码详细注释3. 使用方式4. 效果展示 总结 前言 今天这篇主要讲项目常用复制文本组件封装&#xff0c;这个组件是一个用于拷贝文本的 React 组件&#xff0c;它提供了拷贝&#xff0c;国际化和消息提示的功能 CopyText组件 1. 功能分…

HTML表格的跨行与跨列:《红楼梦》人物与小学课表示例

在HTML中&#xff0c;表格不仅可以按常规行和列排列数据&#xff0c;还可以通过跨行&#xff08;rowspan&#xff09;和跨列&#xff08;colspan&#xff09;属性来合并单元格&#xff0c;以适应更复杂的数据展示需求。以下是跨行与跨列属性的介绍&#xff0c;以及两个示例&…

全网爆火《pvz植物大战僵尸杂交版》最新安装包,Android、Windows、ios安装包+教程!

今天阿星想和大家分享一个最近在B站上引起轰动的老游戏——《植物大战僵尸》&#xff01; 是的&#xff0c;你没听错&#xff0c;就是那个曾经让我们熬夜到天亮&#xff0c;一关接一关挑战的游戏。 让我们来聊聊&#xff0c;这款游戏怎么就突然又火了起来呢&#xff1f; 原来…

4款好用的文本扩展器!!提高工作效率!【送源码】

今天的文章中为大家带来几款好用的文本扩展器&#xff0c;帮助大家提供工作效率&#xff0c;减少重复劳动&#xff5e; Beeftext Beeftext 是一个文本扩展工具&#xff0c;可以帮助用户快速输入短语、段落或者常用的文本片段。它允许你创建自定义的缩写和对应的文本替换&…

HTTP-代理

HTTP-代理 web代理服务器是网络的中间实体&#xff0c;代理位于客户端和服务器之间&#xff0c;扮演者中间人的角色&#xff0c;在各端点之间来回传递http报文 web的中间实体 web上的代理服务器是代表客户端完成事务处理的中间人&#xff0c;如果没有web代理&#xff0c;htt…

【猫狗分类】Pytorch VGG16 实现猫狗分类4-开始训练

背景 现在&#xff0c;我们已经完成了&#xff0c;数据集的清洗&#xff0c;标签的制作&#xff0c;也把VGG16的模型建立好了。那接下来&#xff0c;我们应该把数据&#xff0c;放到我们搭建的vgg16的模型里面&#xff0c;让模型针对这些猫和狗的图片&#xff0c;去进行训练&a…

MyBatis操作数据库(一)

什么是MyBatis? MyBatis是一个优秀的持久层框架&#xff0c;⽤于简化JDBC的开发。 MyBatis本是Apache的⼀个开源项⽬iBatis&#xff0c;2010年这个项目由apache迁移到了googlecode&#xff0c;并且改名为MyBatis。 简单来说MyBatis是更加简单完成数据和数据库交互的框架 什么…

内存泄漏 内存溢出

概念 内存泄漏&#xff1a;是程序没有正确的释放已分配的内存&#xff0c;造成系统内存的浪费。内存泄漏很难发现&#xff0c;因为他不会直接导致程序崩溃&#xff0c;而是会慢慢降低程序的性能。 内存溢出&#xff1a;系统中存在无法回收的内存或使用的内存过多&#xff0c;…

【C#】使用JavaScriptSerializer序列化对象

在C#开发语言编程中&#xff0c;通常使用系统内置的JavaScriptSerializer类来序列化对象&#xff0c;以便将其转换为JSON格式的文本存储与后台服务通信, 在这里将为大家详细介绍一下这个过程。 文章目录 反序列化序列化忽略属性 假设处理的数据中有一个对象类, 如下 public cl…

逆天改命 17岁中专女生横扫全球数学竞赛

“逆天改命!17岁中专女生横扫全球数学竞赛,清华北大高手纷纷落马!” 最近全网被这则消息震惊了。 来!随便挑几个题目,让大家体验一下阿里巴巴全球数学竞赛的难度? 数学是人工智能算法的基石。它为算法提供了逻辑框架和分析工具,使得人工智能能够处理复杂的数据和问…

电商秒杀系统

一&#xff0c;细节 二&#xff0c;需要注意的细节 1.库存超卖问题 使用mysql数据库的 悲观锁 机制。在事务中使用 for update 语句&#xff0c;此时数据库会加锁&#xff0c;其他想要当前读的线程都会被阻塞&#xff0c;在事务处理完成之后释放这一条数据。该方法的缺点在于…

基于springboot实现入校申报审批系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现入校申报审批系统演示 摘要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装入校申报审批系统软…

英伟达开源最强通用模型Nemotron-4 340B

英伟达的通用大模型 Nemotron&#xff0c;开源了最新的 3400 亿参数版本。 本周五&#xff0c;英伟达宣布推出 Nemotron-4 340B。它包含一系列开放模型&#xff0c;开发人员可以使用这些模型生成合成数据&#xff0c;用于训练大语言模型&#xff08;LLM&#xff09;&#xff0…

排序——希尔排序

希尔排序实际上是插入排序的优化&#xff0c;所以要先介绍插入排序。 目录 插入排序 思想 演示 代码实现 总结 希尔排序 思想 演示 代码 总结 插入排序 思想 又称直接插入排序。它的基本思想是将一个值插入到一个有序序列中。直至将所有的值都插入完。 演示 假设数…

Web爬虫--fofa-资产信息搜集

免责声明:本文仅做技术交流与学习... 目录 fofa.py fofa搜索参数分析 fofa_api.py fofa.py import requests from bs4 import BeautifulSoup# 登录fofa之后,把自己的cookie弄过来. header{cookie: } # 参数为搜索的语法. urlhttps://fofa.info/result?qbase64dGl0bGU9IuS4…

云计算【第一阶段(14)】Linux的目录和结构

一、Liunx目录结构 1.1、linux目录结构 linux目录结构是树形目录结构 根目录&#xff08;树根&#xff09; 所有分区&#xff0c;目录&#xff0c;文件等的位置起点整个树形目录结构中&#xff0c;使用独立的一个"/",表示 1.2、常见的子目录 必须知道 目录路径目…

xinput1_3.dll怎么安装,关于xinput1_3.dll的多种修复方法分享

在电脑使用过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“找不到xinput1_3.dll”。那么&#xff0c;xinput13.dll到底是什么&#xff1f;为什么会出现找不到的情况&#xff1f;它对电脑有什么影响&#xff1f;本文将为您详细解析xinput1_3.dll的含义…