本文介绍了野指针及其防范措施
,字符指针变量
,二级指针及指针数组
,数组指针变量及其传参本质
,函数指针变量
,函数指针数组
定义:野指针就是指向的位置是不可知的(随机的,不正确的,没有明确限制的)
#int main()
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p=10;
return 0;
}
#include
int main()
{
int arr[10]={ 0 };
int* p = arr;
for(int i=0;i<11;i++)
{
//当i=10的时候,指针p超出数组arr的范围,p就成了野指针
*(p++) = i;
}
return 0;
}
#include
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p=test();//这里返回的空间已经被释放,得到的是野指针
return 0;
}
int main()
{
int a = 10;
int* pa = &a;
int* pb = NULL;
return 0;
}
注意指针是否越界访问
⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。
在使用完指针后,养成将其置空的习惯
#include
int main()
{
int a = 1;
int* pa = &a;
printf("%d",*pa);
//使用过程....
pa = NULL;
return 0;
}
#include
#include
int main()
{
int p1 = NULL;
int a = 10;
int* p2 = &a;
assert(p2);//p2不为NULL,程序继续运行
assert(p1);//p1为NULL,assert()进行报错,并在屏幕中打印出该错误信息并标明多少行
printf("%p %p",p1,p2);
return 0;
}
#include
void assert(int expression);
assert() 宏接受⼀个表达式作为参数,若这个参数为真(结果为true),assert()不会产生任何作用
程序继续运行;反之,这个参数为假(结果为false),assert()就会报错,在标准错误流stderr中写入一条错误信息,显示没有通过的表达式,包括这个表达式的文件名与行号。
#define NDEBUG
若我们已确保程序没有问题,便可以在#include
#define NDEBUG
#include
若在程序中再次出现问题,我们也可以将#define NDEBUG指令注释掉,再次编译调试
在指针的类型中,字符指针为char*
一般使用:
#include
int main()
{
char ch = 'a';
char* pch = &ch;
printf("%c",*pch);
return 0;
}
另一种使用方式:
#include
int main()
{
char* pstr = "hello,world";
printf("%s",pstr);
return 0;
}
上述代码char* pstr=“hello,world”;指的是常量字符串hello,world的首地址放至pstr中,而不是将hello,world放到字符指针pstr中。
《剑指offer》中收录了一道和字符串相关的笔试题,如下:
#include
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char *str3 = "hello bit.";
const char *str4 = "hello bit.";
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,bit
放到字符串数组str1,str2,所以这里str1与str2的地址是不同的hello,bit
的首地址,所以str3==str4成立。指针变量也是变量,是变量就有地址,因此也存在二级指针。
#include
int main()
{
int a = 10;
int* pa = &a;
int** ppa = &pa;
return 0;
}
int a= 10;
*ppa = &a;//pa = &a;
**ppa = 20;
//*pa = 20;
//a= 20;
顾名思义,指针数组的每个元素都是用来存放地址的,所以,指针数组的每一个元素都是地址,其中每个地址也都可以指向一块区域,例如:
#include
int main()
{
int arr1[]={1,2,3,4,5};
int arr2[]={6,7,8,9,10};
int arr3[]={11,12,13,14,15};
int *parr[3]={arr1,arr2,arr3};
int 1=0 , j=0;
int col=sizeof(parr)/sizeof(parr[0]);
int row=sizeof(arr1)/sizeof(arr1[0]);
for(i=0;i<col;i++)
{
for(j=0;j<row;j++)
{
printf("%d",parr[i][j]);
}
printf("\n");
}
return 0;
}
parr[i]是parr数组的元素,parr[i]找到的数组元素指向了一位数组的元素,所以parr[i][j]就可以表示在parr[i]中arr(i)[j]的元素
注意:上述代码只是模拟出二维数组的效果,实际上并不是二维数组,因为每一行并非是连续的。
数组指针变量是存放数组的地址是指向数组的指针变量
int (*p)[10];
p先与*结合,说明p是一个指针变量,然后指着指向一个大小为10的整形数组,所以,p是一个指针,指向一个数组,叫做数组指针。
注意:[]的优先级要高于 * 号,所以,要加上括号来保证*与p最先结合。
#include
void Print_arr(int arr[3][5],int x,int y)
{
for(int i=0;i<x;i++)
{
for(int j=0;j<y;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(arr,3,5);
return 0;
}
根据数组名就是这个数组的首元素地址这个规则,如果我们把3个一维数组arr1,arr2,arr3看作3个元素,在将其放置到另一个一维数组nums中,那么nums的首元素就是arr1这个一维数组。
根据上面的例子,第一行的数组类型是int [5]
,所以第一行的地址类型就是数组指针类型int (*)[5]
。
所以,这就意味着二维数组的传参本质也就是传递了地址,传递的是这一个一维数组的地址,那么我们的形式参数就可以使用指针来接收,如下:
#include
void Print_arr(int (*pnums)[5],int x,int y)
{
for(int i=0;i<x;i++)
{
for(int j=0;j<y;j++)
{
printf("%d ",pnums[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(arr,3,5);
return 0;
}
我们先观察下面的一个代码:
#include
void test()
{
;
}
int main()
{
printf(" test = %p\n",test);
printf("&test = %p\n",&test);
return 0;
}
运行结果如下:
test = 00007FF7785C13F7
&test = 00007FF7785C13F7
由此,我们可以得出函数也有地址,而函数名就是函数的地址,当然也可以使用&函数名
来表现。
因此,我们也可以使用指针指向函数的地址,也就是创建函数指针变量。
函数指针变量用来存放函数的地址,通过地址调用函数
#include
#include
void print_1()
{
printf("test_1 no problem\n");
}
int cmp_max(int x, int y)
{
if (x < y)
x = y;
return x;
}
char* my_strcat(char* dest, char* src)
{
assert(dest);
if (src == NULL)
return dest;
char* ret = dest;
while (*dest)
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
void(*pv)(void) = print_1;
int(*pi)(int x, int y) = cmp_max;
char* (*pc)(char* x, char* y) = my_strcat;
(*pv)();
printf("cmp_max(4,2)=%d\n", (*pi)(4, 2));
char dest[50] = "my_strcat ";
char src[20] = "no problem\n ";
printf("%s", (*pc)(dest, src));
return 0;
}
int (*) (int x,int y)//cmp_max函数指针变量
void (*) (void)//print_1函数指针变量
char* (*) (char* a,char* b)//my_strcat函数指针变量
既然有函数指针变量,那么我们也可以把函数指针当作元素存放到指针数组中,这就叫做函数指针数组。
定义形式:(返回类型) (*(函数名)[存放个数]) (参数类型)
例如:
int (*parr[5]) (int x,int y)
//这里的函数指针数组可以存放五个函数指针,函数指针类型为int (*) (int x,int y)
例如:计算器的简易实现
#define _CRT_SECURE_NO_WARNINGS 1
#include
void menu()
{
printf("+-------------------+\n");
printf("| 1.add(x,y) |\n");
printf("| 2.sub(x,y) |\n");
printf("| 3.mul(x,y) |\n");
printf("| 4.div(x,y) |\n");
printf("| 0.退出程序 |\n");
printf("+-------------------+\n");
}
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()
{
menu();
int input = 0;
int (*pi[5])(int x, int y) = { 0,add,sub,mul,div };
while (printf("输入你的选择:"), scanf("%d", &input), input)
{
if (input > 0 && input < 5)
{
int a = 0, b = 0;
printf("请输入你要计算的数:");
scanf("%d %d", &a, &b);
printf("%d\n", (*pi[input])(a, b));
//利用(*pi[input])(a, b)函数指针调用函数,从而返回计算结果
}
}
printf("成功退出\n");
return 0;
}
这里通过改变input,来调用函数指针,进而使用函数来进行计算。
好了,以上就是本期的全部内容,喜欢请多多关注吧!!!