野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
常见野指针案例:
1. 指针未初始化
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
但是下面这个案例却又是合理的:
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;//*vp vp++
}
2. 指针越界访问
int main()
{
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
3. 指针指向的空间释放
int *test()
{
int a = 10;
return &a;
}
int main()
{
int *p = test();
return 0;
}
在函数 test()
中定义了一个局部变量 a
,当函数执行完毕后,这个局部变量就会被销毁,其存储空间也会被释放。而在函数返回时,返回的是指向这个局部变量 a
的地址,这个地址已经不再是有效的,因为这个地址所指向的存储空间已经被释放了,所以这个指针就是一个野指针。在 main 函数中,通过函数返回的地址来创建指针变量 p,这样就会导致程序运行时访问非法内存,造成程序崩溃或者产生不可预期的结果。因此,应该将返回的指针变量 p 置为 NULL,以避免访问非法内存。
修改:
int *test()
{
int a = 10;
return NULL;
}
int main()
{
int *p = NULL;
p = test();
if (p != NULL) {
printf("%d\n", *p);
}
return 0;
}
对于NULL小案例
int *p = NIULL;//初始化之后
*p = 10;//这样是错误的
/*正确写法如下*/
if(p != NULL)
{
*p = 10;
}
指针运算
指针运算是指通过指针变量访问其所指向的内存单元,以及通过指针变量进行加减运算等操作。常见的指针运算包括以下几种:
1. 指针加减运算:指针加减运算是通过指针变量进行加减操作来改变指针所指向的内存地址,从而访问对应内存单元的值。例如,指针加 `1` 表示将指针向后移动一个元素,指针减 `1` 表示将指针向前移动一个元素。
2. 指针取值运算:指针取值运算是通过指针变量来访问其所指向的内存单元的值。例如,`*p` 表示通过指针变量 `p` 来访问其所指向的内存单元的值。
3. 指针比较运算:指针比较运算是通过指针变量来比较两个指针所指向的内存地址的大小关系。例如,`if (p < q)` 表示比较指针变量 `p` 和 `q` 所指向的内存地址的大小关系,如果 `p` 指向的内存地址小于 `q` 指向的内存地址,则返回 `true`。
4. 指针类型转换:指针类型转换是将指针变量转换为其他类型的指针变量,以便于对其进行操作。例如,`(int *)p` 表示将指针变量 `p` 转换为 `int` 类型的指针变量。
5. 指向数组的指针:指向数组的指针是指向数组的第一个元素的指针。例如,`array` 是一个整型数组,`&array[0]` 表示指向数组的第一个元素的指针。
指针运算可以用于动态内存分配、数组操作、函数参数传递、字符串操作等方面。需要注意的是,指针运算需要谨慎处理,避免访问非法内存导致程序崩溃或产生不可预期的结果。
简单例子
1. 指针加减运算
int arr[] = {1, 2, 3, 4, 5};
int *p = arr; // p 指向数组的第一个元素
// 指针加 1
p++;
printf("%d\n", *p); // 输出 1
// 指针减 1
p--;
printf("%d\n", *p); // 输出 4
2. 指针取值运算
int arr[] = {1, 2, 3, 4, 5};
int *p = arr;
// 指针取值运算
printf("%d\n", *p); // 输出 1
3. 指针比较运算
int arr[] = {1, 2, 3, 4, 5};
int *p = arr;
int *q = arr + 2;
// 指针比较运算
if (p < q) {
printf("p is before q\n");
} else if (p > q) {
printf("p is after q\n");
} else {
printf("p is equal to q\n");
}
4. 指向数组的指针
int arr[] = {1, 2, 3, 4, 5};
int *p = arr;
int *q = &arr[2];
// 指向数组的指针
printf("p points to %d\n", *p); // 输出 3
printf("q points to %d\n", *q); // 输出 4
5. 指针类型转换
int arr[] = {1, 2, 3, 4, 5};
int i = 3;
int *p = &arr[i];
// 指针类型转换
printf("%d\n", *(int *)p); // 输出 4
实用案例
1.对于数组的遍历:除了下标法还可以使用指针解引用
int main()
{
int arr[10];
int i;
int sz = sizeof(arr)/sizeof(arr[0]);
/*
for(i=0;i<sz;i++)//下标法
{
arr[i] = 1;
}
*/
int *p = arr;
for(i=0;i<sz;i++)
{
*p = 1;
p++;
}//或者直接*(p + i) = 1;
return 0;
}
2.指针 - 指针得到的是指针和指针之间的元素个数,
但不是所有指针都可以相减,只有在同一个空间的才可以。
3.在常见题目中计算字符串长度除了strlen();函数之外
还可以使用指针运算来计算:
int my_strlen(char *str)
{
char *count = str;
/*while(*str != '\0')
{
count++;
str++;
}*/
while(*str != '\0')
{
str++;
}return (str - count);
}
当然递归也可以。
对于指针的加法意义不大,不讨论。