个人博客网址:https://ljsblog.com
指针(七)
指针
指针就是变量,用来存放内存单元的地址
存放在指针中的值都被当成地址处理
指针在32位平台大小是4个字节,指针在64位平台大小是8个字节
printf("%d\n",sizeof(char*));//4
printf("%d\n",sizeof(short*));//4
printf("%d\n",sizeof(int*));//4
printf("%d\n",sizeof(float*));//4
printf("%d\n",sizeof(double*));//4
指针类型
一.指针类型决定了指针进行解引用操作的时候,能访问空间的大小。
例:
int* p; *p能访问4个字节
char* p; *p能访问1个字节
double* p; *p能访问8个字节
二.指针类型决定了指针向前或者向后走一步的距离。
例:
int a=0;
int *pi=&a;
char *pc=&a;
printf("%p\n",pi);//010FFEC4
printf("%p\n",pi+1);//010FFEC8
printf("%p\n",pc);//010FFEC4
printf("%p\n",pc+1);//010FFEC5
int* p; p+1-->4
char* p; p+1-->1
double* p; p+1-->8
注:
- void* 类型的指针可以接受任意类型的地址
- void* 类型的指针不能进行解引用操作
- void* 类型的指针不能进行加减整数的操作
野指针
野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
野指针由来
一.局部变量没有初始化
//错误示例
#include<stdio.h>
int main()
{
int *p;//指针变量如果未初始化,其值是随机的
*p=20;
return 0;
}
二.指针越界访问
//错误示例
#include <stdio.h>
int main()
{
int arr[10]={0};
int i=0;
int* p=arr;
for(i=0;i<11;i++)
{
*(p+i)=i;//当指针指向超出arr范围时,p就是野指针
}
return 0;
}
三.指针指向的空间释放
//错误示例
#include <stdio.h>
int* Wild_Pointer()
{
int a=1;
return &a;
}
int main()
{
int *p=Wild_Pointer();//空间已经被释放了
*p=10;
return 0;
}
如何规避野指针
- 指针初始化
- 小心指针越界
- 指针指向空间释放即使置NULL(NULL和0的值都是一样的,但是为了目的和用途及容易识别的原因,NULL用于指针和对象,0用于数值)
- 指针使用之前检查有效性
指针的运算
指针加减整数
#include <stdio.h>
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9,10};
int *p=arr;
int i=0;
int sz=sizeof(arr)/sizeof(arr[0]);
for(i=0;i<sz;i++)
{
printf("a[%d]=%d\n",i,*(p+i));
}
return 0;
}
/*结果为
a[0]=1
a[1]=2
a[2]=3
a[3]=4
a[4]=5
a[5]=6
a[6]=7
a[7]=8
a[8]=9
a[9]=10
*/
指针减指针
指针减指针时,两个指针必须在同一个空间内,例如;在一个数组内
#include <stdio.h>
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9,10};
int *p=arr;
printf("%d\n",&arr[9]-&arr[0]);//9
return 0;
}
指针的关系运算
#include <stdio.h>
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int *p=arr;
for(p=&arr[10];p>&arr[0];)
{
*--p=0;
}
return 0;
}
字符指针
一般用法
#include <stdio.h>
int main()
{
char a='m';
char* p=&a;
*p='y';
printf("%c\n",a);//y
return 0;
}
另一种用法
#include <stdio.h>
int main()
{
char* p="myblog";
//这里是将常量字符串"myblog"首字符的地址放到了p中。
printf("%c\n",*p);
printf("%s\n",p);
return 0;
}
/*打印结果为
m
myblog
*/
指针数组
指针数组就是存放指针的数组。
例:
#include <stdio.h>
int main()
{
int arr1[]={1,2,3,4,5};
int arr2[]={6,5,4,6,7};
int arr3[]={5,6,7,4,8};
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;
}
/*打印结果
1 2 3 4 5
6 5 4 6 7
5 6 7 4 8
*/
数组指针
数组指针就是指针,指向数组的指针就是数组指针。
例如:
#include <stdio.h>
int main()
{
//int arr[10]={0};
//arr首元素地址
//&arr[0]首元素地址
//&arr整个数组的地址
int arr[5]={1,2,3,4,5};
int (*p)[5]=&arr;//p就是一个数组指针
return 0;
}
int* p1[10];//存放指针的数组
int (*p2)[10];//指向数组的指针
例如:
#include <stdio.h>
int main()
{
char* arr[5];//指针数组
char* (*pa)[5]=&arr;//数组指针
//*pa说明pa是个指针
//[5]说明pa指向的数组有五个元素
//char* 说明pa指向的数组的元素类型是char*
}
数组指针的使用
#include <stdio.h>
//参数是数组
void print1(int arr[3][5],int x,int y)
{
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");
}
}
//参数是指针
void print2(int (*pa)[5],int x,int y)
{
int i=0;
int j=0;
for(i=0;i<3;i++)
{
for(j=0;j<5;j++)
{
printf("%d ",*(*(pa+i)+j));
//printf("%d ",*(pa[i]+j));
//printf("%d ",(*(pa+i))[j]);
//printf("%d ",pa[i][j]);
// *(*(pa+i)+j) == *(pa[i]+j) == (*(pa+i)[j]) == pa[i][j]
}
printf("\n");
}
}
int main()
{
int arr[3][5]={{3,5,7,4,1},{5,6,7,6,4},{6,7,3,5,6}};
print1(arr,3,5);//arr数组名,数组名就是首元素地址
printf("\n");
print2(arr,3,5);
return 0;
}
/*
3 5 7 4 1
5 6 7 6 4
6 7 3 5 6
3 5 7 4 1
5 6 7 6 4
6 7 3 5 6
*/
int arr[5];
arr是一个5个元素的整型数组
int *parr1[10];
parr1是一个指针数组,10个元素,每个元素的类型是int*
int (*parr2)[10];
parr2是一个指向数组的指针,该数组有10个元素,元素的类型是int,parr2是数组指针
int (*parr3[5])[10];
parr3是一个数组,数组有5个元素,每个元素是一个数组指针,数组指针指向的数组有10个元素,元素类型为int
数组传参
一维数组传参
#include <stdio.h>
//传参方式一
void test(int arr[])
{}
//传参方式二
void test(int arr[10])
{}
//传参方式三
void test(int* arr)
{}
int main()
{
int arr[10]={0};
test(arr);
return 0;
}
#include <stdio.h>
//传参方式一
void test(int* arr[])
{}
//传参方式二
void test(int **arr)
{}
int main()
{
int* arr[10]={0};
test(arr);
return 0;
}
二维数组传参
//传参方式一
void test(int arr[3][5])
{}
//传参方式二
void test(int arr[][5])
//注:二维数组传参时,行可以省略,列不能省略
{}
//传参方式三
void test(int (*arr)[5])
{}
#include <stdio.h>
int main()
{
int arr[3][5]={0};
test(arr);
return 0;
}
指针传参
一级指针传参
当函数的参数为一级指针时,函数能接收什么参数?
#include <stdio.h>
void test(int *p)
{}
int main()
{
int a=0;
int* p=&a;
test(p);
test(&a);
return 0;
}
#include <stdio.h>
void test(char *p)
{}
int main()
{
char a='w';
char* p=&a;
test(p);
test(&a);
return 0;
}
二级指针传参
当函数的参数为二级指针时,函数能接收什么参数?
#include <stdio.h>
void test(int **p)
{}
int main()
{
int a=0;
int* p=&a;
int** pp=&p;
int* arr[10];
test(pp);
test(&p);
test(arr);
return 0;
}
#include <stdio.h>
void test(char **p)
{}
int main()
{
char a='w';
char* p=&a;
char** pp=&p;
char* arr[10];
test(pp);
test(&p);
test(arr);
return 0;
}
函数指针
指向函数的指针就是函数指针。
&函数名和函数名都是函数的地址
#include <stdio.h>
void test()
{}
int main()
{
printf("%p\n",&test);
printf("%p\n",test);
//打印结果一样
return 0;
}
#include <stdio.h>
int add(int x,int y)
{
return x+y;
}
int main()
{
int (*pf)(int,int)=add;
printf("%d\n",(*pf)(10,20));//30
printf("%d\n",pf(10,20));//30
//pf前面的 * 没多大意义,加不加都一样
return 0;
}
#include <stdio.h>
void print(char* str)
{
printf("%s\n",str);//myblog
}
int main()
{
void (*p)(char*)=print;
(*p)("myblog");
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;
}
int main()
{
int i=0;
int (*pfa[4])(int,int)={add,sub,mul,div};
for(i=0;i<4;i++)
{
printf("%d\n",pfa[i](6,3));
}
return 0;
}
/*
9
3
18
2
*/
指向函数指针数组的指针
#include <stdio.h>
int main()
{
int (*pfa[4])(int,int);//pfa是一个函数指针数组
int (*(*ppfa)[4])(int,int)=&pfa;
//ppfa是一个数组指针,指针指向的数组有4个元素
//每个元素的类型是一个函数指针int(*)(int,int)
//ppfa是一个指向函数指针数组的指针
return 0;
}
回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
例:
#include <stdio.h>
void print(char* str)
{
printf("haha:%s\n",str);
}
void test(void (*p)(char*))
{
p("myblog");
}
int main()
{
test(print);
return 0;
}
/*
haha:myblog
*/