C语言指针(上)

目录

1.前言

 2.内存和地址

3.指针变量和地址 

3.1取地址操作符 “&”

3.2指针变量

3.3解引用操作符“*”

4.指针变量的大小

5.指针变量类型的意义

5.1指针的解引用

5.2指针加减整数

5.3void*类型指针 

6.后记


1.前言

指针,当我第一次听到它时,我的眼前不禁呈现出一片璀璨的星空,北面最亮的几颗组成一把勺子,勺柄指向我未曾到过的远方——同学们我们今天来一起学习指针这一章节。原来是上课,走神了,那没事了。

 2.内存和地址

  我们知道CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中。而电脑电脑上内存是一般是8GB/16GB/32GB等,那这些内存空间如何高效的管理呢?
  我们常说C语言的发明是为了解决生活中的问题。那么请问一个国家是如何管理它的领土的呢?当然是将一整块领土划分成一个个省,再把省划分成市,再把市划分成县等。
  同理,要想高效管理内存空间,其实也是 把内存划分为⼀个个的内存单元,每个内存单元的大小取1个字节。每个字节都有它所对应的编号,计算机中我们也把编号称之为地址,C语言就把这个地址称之为指针。                                 内存单元编号==地址==指针

3.指针变量和地址 

3.1取地址操作符 “&”

C语言中创建变量是需要向内存中申请空间的。而为存放变量而创建的空间就有对应的地址。假设我们创建一个整形变量a,我们想得到a的地址,并把它打印出来看看。这就需要我们的取地址专用操作符——“&”。事不宜迟,我们先来波代码:

#include<stdio.h>
int main()
{
	int a = 5;
	printf("%p", &a);//使用占位符%p打印地址
	return 0;
}

看到打印出来的结果,你是否产生了一些疑问?每一个字节都有对应的地址,而一个整形变量的大小是四个字节,但为啥只打印了一个地址呢?我们不妨对代码进行调试,打开内存看看:

  如图,变量a的确有4个字节,每个字节对应一个指针(编号),只是&a取出的是第一个字节(编号最小的字节)的地址。

    那么就算整形变量有四个字节,我们只要知道了第一个字节的地址,顺藤摸瓜地访问四个字节的数据也是可以达到目的的。


3.2指针变量

当我们想把一个表达式的值储存起来时,就会创建一个对应类型的变量,比如 int a = 3+2。 类似地, 我们通过取地址操作符(&)拿到的地址也是⼀个数值,当我们想把它储存起来时,也会创建一个对应类型的变量, C语言就把这类储存地址的变量称之为指针变量。
   例如:
#include<stdio.h>
int main()
{
	int a = 5;
	int* p = &a;
	//取出a的地址放在指针变量p中
	return 0;
}

  这里取出的是整形变量a的地址,那么对应地就创建整形指针int *来接收。

  我们也可以换一种方式来理解指针变量p。变量名p前面跟着*”,说明p是指针,*前面是跟着的int,说明指针变量p指向的是整形类型的对象。那么请问,我们该如何理解char**pa呢?

  *说明pa是指针变量, 前面紧跟着的“char*”说明pa指向的是字符指针“char*”类型的对象                    

3.3解引用操作符“*

  我们将地址储存起来,未来要是想使用,用该如何呢?这里我们就需要一个起到通过地址(指针)找到地址(指针) 指向的对象作用的操作符——解引用操作符 *了。

  例如:

int main()
{
	int a = 10;
	int* p1 = &a;//将a的地址存放进指针变量p1中
	*p1 = 0;
	//通过解引用操作符*,找到a的值,并值为0
	return 0;
}

 *p1的意思就是通过p1中存放的地址找到指向的空间,这里的*p1就等价于a,所以*p1=0;就是将a赋值为0。

  说到这里不知道各位是否会产生这样的想法:明明可以直接将a赋值为0,为啥非要使用指针变量将a的地址储存起来,再解引用后赋值呢?这不脱裤子放屁,多此一举吗?我的评价是:确实多此一举,但是存在即合理,倘若我们把它用到合适的位置上,那么妙不可言只是基本操作。

4.指针变量的大小

 上面我们提到,指针变量是用来存放编号(指针)的。那么显然指针变量的大小取决于存放编号的所需空间的大小。那么内存是如何进行编号的呢?我们知道CPU和内存之间是有大量的数据交互的,而数据交互就要用到“线”。而我们今天的主角就是其中的一员——地址总线

 我们可以简单理解,32位机器有32根地址总线,每根线只有两种状态——0,1(电脉冲有无),那么⼀根线,就能表示2种含义,2根线就能表是4种含 义,依次类推。32根地址线,就能表示2^32种含义,每⼀种含义都代表⼀个地址。而将地址线发出的电信号转换成数字信号后是1或者0,且储存一个1或者0所需要的空间大小就是一个bit位。那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4 个字节才能存储。所以32位平台下,一个指针变量的大小就是4个字节。

同理64位机器,假设有64根地址线,⼀个地址就是64个⼆进制位组成的⼆进制序列,存储起来就需要8个字节的空间。故而,64位平台下,一个指针变量的大小就是8个字节

值的注意的是:指针变量的大小和类型是无关的,只要指针类型的变量,在相同的平台下,大小都是相同的。 

5.指针变量类型的意义

5.1指针的解引用

  上面我们提到指针变量的类型是要和指针所指向变量的类型相对应的,那么当我们来近距离看看为什么两者的类型要先对应。

 我们创建一个整形变量n并赋值一个十六进制数,当我们通过int*类型的指针pi将其赋值为0时,我们发现内存中n的值全部变成0

我们将pi改成char*类型 ,并将&n强制类型转化成char*,当我们再通过pi将n赋值时,我们发现此时的n只是第一个字节的值变成了0。

由此我们可以得到以下结论:

  指针的类型决定了对指针解引用的时候有多大的权限(⼀次能操作几个字节)。 如上述的 char* 的指针解引用只能访问⼀个字节,而 int* 的指针的解引用能访问四个字节

5.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后跳过1个字节, int* 类型的指针变量+1后跳过了4个字节。

所以我们可以得出: 指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)

5.3void*类型指针 

 在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为⽆具体类型的指针(或者叫泛型指
针),这种类型的指针就像一个万能容器,可以用来存放任意类型地址。也正因为且特殊性, void* 类型的指针不能直接进行指针的加减整数和解引用的运算。而需要强制类型转化成我们所需要的类型的指针。

如图,我们使用void*的指针p1接收a的地址编译器没有报错,而使用 char*的地址接收a的地址却不兼容。

这里我们可以看到, void* 类型的指针可以接收不同类型的地址,但是无法直接进行指针运算。

6.后记

总算写完了!不觉得抬头又是满天繁星,天空中的最亮的那个指针,正指向着我未曾到过的远方,承载着我对未来的期许....不废话了,赶紧洗洗睡。

 

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值