目录
1.指针是什么?
2.指针和指针类型
2.1指针+-整数
2.2指针的解引用
3.野指针
3.1野指针的成因
3.2如何规避野指针
4.指针运算
4.1指针+-整数
4.2指针-指针
4.3指针的关系运算
5.指针和数组
6.二级指针
7.指针数组
1.指针是内存中一个最小单元的编号(就是一个字节),也就是地址。
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
总结:指针就是地址,口语中说的指针通常指的是指针变量。
看代码:
#include
int main()
{
int a = 10;//在内存中开辟一块空间
int *p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
//中,p就是一个指针变量。
return 0; }
11111111 11111111 11111111 11111111
我们知道变量有不同的类型,比如:int char float 等等,那么指针有没有类型呢?
当然有呢
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
看代码:
#include
int main()
{
int n = 10;
char *pc = (char*)&n;//(char* 是强制类型转换)
int *pi = &n;
printf("%p\n", &n);
printf("%p\n", pc);
printf("%p\n", pc+1);
printf("%p\n", pi);
printf("%p\n", pi+1);
return 0;
}
结论:
1.指针类型决定了指针的步长(向前(+)/向后(-)走一步有多大距离)。
2.int*的指针+1,表示跳过一个整型,也就是向后走4个字节
char*的指针+1,表示跳过一个字符,也就是向后走1个字节
double*的指针+1,表示跳过一个双精度浮点数,也就是向后走8个字节
short*的指针+1,表示像后走2个字节
//演示实例
#include
int main()
{
int n = 0x11223344;
char *pc = (char *)&n;
int *pi = &n;
*pc = 0; //重点在调试的过程中观察内存的变化。
*pi = 0; //重点在调试的过程中观察内存的变化。
return 0; }
#include
int main()
{
int *p;//局部变量指针p未初始化,默认为随机值,
//也就是说p的初始值未指定,p的地址不知道,通过p去找的值,不知道p的地址,但是p里面存了值
*p = 20;
//我们找这个随机值对应的空间,然后把20放进去,这个随机值对应的空间不是我们的,
//所以,野指针
return 0; }
2.指针越界访问
#include
int main()
{
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
//p++先用后加,也就是先 1. *p = i; 再 2. p++;
//等价于:
//*p = i;
//p++;
}
return 0; }
3.指针指向的空间被释放给操作系统了,这个时候再去使用这个指针,就会形成非法访问(访问的空间不属于我,而是属于操作系统),形成野指针。
//非法访问,形成野指针
int* Test()
{
int num = 100;
return #
}
int main()
{
int* p = Test();
*p = 200;
return 0;
}
分析:
1. 指针初始化
2. 小心指针越界
3. 指针指向空间释放,及时置NULL
4. 避免返回局部变量的地址——也即是避免返回栈空间上的地址
5. 指针使用之前检查有效性
int main()
{
double arr[5] = {0};
double* p = arr;
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%lf ", *(p + i));//指针加上整数,每次+1,往后走一个double类型的长度
}
return 0;
指针能够减去指针,但是指针不能够加上指针,就好比,日期减去日期,可以知道相隔几
天,但是日期加上日期就没有意义。
指针-指针的前提是,两指针指向同一块连续的空间,如果不是连续的空间,那么无意义。
大指针-小指针:得到的是两指针中间元素的个数
小指针-大指针:得到的是两指针中间元素的个数的相反数
int main()
{
//两个指针相减的前提是:指针指向的同一块连续的空间
int arr[10] = {0};
printf("%d\n", &arr[9] - &arr[0]);
printf("%d\n", &arr[0] - &arr[9]);
int a = 10;
char c = 'w';
printf("%d\n", &a - &c);//err
return 0;
}
// 指针-指针
//计算字符串的长度
int my_strlen(char* str)
{
char* start = str;
while (*str)
str++;
return str - start;
}
int main()
{
char arr[] = "abc";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//代码1
for(vp = &values[N_VALUES]; vp > &values[0];)//指针比较大小
{
*--vp = 0;
}
//简化之后的
//代码2
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
*vp = 0;
}
代码2实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因
为标准并不保证它可行。
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,
但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
分析:
我们看一个例子:
#include
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0;
}
结果:
结论:数组名表示的是数组首元素的地址。
注意:
数组和指针不是一个东西,
数组能够存放一组数,是连续的空间,数组的大小取决于元素的个数和类型
指针是一个变量,是用来存放地址的,指针的大小是4/8个字节
数组和指针的联系:
数组名是地址(指针)
数组把首元素地址交给一个指针变量后,可以通过指针来访问数组
#include
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
int* p = arr; //指针存放数组首元素的地址
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p + i);
}
return 0;
}
结果:
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?
一级指针的地址存在二级指针里面 。
二级指针的地址存在三级指针里面。
实际工程中,三级指针用的很少很少。
int main()
{
int a = 10;//
int* p = &a;//p就是指针变量,一级指针变量
int** pp = &p;//pp就二级指针
//下面三种方式都能找到a
//*(*pp) = 200;
**pp = 200;
//*p = 20;
printf("%d\n", a);
return 0;
}
分析:
指针数组是指针还是数组?
答案:是数组。是存放指针的数组。
数组我们已经知道整形数组,字符数组。
int arr1[5];
char arr2[6];
那指针数组是怎样的?
int* arr3[5];//————存放整型指针的数组
char* arr[4];//————存放字符指针的数组
arr3是一个数组,有五个元素,每个元素是一个整形指针。
注意:
int arr[3];
arr[3] ====== *(arr+3)
int arr[4][4];
arr[2][3] ======== *(*(arr+2)+3)
上述这两种访问方式是等价的