-
一、基本概念
在内存中,有一个个的储存单元,每个都是一字节大小,每个储存单元都有唯一的“地址”来找到他们,而指针就是用来储存地址的。我们可以通过储存在指针里的地址来各种操作。
-
二、指针变量及取地址操作符(&)和解引用操作符(*)
指针变量是用来储存指针的变量,方便后期使用。指针变量也是⼀种变量,这种变量就是用来存放地址的,存放在指针变量中的值都会理解为地址。
1.那怎么创建指针变量?
这里需要“int*”和取地址操作符(&)。
int main()
{
int a = 1;
int* pa = &a;
return 0;
}
“&”是取地址操作符,取后面变量的地址。
“int*”是创建一个整形指针变量,名为“pa”。
所以以上代码成功创建了一个指针变量并将“a”的地址储存到指针变量里。
2.怎么使用指针变量?
这时需要用解引用操作符(*)。
int main()
{
int a = 1;
int* pa = &a;
printf("%p\n", pa);
printf("%d\n", *pa);
return 0;
}
“*”放在需要使用的指针变量左边,此时“*pa”就代表他指向的“a”,否则只是一串地址。
上述代码打印结果:
由此可见“pa”只是一串地址,而“*pa”则直接等同于使用“a”。
三、指针变量的类型
指针的类型决定了,对指针解引用的时候有多大的权限,就是⼀次能操作几个字节。比如:char* 的指针解引用就只能访问⼀个字节,而int* 的指针的解引用就能访问四个字节。
还有一种特殊的void类型指针,,这种类型的指针可以用来接受任意类型地址。但是也有局限性,void类型的指针不能直接进行指针的+-整数和解引用的运算。
-
四、Const修饰指针变量
int main()
{
int a = 1;
const int* pa1 = &a;
int* const pa2 = &a;
return 0;
}
第一种情况:const放在*的左边,修饰指针指向的内容,使其不能被修改。可以理解为const修饰*pa,而*pa即为a,所以修饰的是a。
第二种情况:const放在*的右边,修饰指针变量本身,使其不能被修改。可以理解为const只修饰了pa。
-
五、指针运算
1.指针+/-整数
int main()
{
int a = 1;
int* pa = &a;
pa += 1;
pa -= 1;
return 0;
}
+n为向后跳过n个指针变量类型长度,例如int*跳过4个字节,char*跳过1个字节。-n为向前跳过n个指针变量类型长度。
2.指针-指针
由 指针1+整数=指针2得出:指针1-指针2=整数,这个整数即为两个整数之间的元素个数。
-
六、野指针
1.概念
野指针就是指向的位置不明确,随机,无限制的指针。
2.成因
1.指针未初始化
int main()
{
int* p;
*p = 20;
return 0;
}
因为未初始化,默认为随机值,即为野指针。
2.指针越界访问
int main()
{
int arr[10] = { 0 };
int* p = &arr[0];
int i = 0;
for (i = 0; i <= 11; i++)
{
*(p++) = i;
}
return 0;
}
因为指针p超出了数组的范围,越界访问变成了野指针。
3.指针指向的空间释放
int* test()
{
int n = 100;
return &n;
}
int main()
{
int* p = test();
printf("%d\n", *p);
return 0;
}
虽然test函数返回了n的地址给指针p,但是test运行完毕后,将n的内存释放了,指针p因此成为了野指针。
3.规避野指针
为了规避野指针造成不必要的麻烦,我们需要注意小心指针越界,避免返回局部变量的地址以及及时将未初始化与不使用的指针置为NULL。
-
七、简单的传址调用
有了上面的知识,我们终于可以用指针来解决问题了,接下来是一个简单的传址调用函数的使用:写⼀个函数,交换两个整型变量的值
void Swap2(int* px, int* py)
{
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a = % d b = % d\n", a, b);
Swap1(&a, &b);
printf("交换后:a = % d b = % d\n", a, b);
return 0;
}
完。