如何表示地址?
地址是整数,是否可以用int?
理论上可以,但是为了强调它是个内存地址,提出一些新的类型:
char* :表示一个char类型变量的地址
short* :表示一个short类型变量的地址
int* :表示一个int类型变量的地址
float* :表示一个float类型变量的地址
double* :表示一个double类型变量的地址
unsigned char* :表示一个unsigned char类型变量的地址
XXX* : 表示XXX类型变量的地址
上面的指针都是基本数据类型的指针
基本类型指针就是指:存放基本数据类型地址的指针
int *p;
int i = 3;
p = &i;
int j;
j = *p;
-
int *p;
中p是变量的名字,p是一个指针变量 -
int *
可以理解为是数据类型;int *
表示变量p存放的是int类型变量的【地址】 -
&i
表示取i的地址,然后赋值给指针变量p,所以变量p保存了i的地址,因此p指向了i,所以通过指针变量p就可以找到i; -
*p
就完全等同于i (或理解为:在所有出现*p
的地方都可以替换成i,在所有出现i的地方都可以替换成*p
) -
p
和i
是两个不同的变量,并且他们的数据类型也不一样
关于指针
- 1- 不同类型的指针,不能互相赋值
int a = 10;
int* pa = &a;
double* pd = pa; // Error ❌ 左侧double* ,右侧int* , 不能互相赋值
char a = 78;
float* p = &a; // Error ❌ 左侧float* ,右侧cahr* , 不能互相赋值
- 2- 星号*的位置
以下几种方式均可以,没有区别, 看个人习惯
int* p;
int * p;
int *p;
星号操作:按地址访问
有一个指针变量p, 则p用于访问p指向的变量*(p指向的内存)。
int a = 0x123;
int *p = &a; // p指向a所在的内存
*p += 2;
int b = *p; // 取得p指向内存的值
int c = *p + 2;
指针作为函数的参数
可以把指针作为函数的参数
void test(int *p) {
// 能把指针干什么呢?
// 使用星号操作*p,来读写内存
*p = 10;
}
int main() {
int a = 0;
test(&a);
print("a=%d",a); // 打印10
}
使用指针作为参数,可以实现两种功能:
- 可以读取上一层函数中变量的值 *p
- 可以修改上一层函数中变量的值 *p (普通的参数无法实现)
例: 交换两个变量的值
void swap(int* a, int* b)
{
int t = *a;
*a = *b;
*b = t;
}
int main()
{
int a=10, b=11;
swap(&a, &b);
return 0;
}
传递数组作为参数
之前说过数组名,实质就是指针类型,传递数组,就是传递指针
int test(int *p, int length) {
int sum = 0;
for(int i=0; i<length; i++) {
sum += p[i];
}
return sum;
}
int main() {
int arr[] = {1, 2, 3, 4};
int ret;
ret = test(arr,4); // 从arr[0] 到 arr[3] 结果sum=10
ret = test(arr,3); // 从arr[0] 到 arr[2] 结果sum=6
ret = test(arr + 1,3); // 从arr[1] 到 arr[3] 结果sum=9
}
❤️ 对于test函数来说,不关心传进来的是不是数组;在它眼里,它只是接收到一个内存地址而已
- 以下两种方法完全等价
int test(int *p, int length)
和
int test(int p[], int length)
- 传递数组时,总是要传递长度信息,不能只把手地址传递给函数,这样是不够的
传指针有什么作用?
把一个指针传给函数,有什么作用?
- 返回多个参数: return只能返回一个值,如果一个函数要返回多个值,必须使用指针参数(输出参数)
- 效率问题: 传值和传地址,后者一般效率更高
如何安全的使用指针 ?
指针不可乱用,安全的使用指针,需要对指针有足够清晰的认识。
在使用指针之前,一定要弄清楚两个问题:
- 这个指针指向了哪儿?
- 这个指针指向的那个地方是否有效?(即:是否能够访问)
指针指向了哪儿?
- 指向了变量、数组、结构体
- 指向0
指向0的情况:(空指针)
int* p = 0; // 空指针
空指针
值为0的指针,称为空指针
int* p = 0;
当指针为空时,不能使用星号操作。
int* p = 0;
printf(“%d \n”, *p); // 不能这么干❌
但空指针是程序员可以接受的一种情况,只需加一个if判断就能解决!
if(p) {
printf(“%d \n”, *p);
}
空指针:应用
某些参数可以省,当不想传入时就传一个空指针
void max_min(const int* arr, int len,
int* pmax, int* pmin)
{
...
if(pmax) *pmax = _max;
if(pmin) *pin = _min;
}
// 调用时
int _max;
// 表示只需要得到最大值
max_min(arr,4, &_max, 0);
危险的情况
int* p;
printf("%d", *p); // 立即crash
这个指针指向哪了? 当一个指针未赋值时,其值为随机值,此时指向了一个随机的内存地址,称为“野指针”
安全使用指针
1-杜绝野指针
一个好习惯:初始化为空指针 : int * p = 0;
考虑:为什么传递一个空指针是允许的?而传一个野指针是不允许的? (空指针也会崩溃)
int * p = 0;
printf(“ %d \n”, *p); // 空指针也会崩溃 ❌
传递空指针:函数内部是有办法处理,判断一下就行。
传递野指针:函数内无法判断出来你是个野指针!!
void test(int* p)
{
// 在使用指针前判断
if( p != 0)
{
printf(“%d \n”, *p);
}
}
2-严防数组越界
当指针指向数组时,要注意不要越界访问
int arr[4];
int* p = arr;
p += 4;
*p = 12; // 已经越界!但不容易察觉!
3-变量是否已经失效?
如果指向的目标的生命期已经终结(失效),则该指针也失效
int main()
{
int* p = 0;
if(1)
{
int a = 10; // a生效
p = &a; // p指向a
} // a已失效
*p = 11; // p指向了一个无效的位置⚠️
return 0;
}
所以,指向全局变量的指针,安全性相对较高。因为它的生命期是永恒的,这一块内存总是有效。
网友评论