1. 指针是什么
- 指针是内存中一个最小单元(1个字节)的编号,也就是地址
- 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量(存放在指针变量中的值都被当成地址处理)
总结: 指针就是地址,口语中说的指针通常指的是指针变量
代码语言:javascript复制#include <stdio.h>
int main()
{
int a = 100;
int * pa = &a;//pa是专门用来存放地址(指针)的,这里的pa就被称为指针变量
//指针变量在32位平台下是4个字节
//指针变量在64位平台下是8个字节
//int arr[10];
//printf("%p", &a);
return 0;
}
注:
- 经过仔细的计算和权衡,我们发现一个字节给一个对应的地址是比较合适的。
- 对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电频(高电压)和低电频(低电压),就是(1或者0),那么32根地址线会产生2的32次方个地址,每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空间进行编址,64位机器同理。
- 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
- 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
#include <stdio.h>
int main()
{
printf("%dn", sizeof(char*));
printf("%dn", sizeof(short*));
printf("%dn", sizeof(int*));
printf("%dn", sizeof(long*));
printf("%dn", sizeof(float*));
printf("%dn", sizeof(double*));
return 0;
}
2. 指针变量的类型
char * pc = NULL; short * ps = NULL; int * pi = NULL; long * pl = NULL; float * pf = NULL; double * pd = NULL;
这里可以看到,指针变量的定义方式是: type *
其实: char* 类型的指针变量是为了存放 char 类型变量的地址。 short* 类型的指针变量是为了存放 short 类型变量的地址。 int* 类型的指针变量是为了存放 int 类型变量的地址。
但是,指针变量的大小又和指针变量的类型无关,那么指针变量的类型的意义是什么呢?
2.1 指针变量±整数
代码语言:javascript复制#include <stdio.h>
int main()
{
int a = 0x11223344;//0x开头的是16进制数字
int * pa = &a;
char * pc = &a;
printf("%pn", pa);//010FFE7C
printf("%pn", pc);//010FFE7C
printf("%pn", pa 1);//010FFE80
printf("%pn", pc 1);//010FFE7D
return 0;
}
总结: 指针变量的类型决定了指针向前或者向后走一步有多大(距离)。
2.2 指针变量的解引用
代码语言:javascript复制#include <stdio.h>
int main()
{
int a = 0x11223344;//0x开头的是16进制数字
char * pa = &a;
*pa = 0;
return 0;
}
代码语言:javascript复制#include <stdio.h>
int main()
{
int a = 0x11223344;
int* pa = &a;
*pa = 0;
return 0;
}
总结: 指针变量的类型决定了对指针变量解引用的时候有多大的权限(能操作几个字节),比如: char* 的指针变量解引用就只能访问一个字节,而 int* 的指针变量解引用就能访问四个字节。
3. 野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
3.1 野指针成因
- 指针未初始化
#include <stdio.h>
int main()
{
int* p;//局部变量不初始化的时候,内容是随机值
*p = 20;
printf("%dn", *p);
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;
}
- 指针指向的空间释放
放在动态内存开辟的时候讲解,这里可以简单提示一下。
代码语言:javascript复制#include <stdio.h>
int * test()
{
int a = 110;
return &a;
}
int main()
{
int* p = test();
printf("%dn", *p);
return 0;
}
3.2 如何规避野指针
- 指针初始化
- 小心指针越界
- 指针指向空间释放,及时置NULL
- 避免返回局部变量的地址
- 指针使用之前检查有效性
#include <stdio.h>
int main()
{
int a = 10;
int* p = &a;
int* ptr = NULL;//ptr是一个空指针,没有指向任何有效的空间,这个指针不能直接使用
//int* ptr2;//野指针
if (ptr != NULL)
{
//使用
}
return 0;
}
4. 指针运算
4.1 指针±整数
代码语言:javascript复制#include <stdio.h>
int main()
{
int arr[10] = { 0 };
//不使用下标访问数组
int* p = &arr[0];
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
for (i = 0; i < sz; i )
{
*p = i;
p ;//p = p 1
}
p = arr;
for (i = 0; i < sz; i )
{
printf("%d ", *(p i));//p i
}
/*for (i = 0; i < sz; i )
{
printf("%d ", arr[i]);
}*/
return 0;
}
代码语言:javascript复制#define N_VALUES 5
int main()
{
float values[N_VALUES];
float *vp;
//指针 -整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES]; )
{
*vp = 0;
}
return 0;
}
注:
代码语言:javascript复制//int arr[10]
//int* p = arr;
//*(p i) == arr[i]
//*(arr i) == arr[i]
//arr[i] == *(arr i) == *(i arr) == i[arr]
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int i = 0;
for (i = 0; i < 10; i )
{
printf("%d ", i[arr]);//[] 操作符
}
//2 3 --> 3 2
//arr[i] --> i[arr]
return 0;
}
4.2 指针-指针
代码语言:javascript复制//地址-地址
//指针-指针
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%dn", &arr[9] - &arr[0]);//9
printf("%dn", &arr[0] - &arr[9]);//-9
return 0;
}
//指针-指针得到的数值的绝对值是指针和指针之间的元素个数
之前我们学习过如何通过自己的函数来实现计算字符串长度:
代码语言:javascript复制#include <stdio.h>
int my_strlen(char* s)
{
int count = 0;
while (*s != '