指针的介绍1后
5.指针运算
5.1指针+-整数
通过指针加减整数跳过相应的字节就可以访问相应的数组元素等
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = &arr[0];
for (int i = 0; i < sz; ++i)
{
printf("%d ", *p);
p++;
}
printf("\n");
int *q = &arr[0] + sz - 1;
for (int i = 0; i < sz; ++i)
{
printf("%d ", *q);
q--;
}
return 0;
}
5.2指针-指针
指针-指针的绝对值等于指针与指针之间的元素个数
要求这两个指针指向同一块内存区域
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", &arr[0] - &arr[sz - 1]);
printf("%d\n", &arr[sz - 1] - &arr[0]);
return 0;
}
数组一个九个元素,第九个元素的起始地址与第一个元素的起始地址之间正好8的元素
5.3指针之间的关系运算
即比大小
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = &arr[0];
while (p <= &arr[sz - 1])
{
printf("%d ", *p);
p++;
}
return 0;
}
6.野指针
(1) 野指针的概念:指向不可知的位置的指针就是野指针
(2)不可知的位置包括但不限于随机的、不明确的、没有明确限制的位置
6.1野指针成因
1.指针未初始化
指针未初始化,该指针指向的内容就是随机的、不明确的
2.指针越界访问
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = &arr[0];
for (int i = 0; i <= sz; ++i)
{
printf("%d ", *p);
++p;
}
return 0;
}
指针越界访问,得到了一个随机值
指向随机值时的指针就是一个野指针
指向1、 2、3时的指针不是野指针
3.指针指向的空间已经被释放
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int* X()
{
int a;
return &a;
}
int main()
{
int* p = X();
return 0;
}
函数调用完毕后,之前所申请的内存空间就会归还给操作系统
相应的局部变量就会被销毁不存在了
X()函数返回了一个局部变量的地址赋值给p,此时p就是一个野指针
如果再使用p去访问已经被归还的内存,就形成了非法访问
此时造成的后果包括但不限于
未定义行为、内存安全问题、程序不稳定等
6.2规避野指针
6.2.1指针初始化
int * p = NULL;
在C语言中,NULL
是一个宏定义,它代表一个空指针常量
NULL
用于初始化指针变量,表示该指针不指向任何有效的内存地址。这有助于防止指针在未经初始化的情况下被使用,从而避免未定义行为(如访问无效内存)。
在C标准中,NULL
通常被定义为((void*)0)
,但也可能简单地定义为0
。这两种定义在大多数情况下是等效的,因为当0
被赋值给一个指针时,它会自动被转换为空指针。然而,使用((void*)0)
的显式类型转换可以提供更好的类型安全性,特别是在与标准库函数交互时。
6.2.2小心指针越界
⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是 越界访问
6.2.3指针变量不再使用时及时置为NULL,使用之前检查有效性
(1)不再使用及时置为NULL,避免非法访问,或者不必要的修改
(2)使用之前检查有效性,是为了防止未定义行为、避免内存泄漏等
7.传值传参和传址传参
7.1传值传参
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int Add(int a, int b)
{
return a + b;
}
int main()
{
int a = 1;
int b = 2;
int c = Add(a, b);
printf("%d\n", c);
return 0;
}
传入的只是a,b的值
此时,我只是希望获得a与b的和
7.2传址传参
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int a = 1;
int b = 2;
printf("交换前:a = %d\n", a);
printf("交换前:b = %d\n", b);
swap(a, b);
printf("交换后:a = %d\n", a);
printf("交换后:b = %d\n", b);
return 0;
}
我们调用swap函数的目的是改变a与b的值
如果我们传入的是a与b的值
那么形参是实参的临时拷贝
函数调用完毕后,a与b并不会发生改变
形参是实参的临时拷贝的解释:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
printf("形参a的地址: % p\n", &a);
printf("形参b的地址:%p\n", &b);
}
int main()
{
int a = 1;
int b = 2;
printf("实参a的地址:%p\n", &a);
printf("实参a的地址:%p\n", &b);
swap(a, b);
return 0;
}
我们发现形参与实参的地址不同
那么,既然地址不同
实际交换的就并不是main函数中实参a与b的值了
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int a = 1;
int b = 2;
printf("交换前:a = %d\n", a);
printf("交换前:b = %d\n", b);
swap(&a, &b);
printf("交换前:a = %d\n", a);
printf("交换前:b = %d\n", b);
return 0;
}
当我们传入的是a与b 的地址的时候
我么就可以在函数中通过地址找到a与b 的值
就进行常规的交换
总结:
函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值传参
函数内部要修改主调函数中的变量的值,就需要传址传参