首页 > 其他分享 >指针的详解延续二

指针的详解延续二

时间:2024-06-01 23:28:25浏览次数:14  
标签:arr return int 延续 地址 详解 数组 指针

 第一篇移步CSDNicon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/139301675

 第二篇移步CSDN​​​​​​​​​​​icon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/139329194

目录

一、指针数组

二、字符指针变量

三、数组指针变量

四、二维数组传参的本质

五、函数指针变量         

六、typedef关键字

ok,也是老样子六个标题用完了,写这个东西对我的知识梳理其实挺大的,希望也能帮助到你们,好像还有一点没有写完,哈哈                                                                                            


一、指针数组

指针数组是指针?还是数组呢?

char arr[10] 字符数组——存放字符的数组

int arr[5]整形数组——存放整型的数组

指针数组——存放指针的数组,数组的每个元素其实是指针类型

char *arr[5]存放字符指针的数组 数组中的每个元素是char*

int *arr[5]存放整型指针的数组  数组中的每个元素是int*

指针数组的每个元素是地址,又可以指向一块区域

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	//int* pa = &a;
	//int* pb = &b;
	//int* pc = &c;
	int* arr[3] = { &a,&b,&c };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d ", *(arr[i]));
	}

}

这个时候就能通过解引用操作符来将指针数组中的元素进行取出来。

接下来写一个指针数组来模拟二维数组

#include <stdio.h>
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* arr[] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
			printf("%d ", arr[i][j]);
		printf("\n");
	}
	return 0;
}

我们并没有创建一个二维数组,而是通过一个指针数组将它三个一维数组们串联起来

arr[i][j]等价于(*(arr+i)+j) ___*(arr+i)拿到的是一行的数组名,相当于arr[i]是首元素的地址+j就可以访问一行数组中的元素的地址,再进行解引用就能找出每个元素的取值。

二、字符指针变量

#include <stdio.h>
int main()
{
	//char* p = "abcdef";
	char arr[]= "abcdef";
	char* p = arr;
	return 0;
}

我们来看第一行的代码和下面两行的代码有什么区别

首先我们先来说下面两行的代码

1.下面是创建了一个数组。然后指针指向的是数组首元素的地址,其次数组的特点是连续且里面的字符串的内容是可以被修改的

2.第一行的代码是一个后面的内容是一个常量字符串,常量字符串的内容是连续不可以被修改的,但是这六个字节大小怎么存储到只有四个字节大小的指针呢,所以这里的指针指向的也是常量字符串首元素的地址,我们来验证一下这个内容。

这里补充一个知识点,使用%s进行打印字符串的时候只需要提供首元素的地址就行了,所以也不需要进行解引用进行操作

因为这里是常量字符串所以我们在进行以下操作的时候是做不到的 

既然不期望被修改,我们就可以用到

#include <stdio.h>
int main()
{
	const char* p = "abcdef";

	printf("%c\n", *p);
	printf("%s\n", p);


	return 0;
}

加上一个const来对它进行一个约束,这是一个标准的写法,因为你不加const其实也不会报错但不会过去。

三、数组指针变量

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

我们接着用类比法来推

字符指针——char*——指向字符的指针-字符指针变量中存放字符变量的地址

整型指针-int*——指向整型的指针——整形指针变量中存放整型变量的地址

数组指针——指向数组的指针——数组指针变量中存放数组的地址

什么是数组的地址,在前面我们说过其实是&数组名

int main()
{
	int arr[10] = { 1,2,3,4,5 };
	int(*p)[10] = &arr;//p就是数组指针,p中存放的是数组的地址
	return 0;
}

int * ptr 我们来分析ptr是整型指针int *是他的类型

那么对于int(*p)[10]呢,去掉名字其实就是它的类型类型就是int(*)[10]

所以说&arr的类型也要跟等号的右边相符合就是int(*)[10]

arr——的类型是int * arr+1跳过四个字节

&arr[0]——的类型也是int * &arr[0]+1也是跳过四个字节

&arr——的类型是int(*)[10]  &arr+1跳过的是40个字节,这也解答了我们前面留下的悬念,这主要由他的类型来决定。

这是一个指针,它指向的是一个大小为10个整形的数组。

这里要注意:[ ]的优先级要高于*号的,所以必须加上()来保证p先和*结合

那我们想使用p这个数组访问arr数组的内容

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int(*p)[10] = &arr;
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", (*p)[i]);
	}
}

这里我们的(*p)[i]中的(*p)相当于拿到了一个数组,拿到了一个数组相当于拿到了一个数组名所以我们想要访问它的每个元素只需要给他加一个下标就完事了,当然这样写有点麻烦主要是为了体现数组指针变量的特点

int main()
{
	//int a = 10;
	//int* p = &a;
	//*p;//*&a==a;
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int*p = arr;
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", p[i]); //p[i]--*(p+i)
	}
}

由上面几行代码我们可以发现,其实p=&a;然后*p这样其实是一个*&可以相互抵消的操作

所以其实我们也不需要写的那么复杂平时,将数组名也就是数组首元素的地址交给指针p然后一个个去访问就行了

四、二维数组传参的本质

一维数组传参

数组名是首元素的地址

一维数组在传参的时候,其实传递的是首元素的地址。

函数的参数的可以写成数组,也可以写成指针

二维数组呢?

二维数组的数组名是如何理解的呢

其实二维数组的数组名也是数组首元素的地址

二维数组可以理解位一维数组的数组

二维数组的每一行可以看作是一个一维数组

所以二维数组其实是一个以为数组的数组

二维数组的首元素就是他的第一行

二维数组的数组名就是他第一行的地址

第一行是一个一维数组,传过去的就是第一行这个一维数组的地址

void print(int (*arr)[5], int r, int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d ", ((*(arr+i))+j));
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5} ,{2,3,4,5,6}, {3,4,5,6,7} };
	print(arr,3,5);//将arr数组的内容打印出来
	//arr是数组首元素的地址,是他第一行的地址
	return 0;
}

                   

我们传过去的是数组首元素的地址,也就是第一行的地址,所以我们访问的时候用i来访问每一行的地址,用j来访问每一行的每一个元素的地址   

在访问第一行的元素是就是arr[0][j]以此类推接下来的每一行的元素

arr[i]是第i行的数组名,数组名又表示数组首元素的地址,arr[i]表示是&arr[i][0]         

五、函数指针变量         

变量有地址 

数组也有地址

函数是否也有地址   

接下来让我们写几行来代码来验证一下

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	printf("%p\n", &Add);
	printf("%p\n", Add);
	return 0;
}

这时候我们发现数组也是有地址的         

我们写数组名和&数组名的时候发现两个地址是不一样的,但是我们对函数实行相同的操作的时候就会发现

&函数名和函数名都是函数的地址,没有区别      

那我们怎么将它存储起来呢

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf)(int, int) = &Add;
	return 0;
}

 
    int (*pf)(int, int)    首先它是一个指针,其次我们要将它和函数结合起来,函数的参数类型是(int,int),指向的函数的返回类型也是int所以就写成 int (*pf)(int, int)     
   

同样的去掉名字也是函数指针类型int (*)(int, int) 

那我们怎样去调用这个函数呢

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf)(int, int) = &Add;
	int ret = (*pf)(4, 5);
	printf("%d\n", ret);
	return 0;
}

   *pf是对这个函数取出来使用,然后我们再把值传过去就得到了对这个函数进行使用     

我们上文说到其实&函数名和函数名是等价的

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*pf)(int, int) = Add;
	int ret = pf(4, 5);
	printf("%d\n", ret);
	return 0;
}

    那我们其实是把Add这个函数名传给函数指针变量pf,那我们其实也能使用pf去充当这个函数名 ,只不过*pf可以帮助我们更好的去理解

六、typedef关键字

typedef是用来类型重命名的,可以将复杂的类型,简单化。

比如,你觉得unsigned int写起来不方便,如果能写成uint就方便多了

typedef unsigned int uint;
int main()
{
	unsigned int num;
	uint num2;
	return 0;
}

这样重命名就可以使用了

 接下来我们对整型指针变量进行重命名试一下

typedef int* pint_t;
int main()
{
	int* p;
		pint_t p2;
	return 0;
}

   接下来我们试一下对数组指针变量进行重命名一下

typedef int(*parr_t)[6];
int main()
{
	int arr[5] = { 0 };
	int(*p)[6] = &arr;
	parr_t p2 = &arr;
	return 0;

}

 这个有点特殊 int(*)[6] 是变量parr_t的类型,但是parr_t要放到括号里面这才代表

接下来函数指针变量     

int Add(int x, int y)
{
	return x + y;
}
typedef int(*pf_t)(int, int);
int main()
{
	int(*pf)(int, int) = Add;
	pf_t pf2 = Add;
	return 0;
}

这个其实跟上面的大差不差

ok,也是老样子六个标题用完了,写这个东西对我的知识梳理其实挺大的,希望也能帮助到你们,好像还有一点没有写完,哈哈                                                                                            

标签:arr,return,int,延续,地址,详解,数组,指针
From: https://blog.csdn.net/2401_84068287/article/details/139361438

相关文章

  • C++多线程原理详解
    学习C++多线程时,我有如下疑问:mutex的lock和unlock做了什么?mutex、lock_guard、unique_lock,它们之间的关系是什么?condition_variable中的wait做了什么?带着这些疑问,我查阅了一些资料,整理出本文。文章目录一、mutex二、lock_guard三、unique_lock四、condition......
  • 【包邮送书】Node-RED 物联网应用开发技术详解
    ......
  • JSP详解,看这一篇就够了(含示例)
    JSP(JavaServerPages)是Java技术的一部分,用于创建动态Web内容。JSP的主要功能是简化服务器端的Web开发,尤其是对于HTML、XML等页面内容的动态生成。一、JSP的基础概念什么是JSP:JSP是一种基于Java的技术,用于创建动态网页。它允许在HTML中嵌入Java代码,这些代码在服务器端执......
  • 旅行第五天【算法】双指针-----三数之和+四数之和
    文章目录一、题目二、算法原理三、编写代码四、题目五、算法原理六、编写代码一、题目链接:三数之和二、算法原理首先是解法一:暴力解法(其实有必要思考一下,不用把程序写出来,写伪代码就可以了,因为优化后算法的代码是建立在暴力解法的基础上的)三个指针,分别依次......
  • 关于css预处理器sass详解
    Sass(SyntacticallyAwesomeStylesheets)是一种强大的CSS预处理器,旨在简化CSS的编写并增强其功能。以下是对Sass的详细解释,包括其特点、功能、语法格式以及使用方式。1.Sass的特点扩展CSS功能:Sass在CSS的基础上增加了变量、嵌套、混合(mixins)、继承等高级功能,使得CSS的编......
  • 进程间通信(27000字超详解)
    ......
  • 深入解析力扣170题:两数之和 III - 数据结构设计(哈希表与双指针法详解及模拟面试问答)
    在本篇文章中,我们将详细解读力扣第170题“两数之和III-数据结构设计”。通过学习本篇文章,读者将掌握如何设计一个数据结构来支持两种操作,并了解相关的复杂度分析和模拟面试问答。每种方法都将配以详细的解释和ASCII图解,以便于理解。问题描述力扣第170题“两数之和III......
  • TorchServe详解和应用
    TorchServer是PyTorch的一个组件,它是一个轻量级的服务框架,用于部署和管理PyTorch模型,以便在生产环境中提供高效、可扩展的推理服务。TorchServer提供了RESTfulAPI,可以方便地与其他系统集成,支持模型热加载和热更新,确保模型的快速部署和更新。以下是TorchServer的一些关键特......
  • 【C/C++】--- 指针详解 2.0
    接下来进入指针的进阶部分,准备好大脑补充:(重点)数组名是数组首元素地址数组首元素地址和数组地址,值相同,但本质不同,区别在于二者的类型不相同比如数组intarr[10];数组首元素地址的类型:首先这是一个地址所以要用指针接收,(),然后是地址指向元素的类型为int,所以这个指针的......
  • 宝塔Linux面板-Docker管理(2024详解)
    上一篇文章《宝塔Linux可视化运维面板-详细教程2024》,详细介绍了宝塔Linux面板的详细安装和配置方法。本文详细介绍使用Linux面板管理服务器Docker环境。目录1、安装Docker1.1在线安装​编辑 1.2手动安装1.3运行状态1.4镜像加速2应用商店 3总览 4容器4.1......