【C 指针】自底向上理解C语言指针 01----地址与指针

2023-05-16

Navigator

  • 一、变量---地址---指针
    • 1.1变量存储位置
    • 1.2 地址
    • 1.3 指针与地址
    • 1.4 简单代码理解
    • 1.5 拓展---变量名存储在哪里了
  • 二、指针变量的定义、使用与运算
    • 2.2 指针变量的定义
    • 2.2 使用指针(入门)
    • 2.3 指针变量的运算

指针即地址

指针就是这么简单,又远不止这么简单。
在这里插入图片描述

一、变量—地址—指针

1.1变量存储位置

在程序定义了变量,变量又被存储在了哪里呢?硬盘还是内存条?
在这里插入图片描述

其实都不是,上面最快的内存(40G/s),速度依旧不够快。

答案是缓存(cache):
在这里插入图片描述
而缓存一般是多级缓存,其速度比内存快得多。
在这里插入图片描述

缓存与内存都称为RAM(Random Access Memory),其中:缓存一般都是SRAM,内存为DRAM。
在这里插入图片描述

不同类型的变量会存放在SRAM的不同区域,如堆区、栈区、静态区:

1.栈区(stack)— 由编译器自动分配释放 ,存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。该栈的最大存储是有大小的,该值固定,超过该大小将会造成栈溢出。
2.堆区(heap) — 一般由程序员分配释放, 用来存储数组,结构体,对象等。若程序员不释放,程序结束时可能由OS回收。
3.全局区(静态区)(static)— 存放全局变量、静态数据、常量。程序结束后由系统释放。

1.2 地址

用一个黄色方块表示一位SRAM存储区,一个字节则需要8个小方块,它们共用一根地址线。

一个字节数据读写时,先给地址线高电平(写1),再给数据线相应的电平(看你要写什么了)。
在这里插入图片描述

两个字节读写时:则可以使用非门来选择对哪一个字节进行读写。
在这里插入图片描述
而对多个字节进行读写,则可以使用译码器,使用很少的地址线就可以确定对哪一个字节进行读写,如下图的38译码器,3根地址线可以操作8个字节。
在这里插入图片描述
而16根地址线就可以控制64KB字节的读写:
在这里插入图片描述

1.3 指针与地址

我们来定义简单定义一个变量和指针:

char a = 10;
char* p = &a;

上面:我们定义了一个char型变量a,它的值是10,&a是a的地址(也就是a在SRAM中的存储位置,&是取地址运算符),我们把a的地址(&a)赋值给了p。其中char* pchar *p是一样的,不过在你的代码中要统一才好(看)。

假设我们的存储空间只有16个字节。存储char型变量a需要一个字节空间,假设他存储在第16个字节中,那么他的地址就是1111(二进制),即0xF(十六进制)。
在这里插入图片描述
将a的地址赋值给指针p:char* p=&a。也就是char型指针p的值是0xF,再使用间接运算符就可以获得该地址的值。

间接运算符 * 也称作解引用运算符。
在这里插入图片描述

1.4 简单代码理解

先来点简单的:

#include<stdio.h>

int main()
{
	char a = 10;
	char* p = &a;
	//变量的值
	printf("变量a的值是:%d\n", a);
	//变量的地址
	printf("a的地址是:%p\n",&a);
	//指针的值即地址处存放的值
	printf("指针p指向地址存储的值是:%d\n", *p);
	//指针的值是变量地址
	printf("指针的值(即变量地址)是:%p\n", p);
	//变量的地址处存放的是变量的值
	printf("a的地址存放的值是a的值:%d\n",*(&a));
	//指针的地址
	printf("指针p的地址是:%p\n",&p);
	//指针的地址处存放的是a的地址;
	printf("指针的地址处存放的是a的地址:%p\n",*(&p));
	return 0;
}

输出:

变量a的值是:10
a的地址是:000000A801CFF9C8
指针p指向地址存储的值是:10
指针的值(即变量地址)是:000000A801CFF9C8
a的地址存放的值是a的值:10
指针p的地址是:000000A801CFF9C0
指针的地址处存放的是a的地址:000000A801CFF9C8

注:

  1. char从技术层面讲是(8位)整数类型;
  2. 64(x64)位的电脑,地址则是16位16进制数,32位(x86)则是8为16进制数。
  3. 指针变量本身也是有地址的,这个地址存放的是指针指向变量的地址。

1.5 拓展—变量名存储在哪里了

一个变量通常包含这些要素:

  1. 变量类型;
  2. 变量名;
  3. 变量地址;
  4. 变量的值。
    在这里插入图片描述

变量存储在缓存中,那变量名呢?

在C语言中,变量名存放在符号表(symbol table)中。符号表是一个数据结构,用于保存程序中定义的所有变量、函数等符号的名称、数据类型以及在内存中的地址等信息。在程序编译过程中,编译器会将变量名添加到符号表中,以便在程序执行时能够正确地访问变量。

在程序运行时,变量名不再存在于内存中,只有变量的值和地址才会被存储在内存中。当程序需要访问变量时,编译器会根据变量名在符号表中查找变量的信息,然后使用该信息来生成相应的汇编代码,从而访问变量的值或地址。

二、指针变量的定义、使用与运算

2.2 指针变量的定义

下面两种定义方式都可以
在这里插入图片描述

即:

char* p;
char *p;

不同类型的指针操作不同类型的变量。

定义后最好进行初始化。

如果没有显式初始化指针变量,它的值将是不确定的,也就是一个随机的值。如果你试图在没有初始化指针变量的情况下对其进行解引用操作,程序可能会导致不可预知的结果,甚至可能崩溃。

在某些情况下,如果你还不确定要分配内存,您可能希望在稍后的代码中初始化指针变量。在这种情况下,建议将指针变量初始化为NULL。这样做可以确保指针变量不会被用于解引用操作,直到分配了相应的内存。

int *ptr; // 未初始化的指针变量
int *ptr = NULL; // 初始化为NULL的指针变量

2.2 使用指针(入门)

本文先介绍指针简单的使用,能看懂下面这段就ok了。

int a=5;
int* pa=&a;
 *&a可以理解为*(&a)&a表示取变量 a 的地址(等价于 pa),*(&a)表示取这个地址上的数据(等价于 *pa),绕来绕去,又回到了原点,*&a仍然等价于 a。
 
 &*pa可以理解为&(*pa),*pa表示取得 pa 指向的数据(等价于 a),&(*pa)表示数据的地址(等价于 &a),所以&*pa等价于 pa。

以及这张图:
在这里插入图片描述

2.3 指针变量的运算

指针存储的是它指向的变量的地址,是一个数(我们通常使用%p来输出,即左侧带补0的十六进制),因此也可以做一些运算,如加减、比较等。

定义如下变量并赋值:

	char a = 10;
	int arr[4] = { 5,2,3,4 };
	int b = 20;
	char* pa = &a;
	int* pb = &b;

先打印a、b以及地址:

	printf("a = %d\n",a);
	printf("*pa = %d\n\n", *pa);

	printf("b = %d\n",b );
	printf("*pb = %d\n\n", *pb);

	printf("&a = %p\n",&a );
	printf("pa = %p\n\n",pa );

	printf("&b = %p\n", &b);
	printf("pb = %p\n\n", pb);

这似乎没有什么问题,输出:

a = 10
*pa = 10

b = 20
*pb = 20

&a = 00000047880FF770
pa = 00000047880FF770

&b = 00000047880FF774
pb = 00000047880FF774

再来对指针变量进行加法运算:

	printf("pa = %p\n",pa);
	printf("pa+1 = %p\n", ++pa);
	printf("pb = %p\n",pb);
	printf("pb+1 = %p\n\n", ++pb);

输出:

pa = 00000047880FF770
pa+1 = 00000047880FF771
pb = 00000047880FF774
pb+1 = 00000047880FF778

这里可以看到,指针变量pa加1之后,结果确实加1了,而pb加1之后,输出值却增加了4

这其实与指针变量的类型有关:指针变量pa是char型变量,加1就加1个字节;而指针变量pb是int型变量,加1则会加4个字节,

也就是说加1指的是加一个存储单元,这就是必须声明指针所指向对象类型的原因之一。


指针变量的比较运算:

	printf("pa!=pb的结果是:%d\n",pa!=pb);

输出:

pa!=pb的结果是:1

数组:
数组的一些操作,下篇文章完整介绍:

注意:* arr,*(arr+1),*arr+1

	printf("*arr = %d = arr[0] = %d\n",*arr,arr[0]);
	printf("*(arr+1) = %d = arr[1]=%d\n", *(arr+1),arr[1]);
	printf("*arr+1 = %d = arr[0]+1 = %d\n\n",*arr+1,arr[0]+1);

输出:

*arr = 5 = arr[0] = 5
*(arr+1) = 2 = arr[1]=2
*arr+1 = 6 = arr[0]+1 = 6

————————————————
参考视频1:https://www.bilibili.com/video/BV1o8411T7K5?t=5.9
参考视频2:https://www.bilibili.com/video/BV1Gi4y1w7yG?t=105.7

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C 指针】自底向上理解C语言指针 01----地址与指针 的相关文章

随机推荐