1.指针的认识
说到指针就不得不提起内存,计算机在计算上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,每个内存单元都有⼀个编号,有了这个内存单元的编号,CPU就可以快速找到⼀个内存空间。那么c语言把地址也叫指针。所以 地址==指针。而地址相当于我们现实生活中的门牌号,只有通过门牌号,这样才能让别人知道你住在哪里,也方便别人找到你。
2.指针变量和地址
指针变量就是存放的地址的地方。
int main()
{
int a = 10;
int* p = &a;
printf("%d", *p);
return 0;
}
上面就是取出a的地址,将其放在p上面, * 说明p是一个指针变量,存放地址的,而*之前的int则说明p所指向对象的类型。而在打印时出现了*p这里的*是指解引用,即指向a的值,所以*p==a。
3.指针的大小
1.在x86的环境下:
32个bit位(相当于32个地址总线),占4个字节空间大小,最多占2^32字节空间大小(一个地址线发出两个信号)
×64环境下:
(64位):64个bit位(相当于64个地址总线),占8个字节空间大小,最多占2^64字节空间大小(一个地址线发出两个信号)
注:指针大小与变量类型无关,变量类型只决定指针解引用访问的权限大小和向前向后走的长度
4.指针的相关使用与注意事项
1.指针运算
指针-指针=指针之间元素的个数(数字)
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = &arr;
int* pa = &arr;
int i = 0;
for (i = 0; i < sz; i++)
{
p++;
}
printf("%d\n", p - pa);
return 0;
}
这里就是通过最后一个指针减去第一个指针得到其中的元素个数。
2.const修饰指针(仅在语法上做限制)
const修饰指针可以分为两个情况,
1.const修饰的是*左边的值
int main()
{
int a = 10;
const int* p = &a;
*p = 0;
p++;
printf("%d\n", *p);
return 0;
}
这里修饰的是*p即限制的是p所指向的内容,但p自身不受限制,即自己还是可以改变的
int main()
{
int a = 10;
const int* p = &a;
printf("%p\n", p);
p++;
//printf("%d\n", *p);
printf("%p\n", p);
return 0;
}
通过上面代码可以了解到const修饰对p自身并没有限制。
2.const修饰*的右边,是 const放在*的右边限制p(p为a的地址,*p不受限制)
和上面是相反的,即自身不能改变,但可以改变指向对象的内容。
3.野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
成因:1.指针未初始化
字面意思指针未定义任何内容。
2. 指针越界访问
3.指针指向的空间释放
规避野指针:
可以在指针定义后,赋值NULL空值
也可写成:p=0或p='\0'
这两种形式和p=NULL是等价的
上面两行代码的含义是,指针变量p被赋值为空。虽然定义了一个指针变量,但是它并不指向任何存储空间。
3.assert断言:
assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报
错终⽌运⾏。这个宏常常被称为“断⾔”
表达式:assert(表达式)仅在Debug版本使用,release版本不行。
可用#define NeDEBUG取消断言
5.二级指针
指针变量也是变量,是变量就有地址,那么二级指针就是存放一级指针的地址的。
int main()
{
int a = 10;
int* p = &a;
int** pa = &p;
return 0;
}
这里int **pa 存放的是p的地址,这里有两个*,首先右边*说明pa是一个指针变量,左边的*则是说明pa所指向对象的类型是int*类型的,即int**pa是二级指针。
6.指针各种表现形式
1.指针数组
从名字来看(从左向右)以右边为主语,即这是一个数组,存放指针的数组,
形式:int *arr == int arr[]
下面是通过指针数组来模拟实现二维数组的效果
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;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
通过一个指针数组来存放三个一维数组,并通过其模拟实现二维数组。
2.字符指针
用于存放字符(数组)地址的变量成为字符指针(变量)
int main()
{
char * p = "abcdef";
return 0;
}
3.数组指针
和上面一个从左向右看,这是一个指针,指向数组的指针,即用于存放数组地址的变量为数组指针
4.函数指针
函数指针是指向函数的指针变量。 因此“函数指针”本身首先应指针变量,只不过该指针变量指向函数
函数名==&函数名==函数的地址
形式:int(*p)(类型)p为函数指针,指向类型为int(*)(类型)
函数指针的简单应用:
void Add(int x, int y)
{
return x + y;
}
int main()
{
int (*p)(int,int) = &Add;
printf("%d\n", p(3, 5));
return 0;
}
5.函数指针数组
函数指针数组也就是存放函数指针的数组。
#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 (*arr[])(int ,int )={Add,Sub,Mul,Div};
for(i=0;i<4;i++)
{
printf("%d\n",arr[i](6,2));
}
return 0;
}
7.小结
通过一篇简单文章简单阐述了指针的相关内容,指针的内容还有很多,需要我们不断探索,练习才能感受到其中到奥妙。