准备
博主:大大怪先森(记得关注哦!)
编程环境: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.野指针的成因
- 指针未初始化
int main()
{
int* p;//局部变量为初始化默认为随机值
*p = 20;
return 0;
}
- .指针越界访问
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.指针指向的空间内存未释放
详细讲解:请观看博主大大的另一篇文章《动态内存管理》
为了防止小可爱们下次走丢了,赶紧…
3.2.如何规避野指针
我们到底应该怎么规避这些问题?
各位小可爱在运用指针的时候一定要注意一下几点
- 指针初始化
- 小心指针越界
- 指针指向空间释放即使置NULL
- 避免返回局部变量的地址
- 指针使用之前检查有效性
所以说一个优秀的程序猿代码规范非常重要!!!!
在此给各位小可爱推荐一本书《高质量的c/c++编程》,小编不是在此打广告哦,
静下心好好看一看,我相信各位小可爱一定会受益匪浅!!!
三、指针数组
指针数组是指针还是数组?
答案:是数组。是存放指针的数组。
数组我们已经知道整形数组,字符数组。
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;
}
八、回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一
个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该
函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或
条件进行响应。
详细了解:请关于博主的下一篇文章《指针和数组笔试题》
结语
希望本篇文章能给各位带来帮助,如有不足还请指正!!!
码字不易,各位大大给个收藏点赞吧!!!