C语言中指针的深度理解(1)

一.什么是指针

计算机中的内存被划分为一个个的内存单元,每个内存单元即一个字节里放置八个比特位,每个内存单元都会有各自的编号方便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;
 }

运行成功。

总结:如果未来函数中只是需要调用主函数中的变量的值实现计算,可以使用传值调用,但如果函数内部要修改主调函数中的变量的值,只能用传址调用了。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值