1. 指针是什么?
指针是什么?
指针理解的2个要点:
1. 指针是内存中一个最小单元的编号,也就是地址
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量.
总结:指针就是地址,口语中说的指针通常指的是指针变量
- 我们可以这样理解:
指针变量
我们可以通过&(取地址操作符)取出变量的内存真实地址,把地址可以存放到一个变量中(说白了还是个变量),这个变量就是指针变量。
#include <stdio.h>
int main()
{
int a = 10;//在内存中开辟一块空间
int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。(int *p和int* p的写法都可以)
//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
中,p就是一个之指针变量。
return 0;
}
总结:指针是地址;指针变量,用来存放地址的变量。(存放在指针变量中的值都被当成地址处理)。
- 那指针的大小是多少呢?
经过计算机设计者仔细的计算和权衡发现,一个字节给一个对应的地址是比较合适的。
对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0);
那么32根地址线产生的地址就会是:
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
…
11111111 11111111 11111111 11111111
这里就有2的32次方个地址。
每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB ==
232/1024/1024MB==232/1024/1024/1024GB == 4GB) 4G的空间进行编址。
- 这里我们就明白:
在32位的机器上**,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储**,所以一个指针变量的大小就应该是4个字节。
那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
2. 指针和指针类型
我们都知道,变量有不同的类型,整形,浮点型等。那指针有没有类型呢?
准确的说:有的。
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
这里可以看到,指针的定义方式是: type + * 。
其实:
char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址。
2.1 那指针类型的意义是什么?
- 指针±整数
可以看到 :char* 类型指针加1 ,地址值变化为1(即1个字节);int* 类型指针加1 ,地址值变化为4(即4个字节);
总结:指针的类型决定了指针向前或者向后走一步有多大(距离)
- 指针的解引用
//演示实例
#include <stdio.h>
int main()
{
int n = 0x11223344; // 调试可以发现内存中的值为 44 33 22 11
int* pi = &n; // 取地址放入int* 类型的指针变量
char* pc = (char*)&n; // 强制类型转换为char*类型的指针变量
*pc = 0; //调试可以发现内存中的值改变为 00 33 22 11
*pi = 0; //调试可以发现内存中的值改变为 00 00 00 00
return 0;
}
总结:指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。
比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
3. 野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
3.1 野指针成因
-
- 指针未初始化
#include <stdio.h> int main() { int *p;//局部变量指针未初始化,默认为随机值 *p = 20; return 0; }
-
- 指针越界访问
#include <stdio.h> int main() { int arr[10] = {0}; int *p = arr; int i = 0; for(i=0; i<=11; i++) { //当指针指向的范围超出数组arr的范围时,p就是野指针 *(p++) = i; } return 0; }
-
- 指针指向的空间释放
Todo 后续博客阐述原因
- 指针指向的空间释放
3.2 如何规避野指针
-
- 指针初始化
-
- 小心指针越界
-
- 指针指向空间释放即使置NULL
-
- 避免返回局部变量的地址
#include <stdio.h> int* test() { int a = 10; return &a; // 返回局部变量地址 } int main() { // 返回局部变量地址,但是局部变量出了test方法的代码块({}范围内)即销毁。即该内存已经不属于我们的程序了 //所以p指针变量指向了不属于我们的内存,这是不允许的 int* p = test(); printf("%d\n", *p); return 0; }
-
- 指针使用之前检查有效性
#include <stdio.h> int main() { int *p = NULL; //.... int a = 10; p = &a; if(p != NULL) { *p = 20; } return 0; }
4. 指针运算
4.1 指针±整数
指针±整数的表示含义,指针前后移动一次(指针j加减1),调整的大小为sizeof(指针去掉一个*)
int main(){
float values[N_VALUES];
float* vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}
// 将values数组的元素全部置为0
return 0;
}
4.2 指针±指针
-
指针+指针
由于指针加指针的值是一个相对于原数组地址相差较大的数值,该数值很有可能超越了我们所定义的数组的右边界,这样获得的地址值将是一个“盲值”,虽然它确实存在,但我们不能对这个地址做任何处理,因为我们无法得知这个位置原先存储的是什么变量,所以我们认为这是个非法的。
-2、指针-指针
只有当两个指针都指向同一个数组中的元素时,才允许从一个指针减去连一个指针。两个指针相减的结果类型是ptrdiff_t,它是一种有符号整数类型。减法运算的值是两个指针在内存中的距离(该距离以间隔的单元格数为单位,而不是以字节为单位)