c语言指针(一)

在我们学习c语言指针时,我们首先要了解内存和地址。

内存和地址:

我们举一个关于生活中的例子,来让我们更好的理解内存和地址,假设你在学校的一间宿舍里,你的朋友想要来找你玩,这时你的朋友就需要到一间一间的宿舍里面去找你,这样效率就会比较慢,但是你的朋友知道了你的寝室号,这样你的朋友就能够很快的找到你。

在计算机中也是这样的,计算机中的数据都是存储在内存里面的,计算机需要读取数据也是从内存中读取的。那么这些内存是怎么管理的呢?

其实就是把内存划分为一个一个的内存单元,每个内存单元的大小为一个字节。

一个字节的大小位8个比特位。

一个比特位都可以存储一个二进制的0和1。

每个内存单元其实相当于一个学生寝室,一个字节能存放8个比特位,就好比寝室的八人间,每一个学生就是一个比特位。

每个内存单元都有一个编号(相当于寝室的门牌号)。有了这个编号,计算机就能快速的读取到这个内存空间里面的数据。在计算机中我们把内存单元的编号也称为地址,在c语言中给地址取了个名字叫做指针。

所以我们可以理解为:内存单元编号 == 地址 == 指针

取地址操作符:

在c语言中创建变量其实就是向内存申请了一块空间

上述的代码中创建了整型变量a,向内存申请了四个字节,用来存放整数10,其中每个字节都有地址。a的四个字节的地址分别为:

那我们怎么得到这个a的地址呢,这时我们就需要用到取地址操作符&,这里&在之前提到过,是位运算符,其实也是取地址操作符。

这里的&a取出的是a所占四个字节中较小的地址。虽然整型变量占四个字节,但是我们知道了第一个字节的地址,那我们顺藤摸瓜就可以访问到四个字节的数据。

指针变量:

我们通过取地址操作符得到了a的地址,那我们想要存储a的地址怎么办呢?

这时我们就需要用到指针变量了。

这里的p就是指针变量。

那我们怎么理解这个指针变量呢

指针变量也是一种变量,这种变量就是用来存放地址的,存在指针变量中的值都会被理解为地址。

解引用操作符:

我们上面把地址存在了指针变量中,那我们之后需要使用怎么使用呢?

这时就用来了解引用操作符*

在上述的代码中,我们用到了解引用操作符,*p 的意思就是通过p中存放的地址,找到指向的空间,*p其实等价于变量a了,所以*p = 0;这个操作符就是把a改成 0 了。

指针变量的大小:

我们常见的计算机分为32位机器和64位机器。

32位机器假设有32跟地址总线,每根地址线出来的电信号转换为数字信号后是1或者0,那我们把32根地址线产生出来的2进制序列当做一个地址,那么一个地址就是32个bit位,需要4个字节才能存储。

64位机器,假设有64根地址线,一个地址就是64个二进制组成的二进制序列,存储起来就需要8个字节的空间,指针的大小就是8个字节。

指针变量类型的意义:

既然指针变量的大小与类型无关,那为什么还要有各种各样的指针类型呢?

这里的a的值为0x11223344(这里表示的是一个16进制的数字)

我们用int*类型的pi指针给他修改为0之后,a的四个字节就都被修改成0了。

但是我们用char*类型的pc指针给他修改成0之后,a就只有一个字节被修改了

结论:指针的类型决定了,对指针解引用的时候有多大的权限(一次能够修改几个字节)

char*的指针解引用只能访问一个字节,但是char*类型的可以访问4个字节。

指针的+ - 整数运算

这里p+1走了4个字节的大小(一个整型的大小)

pc+1走了一个字节的大小(一个字符的大小)

结论:指针的类型决定了指针+1(或者-1)能走多远(几个字节)

void*:

void*在c语言中是一种特殊的指针,它是无具体类型的指针,可以接收任何类型的地址。但是它不能进行指针的解引用操作和+-整数的运算

还是上面的代码,编译器会报警告,因为一个int类型的变量,用char*来接收。

但是用void*指针来接受就不会有任何问题

但是void*类型的不能解引用

const修饰变量:

一个变量初始化之后是可以被修改的,但是如果我们希望这个变量不能被修改,那我们就需要用const来修饰它。简单来说,const修饰的变量,可以当作时一个常量,常量是不能被修改的。

上述的代码,加了const之后就不能被修改了。

但是呢,我们还是有别的方法可以修改它的

我们虽然不能直接来修改a,但是我们可以通过指针p来进行修改,这样就可以进行修改。

我们加上了const就是为了防止a被修改的,如果p拿到了a的地址就能修改a了,这样就打破了const的限制,这样是不合理的,那怎么样才能防止p拿到了a的地址也不能修改了呢?

const修饰指针变量:

const修饰的指针变量,有两种情况

情况1:const放在*号的左边

当我们把const放在了*的左边时,这样就算我们拿到了a的地址,我们也不能对它进行修改了。

但是我们可以修改指针变量p的指向

情况2:const放在*的右边

当我们把const放在*的右边时,这时指针变量p的指向就不能被修改了,但是可以修改它指向的内容可以被修改

如果说,我们想让它指针变量p的指向不被改变,指针变量指向的内容也不能被改变,我们也可在*的两边都加上const。

就像这样

所以通过上面的代码我们可以得出这样的结论:

const如果放在*的左边,修饰的时指针指向的内容,保证指针指向的内容不能通过指针来改变,但是指针变量可以变。

简单来说就是const放在*左边那么*p就不能变。

const如果放在*的右边,修饰的时指针变量本身,保证了指针变量不能被修改,但是指针指向的内容可以通过指针改变。

简单来说就是const放在*右边那么p就不能变。

指针+-整数:

一般我们在对数组的读取是用下标来访问

但是我们学习了指针,就可以用指针的方法来读取数据。

因为数组在内存中是连续存放的,我们只要知道了第一个元素的地址,顺藤摸瓜就能找到后面所有的元素

野指针

概念:野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)

造成野指针的原因

1.指针未初始化

2.指针越界访问

3.指针指向的空间被释放

那有这么多的原因造成了野指针,那我们应该注意如何避免呢?

1.指针初始化

如果我们知道指针指向哪里,我们就给他赋值地址,如果不知道就给他赋值NULL。

NULL是c语言中定义的一个标识符常量,值是0,0也是地址,这个地址是无法被使用的,读写改地址会报错。

2.小心指针越界

一个程序向内存申请了多大的空间,我们通过指针就只能访问多大的空间,不超出这个空间访问就可以了。

3.指针变量不用时应该及时置为NULL,指针使用之前检查有效性

4.避免返回局部变量的地址。

如造成野指针的第三个地址,

既然我们提到了指针使用之前要检查有效性,我们通过什么来检查呢?

assert断言

这时就要用到assert断言了。

assert.h头文件定义了宏assert(),用于在运行时确保程序符合指定条件,如果不符合,就报错终止运行,这个宏常常被称为“断言”。

这个代码的意思就是,验证变量p是否等于NULL,如果不等于NULL,程序继续运行,否则就会终止运行,并且会报出错误信息。

assert()可以接收一个表达式为参数,如果该表达式为真(返回值非0),assert()不会产生任何作用,如果为假(返回值为0),那么assert()就会报错,在屏幕上会打印错误信息,以及包含整个表达式的文件名和行号。

assert()还可以通过定义一个宏NDEBUG来进行关闭

这样他就没有报错了。

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值