当前位置:首页 » 《休闲阅读》 » 正文

C语言指针篇(一篇文章让你不再晕“针”)!!!_wzd547191555的博客

25 人参与  2022年02月02日 08:53  分类 : 《休闲阅读》  评论

点击全文阅读


准备

博主:大大怪先森(记得关注哦!)
编程环境:vs2013


提示:基于C语言所写的关于指针的内容,小白也能看懂!!!

文章目录

  • 准备
  • 前言
  • 一、指针是什么?
  • 二、指针和指针类型
    • 1.指针类型
    • 2.指针类型的解引用
    • 3.野指针
      • 3.1.野指针的成因
      • 3.2.如何规避野指针
  • 三、指针数组
  • 四、数组指针
    • 1.数组指针的定义
    • 2.数组指针的使用
  • 五、指针的传参
    • 1.一维数组传参
    • 2.二维数组传参
    • 3.一级指针传参
    • 4.二级指针传参
  • 六、函数指针
  • 七、函数指针数组
  • 八、回调函数
  • 结语


前言

C语言指针的问题,是否是最让我们头晕眼花???
无限的套娃行为,成为我们惧怕c语言的首要障碍,各位小可爱不要担心。
博主大大已经为各位准备好了学习过程中可能遇到的问题!让我们一起揭开指针的全貌吧!!!!


提示:以下是本篇文章正文内容,内容原创,版权必究!!!</fO

一、指针是什么?

指针:
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元

除此之外大多数的程序猿的理解即是:指针 = 地址在这里插入图片描述
小结:
指针是用来存放地址的,地址是唯一标示一块地址空间的。
指针的大小在32位平台是4个字节,在64位平台是8个字节。

二、指针和指针类型

1.指针类型

我们都知道,变量有不同的类型,整形,浮点型等。那指针有没有类型呢?
将&num(变量num的地址)保存到p指针中,我们知道p就是一个指针变量,那它的类型是怎样的呢?

int main()
{
	int a = 0;
	char ch = 0;
	float p = 0.0f;
	int* ptr = &a; //整形指针
	char* ptr = &ch;//字符指针
	float* ptc = &p;//单精度浮点数指针
	//...
	return 0;
}

小结:我们不难看出存放字符变量的地址就是字符指针,存放整形变量的地址的就是整形指针

2.指针类型的解引用

int main()
{
	int a = 10;
	int*  pa = &a;
	char* pc = &a;
	
	printf("%p\n", pa);
	printf("%p\n", pc);

	printf("%p\n", pa+2);
	printf("%p\n", pc+2);
	//指针类型的意义2:
	//指针类型决定了,指针+-整数的时候的步长(指针+-整数的时候,跳过几个字节)
	//int* 指针 +1 跳过4个字节
	//char* 指针+1 跳过1个字节	
	//int a = 0x11223344;
	//char* pc = &a;//int*
	//*pc = 0;
	//int*pa = &a;
	//*pa = 0;
	//指针类型的意义1:
	//指针类型决定了指针解引用操作的时候,一次访问几个字节(访问内存的大小)
	//char* 指针解引用访问1个字节
	//int* 指针解引用访问4个字节
	return 0;
}

小结:指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

3.野指针

3.1.野指针的成因

  1. 指针未初始化
int main()
{
	int* p;//局部变量为初始化默认为随机值
	*p = 20;
	return 0;
}
  1. .指针越界访问
int main()
{
    int arr[10] = {0};
    int *p = arr;
    int i = 0;
    for(i=0; i<=11; i++)
   {
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
   }
    return 0; }

3.指针指向的空间内存未释放
详细讲解:请观看博主大大的另一篇文章《动态内存管理》
为了防止小可爱们下次走丢了,赶紧…
Alt

3.2.如何规避野指针

我们到底应该怎么规避这些问题?
各位小可爱在运用指针的时候一定要注意一下几点

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放即使置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性

所以说一个优秀的程序猿代码规范非常重要!!!!
在此给各位小可爱推荐一本书《高质量的c/c++编程》,小编不是在此打广告哦,
Alt
静下心好好看一看,我相信各位小可爱一定会受益匪浅!!!

三、指针数组

指针数组是指针还是数组?
答案:是数组。是存放指针的数组。
数组我们已经知道整形数组,字符数组。

在这里插入图片描述

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int* arr[3] = {&a, &b, &c};
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d\n", *(arr[i]));
	}
	//int* pa = &a;
	//int* pb = &b;
	//int* pc = &c;
	return 0;
}

四、数组指针

1.数组指针的定义

数组指针是指针?还是数组?
答案是:指针。
我们已经熟悉整形指针,浮点数指针,那么数组指针就是指向数组的指针

int* pa[10];
int (*pb)[10];
到底哪一个是数组指针呢?

答案是:int (*pb)[10]

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

2.数组指针的使用

代码如下(示例):

//void print1(int arr[], int sz)
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", arr[i]);
//	}
//}
//void print2(int* arr, int sz)
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", *(arr + i));
//	}
//}

//数组指针
//void print3(int (*parr)[10], int sz)//这是一个错误的示范
//{
//	int i = 0;
//	for (i = 0; i < sz; i++)
//	{
//		printf("%d ", parr[i]);//parr[i] == *(parr+i)
//	}
//}
void print4(int(*pa)[10], int sz)
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)
	{
		//printf("%d ", *(pa + i));//错误 
		//printf("%d ", pa[i]);//erro
		printf("%d ", (*pa)[i]);//*(pa +0)[i]  == pa[0][i] ==   *(*(pa) + i)
		printf("%d ", pa[0][i]);
		printf("%d ", *(*(pa)+i));	
	}
}
int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
		int sz = sizeof(arr) / sizeof(arr[0]);
		print4(&arr, sz);
		//print3(&arr, sz);
		//print2(arr, sz);
		//print1(arr, sz);//打印arr数组的内容
	return 0;
}

学习了数组指针你是否对数值指针有更加深刻的理解了~~~
各位下可爱再看看下面的代码的意思吧!

int arr[10];
int* arr2[10];
int (*pa)[10];
int* (*pa[10])(10);

在这里插入图片描述

五、指针的传参

1.一维数组传参

思考如下代码(示例):

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

1.× 2.√ 3.√ 4.× 5.√

2.二维数组传参

代码如下(示例):

void print2( int(*p)[5], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			//printf("%d ", *(*(p + i) + j));
			printf("%d ", p[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 };
	//二维数组传参
	//print1(arr, 3, 5);
	print2(arr, 3, 5);//arr 是数组名,数组名是首元素地址
	return 0;
}

小结:二维数组传参arr是相当于第一个行的数组名

在这里插入图片描述

其他接收参数:int* arr or int** arr or int arr[3][5]

3.一级指针传参

代码如下(示例):

#include <stdio.h>
void print(int *p, int sz) 
{
     int i = 0;
    for(i=0; i<sz; i++)
   {
    printf("%d\n", *(p+i));
   }
}
int main()
{
   int arr[10] = {1,2,3,4,5,6,7,8,9};
   int *p = arr;
   int sz = sizeof(arr)/sizeof(arr[0]);
   //一级指针p,传给函数
    print(p, sz);
   return 0; 
}

当一个函数的参数部分为一级指针的时候,函数能接收什么参数?

in* p or in** pa or int arr[10];

4.二级指针传参

void test(char **p) 
{}
int main()
{
    char c = 'b';
    char*pc = &c;
   char**ppc = &pc;
   char* arr[10];
   test(&pc);
   test(ppc);
   test(arr);//Ok?
   return 0; }

六、函数指针

代码如下(示例):

#include <stdio.h>
void test()
{
   printf("hehe\n");
}
int main()
{
   printf("%p\n", test);
   printf("%p\n", &test);
   return 0;
 }

输出结果:
在这里插入图片描述

思考:两者的地址相同,那如何吧地址存放在指针当中?

答案:void (*fun)(int,int)

这就是所谓的函数指针 ,fun是变量名,void (*)(int,int)则是变量的类型,表示fun指向的函数的两个参数分别都是int类型。

七、函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组,
比如:

int *arr[10];
数组的每个元素是int*

那么那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,
那函数指针的数组如何定义呢?

int (*pa)(int,int)
int (*pa[10])(int,int)
[]的优先级高于高于解引用的星号所以int (*pa[10])(int,int)的本质上是一个数组,而每一个数组的元素存放的都是一个函数的地址即函数指针,每个函数指针所指向的都是含有两个int类型的参数

小练习:使用函数指针数组实现的简易计算机

#include <stdio.h>
int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a*b;
}
int div(int a, int b)
{
	return a / b;
}
int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
	while (input)
	{
		printf("*************************\n");
		printf(" 1:add           2:sub \n");
		printf(" 3:mul           4:div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
		}
		else
			printf("输入有误\n");
		printf("ret = %d\n", ret);
	}
	return 0;
}

八、回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一
个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该
函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或
条件进行响应。
详细了解:请关于博主的下一篇文章《指针和数组笔试题》


结语

希望本篇文章能给各位带来帮助,如有不足还请指正!!!
码字不易,各位大大给个收藏点赞吧!!!

在这里插入图片描述


点击全文阅读


本文链接:http://zhangshiyu.com/post/34178.html

指针  数组  函数  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1