C语言指针初步了解

内存和地址

众所周知,指针在c语言中是最重要的一个部分,它涉及到传参,访问等操作。说到这些就不得不提到一个概念内存和地址。

在讲内存和地址之前,我们先来讲一个生活中的例子。张三在大学宿舍里点了一份外卖,外卖员给他送外卖,到了地点后。外卖员要找他,宿舍里有500间房间,这个时候外卖员不可能挨个挨个敲门去找张三这样的话既费时又费力。这个时候就要用到门牌号了,门牌号可以帮助外卖员很快找到张三的房间并及时把外卖交给张三。

这里的门牌号就相当于电脑中地址。

在计算机中,CPU(中央处理器)在处理数据时,需要的数据是在内存中读取的,处理后的数据也会放回内存中。

我们在买电脑的时候电脑内存通常有8GB/16GB/32GB 等,那这些内存空间如何⾼效的管理呢?

其实内存里面是分为⼀个个的内存单元,每个内存单元的大小取1个字节。

其中,每个内存单元,相当于⼀间学⽣宿舍,⼀个字节空间⾥⾯能放8个比特位,就好⽐学生们住的八人间,每个⼈是⼀个⽐特位。每个内存单元也都有⼀个编号(这个编号就相当于宿舍房间的门牌号),有了这个内存单元的编号,CPU就可以快速找到⼀个内存空间。生活中我们把⻔牌号也叫地址,在计算机中我们

把内存单元的编号也称为地址。C语⾔中给地址起 了新的名字叫:指针。

所以我们可以理解为:

内存单元的编号 == 地址 == 指针

在计算机中,电脑储存数据是2进制编码的形式,1个字节相当于8个比特位,一个比特位可以存一个二进制的0或1

常见的存储单位:

bit - 比特位

Byte - 字节  KB

MB

GB

TB

PB

1Byte = 8bit

1KB = 1024Byte

1MB = 1024KB

1GB = 1024MB

1TB = 1024GB

1PB = 1024TB

1指针变量和地址

1.1取地址操作符(&)

在C语⾔中创建变量其实就是向内存申请空间
#include <stdio.h>
int main()
{
	int a = 0;
	return 0;
}

在上面的代码中我们可以看见,当int a = 0;这条语句执行了后,系统向内存申请了4个字节来存放变量a的数据。每个字节都有自己的地址。

4个字节的地址为:

0x00B3FD28

0x00B3FD29

0x00B3FD2A

0x00B3FD2B

那么要如何才能获得这些地址呢,这时我们就要用到c语言中的取地址操作符&。

#include <stdio.h>
int main()
{
	int a = 0;
	printf("%p", &a); //&a取出a的地址
	return 0;
}

运行上面代码后就可以打印出a在内存中的地址了,可以看到a此时的地址00B3FB6C;

00B3FB6C是变量a在内存中最小的地址,虽然a在内存中占4个字节,但是我们只需要知道它最小的地址后就行了,因为变量的地址在内存中是连续的,我们知道了它最小的地址后其它的地址就可以顺藤摸瓜的找出来了。

1.2指针变量和解引用操作符(*)

1.2.1指针变量

指针变量顾名思义就是存放指针的变量,也可以说存放地址的变量

#include <stdio.h>
int main()
{
	int a = 0;
	int* p = &a; //把a的地址存放在指针变量p中
	printf("%p", p);
	return 0;
}

如和理解 int* p = &a这条语句呢?

我们可以把  p 理解为一个变量,*表示p变量是指针是存放地址的,int表示存放的地址指向的是一个int类型,也可以直接把int*理解成一个类型,用来存放int类型的地址

同理,如果我们有一个字符变量ch,要存放它的地址我们可以这样写:

#include <stdio.h>
int main()
{
    char ch = 'a';
    char* p = &ch; //把a的地址存放在指针变量p中
    printf("%p", p);
    return 0;
}

1.2.2解引用操作符

那么我们把变量存起来是干什么呢?

当然是要使用啦!

在c语言中我们拿到了地址就能通过地址,对地址指向的变量进行修改

要修改就得用到c语言中的解引用操作符*

#include <stdio.h>
int main()
{
	int a = 20;
	int* p = &a;
	printf("%d\n", a);
	*p = 30;
	printf("%d\n", a);
	return 0;
}

运行代码后我们可以看见,在*p = 30 这条语句执行后,a的变量从原来的20 变成了30。

但有一点要切记,*p = 30 不要写出p = 30,如果写出p = 30,相当于把30这数字作为地址存放到指针变量p中去了,而a中的值并不会改变。

1.3指针变量的大小

在了解指针变量的大小之前我们先运行一段代码。

#include <stdio.h>
int main()
{
	printf("%zd\n", sizeof(char*));
	printf("%zd\n", sizeof(short*));
	printf("%zd\n", sizeof(int*));
	printf("%zd\n", sizeof(double*));
	return 0;
}
我们可以看见,指针变量的大小与指向的类型无关,都是八个字节。那么指针变量一定就是8个字节吗?
当我们把平台改成32位的试一下
我们可以看见虽然指针大小与指向的类型无关,但与平台有关的
32位平台下指针变量占4个字节
64位平台下指针变量占8个字节

2指针变量类型的意义

在上面我们发现了指针大小与指向的类型无关,那么指针的类型有什么意义呢?

2.1解引用

我们先来调试2段代码来观察一下,他们在内存中的变化

代码1:

#include <stdio.h>
int main()
{
	int n = 0x55667788;
	int* p = &n;
	*p = 0;
	return 0;
}

代码2:

#include <stdio.h>
int main()
{
	int n = 0x55667788;
	char* p = &n;
	*p = 0;
	return 0;
}
从上面代码调试效果我们可以看出,第7之前的代码运行后没有任何差别,都是占用4个字节来存储数据。
但是当第7行代码运行后就出现了差异,代码1中的*p = 0;是实实在在的把n里面4个字节的内容全部改成了0,但是代码2中的*p = 0却只改变了n里面一个字节的内容。
为什么呢?
原因就出在第六行代码上,代码1中的第六行代码是创建了一个int 类型的指针变量,代码2中的第六行代码是创建了一个char类型的指针变量,我们都知道char类型在c语言中占1个字节,int类型占4个字。在解引用的时候操作系统只会根据指针变量的类型来决定对多少个字节来进行操作。

2.2指针+-整数

我们先来运行一段代码

#include <stdio.h>
int main()
{
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;
	printf("%p\n", &n);
	printf("%p\n", pc);
	printf("%p\n", pc + 1);
	printf("%p\n", pi);
	printf("%p\n", pi + 1);
	return 0;
}

我们可以发现,char*在+1后跳过了一个字节,而int*在+1后跳过了4个字节。指针变量+1跳过多少个字节也与他们的类型有关。

2.3void* 指针

在指针类型中有⼀种特殊的类型是 void* 类型,可以理解为无具体类型的指针或者叫泛型指针

这种指针可以接受任何类型的地址,但是不能进行+-整数和解引用操作。

3 const修饰指针

变量是可以修改的,也可以通过把地址传给指针变量,通过解引用来修改变量,如果我们想给变量加上一些限制,不能被修改,那要怎么做呢?

3.1const修饰变量

c语言中提供了一个关键字const可以修饰变量。

#include <stdio.h>
int main()
{
	int a = 10;
	a = 20; // 可修改变量
	const int b = 10;
	b = 20; //不可修改变量
	return 0;
}

被const修饰后的变量属性就和常量一样不能修改了但本质上还是一种变量,只是在语法上添加了限制。

但是如果我们再创建一个指针变量,用来存放b的地址,通过解引用操作来修改b的值呢?

#include <stdio.h>
int main()
{
	int a = 10;
	a = 20; 
	const int b = 10;
	int* p = &b;
	*p = 20;
	printf("a = %d,b = %d", a, b);
	return 0;
}

我们发现这种方法可以打破语法规则,修改b里面的值。但是这样的话不就相当于打破了const的限制吗?那么怎么才能让p拿到b的地址去不能修改b里面的值呢?

3.2const修饰指针变量

const的位置可以放在*的左边也可以放在*的右边,但是意义是完全不一样的。

这里我们发现,const在左边的时候,是不能进行*p = 20也就是解引用修改操作;

但是在右边就可以进行解引用修改。

这里我们发现,const在左边的时候,可以给p重新取一个地址,但是cons在右边就不行。

综合以上结论,我们可以得出const在左边的时候是限制的*p但是p本身不受限制,如果const在右边限制的是p本身,*p不受限制。

当然我们也可以左右两边都加上const,这样的话指针变量p既不能重新赋值,也不能解引用修改值。

4.野指针

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

4.1野指针成因

指针未初始化
#include <stdio.h>
int main()
{ 
 int *p;
 *p = 30;
 return 0;
}
指针越界访问
#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;
}
指针指向的空间释放
#include <stdio.h>
int* test()
{
	int n = 7;
	return &n;
}
int main()
{
	int* p = test();
	printf("%d\n", *p);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值