广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >零基础详解C语言指针进阶
  • 414
分享到

零基础详解C语言指针进阶

2024-04-02 19:04:59 414人浏览 安东尼
摘要

目录前言1.字符指针例题 12.指针数组例题 23.数组指针3.1数组指针的定义3.2 &数组名与数组名3.3 数组指针的使用4.数组与指针在函数里的传参4.1 一维数组的传

前言

这是指针的进阶,如果想入门指针的朋友可以关注我的另外一篇文章—C语言 指针零基础讲解

坚持看完,一定会有很大的收获~~

那接下来—起航

1.字符指针

我们目前知道整形指针,浮点型指针,字符指针跟他俩类型

字符指针—顾名思义就是指针,一个char*类型的指针 

?在讲解字符指针前,我先提一下怎么连续创建多个指针

连续创建多个指针的方法:

你可能会想到用:


int a ,b;
 
int* a,b;

或者


#define PINT int*
 
int main(){
 
int a ,b;
 
p a,b;
 
}

但是实际上这样创建的结果是:

创建一个整形指针int*a与整形变量int b.

创建指针的正确打开方式:


//方案一,直接定义两次
int a,b;
int*a; int*b;
//方案二,采用typedef重定义
typedef int* pint
{
	int a,b;
pint pa, pb;//此时就是定义int *pa与int *pb都是指针变量
return 0;
}

下面给出一个简单的代码:


char ch='w';
char* p=ch;
char* p="abcde";

定义一个char类型的变量ch,将ch地址放在指针变量p中

此时p存放的就是字符w的地址

这个很容易理解,那么char* p="abcde"是什么意思呢

实际上这次的p存放的是字符串abcde的首元素的地址,也就是a的地址

有了首地址,就很容易找到后续元素的地址

例题 1


    判断下面代码是否相等
    char arr1[] = "abcdef";
    char arr2[] = "abcdef";
 
    const char* str1 = "abcdef";
    const char* str2 = "abcdef";

#include<stdio.h>//证明相等关系
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcdef";
	const char* str1 = "abcdef";
	const char* str2 = "abcdef";
	if (arr1 == arr2)
		printf("arr1==arr2\n");
	else
		printf("arr1!=arr2\n");
	if (str1 == str2)
		printf("str1==str2\n");
	else
		printf("str1!=str2\n");
 
	return 0;
}

为什么arr1!=arr2; str1==str2

首先创建数组,arr1与arr2,先向系统申请两个不同的空间,然后将abcdef放入两个不同的空间里,所以这两个空间的地址当然就不相同

其次是str1与str2的abcdef只存储在只读存储区(这跟const无关,const起到强调的作用,实际上有无const的意思是相同的),就是不能更改其中的元素;

这时候两字符串的内容相同,系统就不会在浪费多余的空间去储存两个不同的内容~

所以就形成str1==str2,实际上这两个变量储存的都是a的地址

2.指针数组

在这之前我们知道整形数组,浮点型数组等

意思就是储存整形与浮点型数子的数组

指针数组就是存放指针的数组,实际上还是数组,里面存放着不同类型的指针


int*arr[5];//整形指针数组
char*arr[5];//一级字符指针数组
char**arr[5];//二级字符指针数组

知道定义,那如何使用呢

例题 2

多组打印字符串


#include<stdio.h>
int main()//指针数组
{
	char* arr[] = { "abcdef","ghi" ,"jklmn" };
	//打印
	int i = 0;
	int sz = sizeof (arr) / sizeof (arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%s\n",arr[i]);
	}
	return 0;
}

其中的    char* arr[] = { "abcdef","ghi" ,"jklmn" }就是指针数组,存放的就是char类型的指针,

他的作用就相当于:


char arr1[] = "abcdef";
char arr2[] = "ghi";
char arr3[] = "jklmn";

打印的结果就是:

✨✨打印整形的数组


#include<stdio.h>
int main()
{
	//打印整形数
	int arr1[] = { 1, 2, 3, 4, 5};
	int arr2[] = { 2, 3, 4, 5, 6};
	int arr3[] = { 3, 4, 5, 6, 7};
	int* arr[] = { arr1,arr2,arr3 };
	int i = 0; int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

3.数组指针

3.1数组指针的定义

有了指针数组的概念,相信很多人就知道数组指针的概念

跟你们想的一样

数组指针—就是指针,什么指针呢,存放数组的指针


int *pint;//能够指向整形数据的指针
char *p;//能够指向字符数据的指针
char (*p)[10];能够指向数组的指针

它的类型包括int(*)[],  char(*)[]

解读一下,其中的(*)就代表是一个指针[]就代表是一个数组的指针,char或者int就代表数组中的数据是啥类型的元素

?那么int * p1[10]与 int (*) p2[10]

其中的p1 p2是什么意思呢

前者是一个数组(指针数组)变量

后者是一个指针(数组指针)变量

3.2 &数组名与数组名

我们知道数组名就是首元素的地址(数组名直接与sizeof相连与&数组名除外)

那么&数组名是什么意思呢

实际上&数组名就是整个数组的地址


#include<stdio.h>
int main()
{
	int arr[20] = { 0 };
	printf("%p\n", arr);
	printf("%p\n", arr+1);
	printf("%p\n", &arr);
	printf("%p\n", &arr+1);
	return 0;
}

有打印的结果中很容易看出

arr 与 arr+1隔四个字节,也就是一个整形的大小

而&arr与&arr+1之间隔八十个字节,就是二十个整形的大小

?接下来在看一个代码


int *p[10];
int *(*pp)[10];

此时的int *p[10],p就是指针数组变量

int *(*pp)[10]这句的意思就是定义一个指针变量,什么指针?数组指针,而这个数组里的元素都是int*,有十个int*类型的数据。这就是数组指针~~

故arr在一般情况下都是这个数组首元素的地址,&arr就是整个数组的地址

3.3 数组指针的使用

说完数组指针的定义和使用规则,下面讲解数组指针的使用

给定三个数组,分别打印出他们的值


#include<stdio.h>
print(int (*p)[5],int r,int c)//此时的int (*p)[5]就是一个数组指针
{
	int i = 0; int j = 0;
	for(i=0;i<r;i++)
	{
		for(j=0;j<c;j++)
		{
			printf("%d", *(*(p+i) + j)); //*(p+i)就相当于拿到第一行的地址
		}
	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);
	return 0;
}

打印的结果:

这道题运用了一个二维数组

在这里我将二维数组看作是一维数组,也就是把一行当作一个元素

所以此数字有三个元素

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

 当然一维数组里也可以用到数组指针的思想,但实际上也不需要,这样反而变得麻烦,反而在二维数组中可以很好的利用。

那下面来总结一下:


int *arr;
int *parr1[10];
int (*parr2)[10];
int(*parr3[10])[10]

int *arr

是一个指针,类型是int*的指针;

int *parr1[10]

是一个数组,数组的类型是int *[],且是一个指针数组

int (*parr2)[10]

是一个指针,指针的类型是int(*)[],且是一个指针数组,数组中有十个整形元素

int(*parr3[10])[10]

是一个数组,此时把parr3去掉就得到nt(*)[],这里就是一个数组指针,所以这是一个存有十个数组指针的数组。

4.数组与指针在函数里的传参

函数传参的前提就是形参与实参是对应的关系,也就是说实参是什么类型,那么形参也是相应的类型,比如说实参是数组,那形参就是数组,参数是指针,那么形参也是指针。

4.1 一维数组的传参


#include<stdio.h>
test(arr1[])
{}
test(arr1[10])//上面两种方法,实参是数组,用数组来接收,因为在函数中本身不创建空间,所以无论【】{}            //中的值为多少,都能达到目的~
 
test(*arr1)
{}          //前面我提到数组名就是首元素的地址,此时就把实参中的数组名当作首元素的地址,此时用指 
            //针来接收
 
test(*arr2[10])
{}          //这里也跟第一二两个相同,也是数组传参,数组来接收
 
test(**arr2)
{}          //这里采用二级指针来接收,实参是一级指针,那么一级指针的指针就可以用二级指针来接收
int main()
{
int arr1[10]={0};
int* arr2[10]={0};
test(arr1);
test(arr2);
return 0;
}

4.2 二维数组的传参


#include<stdio.h>
//通过数组
test(int arr[10][10])      //二级指针传参时以数组的形式,【】中的行可以省略,但是列不能省略
{}                         //列可以让系统知道一行有多少的元素,从而分配多少的空间,便于知道每一 
                           //个元素的地址,故test(int arr[10][10])与test(int arr[][10])是可以 
                           //进行传参的,但是test(int arr[][])显然是不可以的。
test(int arr[][])
{}
test(int arr[][10])
{}
//通过指针
test(int *arr)
{}
test(int (*arr)[10])      //我们知道实参传的是首元素的地址,这里的首元素的地址就是第一行元素的 
{}                        //地址,也就是数组的地址,所以需要数组指针来接收
                          //故需要test(int (*arr)[10])来接收,而一级指针与二级指针显然是不可的 
 
test(int** arr)
{}
int main()//二级指针传参
{
	int arr[10][10] = { 0 };
	test(arr);
	return 0;
}

4.3 一级指针的传参


#include<stdio.h>
test(int *p)
{}
int main()
{
	int a = 10;
	int* par = &a;
	int arr[10];
 
	test(par);
	test(arr);
	test(*a);      //当一级指针传参时,形参是指针,那么实参就可以用这三种方式传参
 
	return 0;
}

4.4 二级指针的传参


#include<stdio.h>     //当二级指针传参时,形参是二级指针,那实参有哪些方式呢
test(int** p)
{}
int main()
{
	//二级指针传参
	char a = 'w';
	char* p = &a;
	char** pp = &p;
	char* arr[10] = { 0 };
 
	test(&p);        //通过一级指针p的取地址
	test(pp);        //通过二级指针传参,用二级指针来接收
	test(arr);       //通过char*()类型指针数组名来传递
 
	return 0;
}

5.函数指针

数组指针就是数组的地址

那么函数指针也是存放函数地址

?那问题是函数有指针吗


#include<stdio.h>
int Add(int x,int y)
{
	int sum = 0;
	sum = x + y;
}
int main()
{
	//证明函数有地址
	int a = 0;
	int b = 0;
	printf("%p", &Add);
    printf("%p\n", Add);
//定义一个函数指针变量
    int (*pf)(int,int)=&Add;  //此时的pf就是函数的指针变量,用pf就可以调用函数
	return 0;                 //括号中是参数的类型
}

这段代码证明,函数也有地址,而且取地址加函数名与函数名的作用是相同的,既然函数有地址就可以通过指针调用这个函数

调用这个函数:


#include<stdio.h>
int  Add(int x,int y)
{
	int sum = 0;
	sum = x + y;
	return sum;
}
int main()
{
	int (*pf)(int,int) = &Add;
	int ret1=(*pf)(2, 3); //括号别忘了
	int ret2 = (pf)(2, 3);//这里的括号可以省略
	printf("%d\n", ret1);
	printf("%d\n", ret2);
 
	return 0;
}

打印的结果:

可以看到,主函数中的pf与*pf的使用效果是一样的,打印的结果也是一样的,所以是否有*都可以达到相同的目的,但是*代表着解引用,容易理解~

下面给出了一个有趣的代码:

void(* signal (int,void (*) (int)) )(int)

这段代码的意思是什么呢,是不是看着有点晕

实际上把signal (int,void (*) (int))提出来剩下的就是void(*)(int),其中的signal是函数声明。

首先这是一个函数指针,有两个参数,一个参数是int,还有一个是函数指针,其返回值就是void(*)(int),也就是一个函数指针。

这个函数可以简化一下,也就是把void(*)(int)重新定义为一个新的变量

你可能认为是typedef void(*)(int)  pfun_t

这样的确好理解一些,毕竟跟我们所学习的结构体是一样的

但是真正的结果是typedef void(*pfun_t)(int) ,其中的pfun_t放在中间,我这样学也起到了强调的作用。

void(* signal (int,void (*) (int)) )(int)简化的结果是pfun-t signal(int,void (*) (int))

pfun-t是类型名,并不是类型名

6. 函数指针数组

前面我们学了整形指针数组,字符指针数组,和他们相同,函数指针数组也是一个数组,只不过数组里的元素是整形指针。

函数指针数组可以一次性实现多个函数的调用


#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x * y;
}
int Div(int x, int y)
{
	return x / y;
}
int main()
{ 
    //int (*pf1)(int, int)=&Add;
    //int (*pf2)(int, int)=&Sub;
    //int (*pf3)(int, int)=&Mul;
    //int (*pf4)(int, int)=&Div;
	int (*pfAdd[4])(int, int) = { &Add ,&Sub,&Mul,&Div };//数组指针可以很好的实现多次的定义
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		printf("%d\n", *(pfAdd[i])(8, 4));
	}
	return 0;
}

 通过简单的数组的调用,就可以实现加减乘除的运算,那函数指针数组有什么用呢

既然函数指针数组可以同时实现加减乘除,那当然可以实现一个计算器

用函数指针数组实现一个计算器


 
#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x * y;
}
int Div(int x, int y)
{
	return x / y;
}
menu()
{
	printf("*****************************************\n");
	printf("********  1.Add     2.Sub     ***********\n");
	printf("********  3.Mul     4.Div     ***********\n");
	printf("********  0.exit              ***********\n");
	printf("*****************************************\n");
 
}
int main()//用函数指针来实现一个计算器
{
	int x = 0;
	int y = 0;
	int input = 0;
	int ret = 0;
	int (*pfArr[5])(int, int) = { 0, &Add ,& Sub,& Mul,& Div };
	do
	{
		menu();
		printf("请选择:>");
		scanf_s("%d", &input);
		if (input == 0)
		{
			printf("计算机以关闭");
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入x与y:>");
			scanf_s("%d %d", &x, &y);
			ret = pfArr[input](x, y);
			printf("%d\n", ret);
		}
		else
			printf("选择错误");
	} while (input);
	return 0;
}

7.回调函数

??回调函数就是通过函数指针调用的函数,如果把一个函数的指针(地址)当作参数,传给另一个函数,当这个函数调用所指的函数时,我们就说这是回调函数。

下面来用回调函数来实现上一次用函数指针数组实现的计算器


#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x * y;
}
int Div(int x, int y)
{
	return x / y;
}
menu()
{
	printf("*****************************************\n");
	printf("********  1.Add     2.Sub     ***********\n");
	printf("********  3.Mul     4.Div     ***********\n");
	printf("********  0.exit              ***********\n");
	printf("*****************************************\n");
}
void calc(int (*pf)(int, int))
{
	int x = 0; int y = 0;
	printf("请输入两个数:>");
	scanf_s("%d %d", &x, &y);
	int ret = pf( x, y);
	printf("%d\n", ret);
}
int main()
{ 
	int input = 0;
	do
	{
		printf("请选择:\n");
		menu();
 		scanf_s("%d", &input);
		switch(input)
		{
		case 1:
				calc(Add);
				break;      
		case 2:
				calc(Sub);
				break;
		case 3:
				calc(Mul);
				break;
		case 4:
				calc(Div);
				break;
		case 0:
			printf("退出计算器");
			break;
		default:
			printf("选择错误");
			break;
		}
	} while (input);
	return 0;
}

回调函数很好的省略了case内部的重复代码过程,此时的加减乘除函数的指针当作参数传入calc函数里,这就是回调函数。

结语:这一章,c语言的指针相关的知识就结束了

到此这篇关于零基础详解C语言指针进阶的文章就介绍到这了,更多相关C语言 指针内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 零基础详解C语言指针进阶

本文链接: https://www.lsjlt.com/news/138594.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 零基础详解C语言指针进阶
    目录前言1.字符指针例题 12.指针数组例题 23.数组指针3.1数组指针的定义3.2 &数组名与数组名3.3 数组指针的使用4.数组与指针在函数里的传参4.1 一维数组的传...
    99+
    2022-11-13
  • c语言 指针零基础讲解
    1.指针是什么(可能有点难理解) 指针的是啥? 指针实际上就是地址,地址就是系统给定的编号,编号就是一个个内存单元。 在某种情况来说指针=地址=编号=内存单元。 指针就是地址,顾名思...
    99+
    2022-11-13
  • C语言指针基础详解
    目录1.1:概述1.1.1:内存1.1.2:内存1.1.3:指针和指针变量1.2:指针基础知识1.2.1:指针变量的定义和使用1.2.2:通过指针间接修改变量的值1.2.3:指针的大...
    99+
    2022-11-12
  • C语言零基础讲解指针和数组
    目录一、指针和数组分析-上1.数组的本质2.指针的运算3.指针的比较4.小结二、指针与数组分析-下 1.数组的访问方式2.下标形式 VS 指针形式3.a 和 &a ...
    99+
    2022-11-13
  • C语言基础指针详解教程
    目录1.1:概述1.1.1:内存1.1.2:内存1.1.3:指针和指针变量1.2:指针基础知识1.2.1:指针变量的定义和使用1.2.2:通过指针间接修改变量的值1.2.3:指针的大...
    99+
    2022-11-12
  • C语言 指针数组进阶详解
    目录指针与数组中的sizeof与strlensizeofstrlen数组名1、一维数组整型数组字符数组指针数组2、二维数组指针笔试题 笔试题1笔试题2笔试题3笔试题4笔试题...
    99+
    2022-11-13
  • 详解C语言初阶基础
    目录1.什么是c语言2.初始化:3.变量和常量4.作用域与生命周期简单阐述:作用域:生命周期(存在与消失):作用域与生命周期的区别作用域:生命周期:5.    &...
    99+
    2022-11-12
  • C语言进阶:指针的进阶(1)
    目录指针进阶字符指针字符指针的作用字符指针的特点指针数组指针数组的定义指针数组的使用总结指针进阶 我们在初阶时就已经接触过指针,了解了指针的相关内容,有: 指针定义:指针变量,用于...
    99+
    2022-11-12
  • C语言进阶:指针的进阶(2)
    目录数组指针数组指针的定义&数组名和数组名数组指针的使用反面用例正面用例Example类型辨别方法总结数组指针 由前面的例子,不难得出,数组指针是指向数组的指针,是指针而非...
    99+
    2022-11-12
  • C语言进阶:指针的进阶(3)
    目录数组传参和指针传参一维数组传参二维数组传参一级指针传参二级指针传参总结数组传参和指针传参 实践之中不免会碰到数组和指针作函数参数而如何设计形参的问题。 一维数组传参 一维数...
    99+
    2022-11-12
  • C语言进阶:指针的进阶(4)
    目录函数指针函数指针的定义函数指针的类型函数指针的使用Example总结函数指针 函数指针的定义 整型指针存放整型的地址;数组指针存放数组的地址;那么类比可得,函数指针存放函数的地...
    99+
    2022-11-12
  • C语言进阶:指针的进阶(5)
    目录函数指针数组函数指针数组的定义函数指针数组的使用转移表回调函数指向函数指针数组的指针总结函数指针数组 //整型数组 - 存放整型变量 int arr[10]; //字符数组 ...
    99+
    2022-11-12
  • c语言中指针零基础知识点有哪些
    小编给大家分享一下c语言中指针零基础知识点有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.指针是什么(可能有点难理解)指针的是啥?指针实际上就是地址,地址...
    99+
    2023-06-29
  • C语言中的初阶指针详解
    目录1.指针是什么2.指针和指针类型3.野指针3.1野指针成因3.2如何规避野指针4.指针的运算4.1指针±整数4.2指针-指针4.3指针的关系运算5.指针和数组6.二级指针7.指针...
    99+
    2022-11-12
  • C语言进阶教程之函数指针详解
    目录一、函数指针1.概念1.2函数指针的使用方法1.3练习巩固1.4小结一下二、阅读两段有趣的代码1.( *(void( *)( ))0 )( )2.void (* signal(i...
    99+
    2022-11-13
  • 详解C语言初阶基础(2)
    目录1.选择语句(if)2.循环while循环for循环do-while循环补充总结1.选择语句(if) 我们先不讲switch,后面会补充。先来对简单地if进行了解。 我们已经知道...
    99+
    2022-11-12
  • Go语言基础学习之指针详解
    目录1. 什么是指针2. 指针地址 & 指针类型3. 指针取值4. 空指针5. make6. new7. make 和 new 的区别8. 问题今天来说说 Go 语言基础中的...
    99+
    2022-12-30
    Go语言指针使用 Go语言指针 Go 指针
  • C语言进阶学习之指针
    目录1.指针概念回顾2.字符指针3.数组指针和指针数组3.1数组指针的含义3.2&数组名vs数组名3.3数组指针4.数组传参和指针传参4.1一维数组传参4.2二维数组传参4....
    99+
    2022-11-12
  • C语言指针详解
    目录前言:复杂类型说明一、细说指针1.指针的类型2.指针所指向的类型3.指针的值----或者叫指针所指向的内存区或地址4 指针本身所占据的内存区二、指针的算术运算三、运算符&...
    99+
    2022-11-12
  • C语言指针详解之野指针
    目录指针是什么?怎么表示?什么是指针变量?指针类型又是什么?指针类型存在的意义野指针是什么?野指针产生的原因一、 指针未初始化二、 指针越界访问如何避免野指针(野狗)的出现呢?指针运...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作