一.什么是指针
计算机中的内存被划分为一个个的内存单元,每个内存单元即一个字节里放置八个比特位,每个内存单元都会有各自的编号方便CPU快速找到,我们把他们的编号称为地址,C语言中地址就是指针。
二.指针的三个相关值
举个例子
int a=0;
int* p=&a;
*p=100;
1.指针变量p
p中存放了一个地址。拆解int * p,int是p指向的对象的类型,*代表p是指针变量。
2.*p 解引用p
1)这个操作符放在p前,执行这个语句就会得到p所指向的那个对象,即a。
*p=100就会把原本a的值改成100。
2)指针的类型决定了对指针解引用的权限。
例如char*解引用就只能访问一个字节,int*可以访问四个字节。
3.&a 取出a的地址
三.指针变量的大小
不管什么地址,只要是地址,在32位下大小都是四个字节,64位下是八个字节。
例如:
int a[]={1,2,3,4};
printf("%d",sizeof(&a));
printf("%d",sizeof(arr+0));
注:除了arr单独放在&和sizeof后时表示整个数组,其余情况都代表首元素的地址。
所以第一个求的是整个数组地址的大小,第二个求的是首元素地址的大小。都为4或者8个字节。
四.指针的运算
1.指针+-整数
指针的类型决定了指针向前还是向后走整数步有多大。
*(p+2)也就是跳过了2*该指针类型所占的字节大小。
2.指针-指针
计算前提是两个指针指向的是同一空间,指针-指针的绝对值是指针之间元素的个数。
3.注意事项
1)不可以指针加指针,类比于日期减日期可得到天数,但日期加日期无意义。
2)void*指针,无具体类型,可以用来接收任意类型地址,但不能直接进行指针的+-整数运算和解引用操作,一般是要进行强制类型转换才可以。
五.const修饰指针
const修饰变量n后,在语法上加上限制,我们就无法强制修改n,但是如果使用n的地址就可以修改n的值。为了防止这一现象出现,我们用const来修饰指针变量。
一般而言,const修饰指针变量可以放在*的左边或右边,意义不同。
eg. int const*p 和int *const p
直接上结论,1.const放在*的左边,修饰的是指针指向的内容即*p,所以p指向的内容就无法被改变,但是p指针变量本身可以改变。如下图,第一个不ok,第二个ok。

2.放在右边,const修饰的是p,则p指向的内容可以改变,但是p指针变量本身不可以改变。如下图,第一个ok,第二个不OK。

六.野指针的成因和规避
1.指针未初始化
如下图

规避方法:如果不知道指针应该指向哪里,就给指针赋值NULL,例如int*p2=NULL.
2.指针越界访问
如下图
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = &arr[0];
int i = 0;
for(i=0; i<=11; i++)
{
*(p++) = i;
}
return 0;
}
//当指针指向的范围超出数组arr的范围时,p就是野指针
规避方法:一个程序申请了多少空间,指针也就只能访问多少空间,不能超出范围访问。
3.指针指向的空间释放
如下图,局部变量n只能在函数中使用,出了函数就释放空间了。
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
规避方法:指针变量不再使用时及时将其变成NULL,指针使用前用assert检查有效性。
assert(p != NULL);
4.assert断言
1.头文件为assert.h,用于在运行中保证程序符合指定条件。
2.assert()宏接受一个表达式作为参数,表达式为真返回值非零,assert不会产生任何作用,程序正常运行。表达式为假,就会报错,在标准错误stderr中写入一条错误信息。
如果确认程序没有问题不需要再做断言,可以在,就在 #include <assert.h>语句的前面,写上#define NDEBUG。
注:assert一般只能在debug中使用,在release中直接被优化了。
七.传值调用和传址调用
eg 写一个函数交换两个整型变量的值,我们可能会写出下面这个程序:
#include <stdio.h>
void Swap1(int x, int y)
{
int tmp = x;
x = y;
y = 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;
}
运行后程序并未交换两个整型的值。
这是因为实参在传递给形参时,形参会单独创建一份临时空间来接受实参,对形参修改,但不会影响实参。为了解决这个问题,我们要使用指针来将ab地址传给swap,交换其地址,就可以达到效果了。
#include <stdio.h>
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);
Swap2(&a, &b);
printf("交换前:a=%d b=%d\n", a, b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
运行成功。
总结:如果未来函数中只是需要调用主函数中的变量的值实现计算,可以使用传值调用,但如果函数内部要修改主调函数中的变量的值,只能用传址调用了。
1965

被折叠的 条评论
为什么被折叠?



