【C语言】深入浅出理解指针及内存与指针的关系(详细讲解+代码展示)上

简介: 笔记

概述


指针,是C语言中的一个重要概念及其特点,也是掌握C语言比较困难的部分。指针也就是内存地址,指针变量是用来存放内存地址的变量,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的储存空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。


为了了解指针,我们首先需要讲解一下内存的概念。


内存

内存含义

存储器:在计算机的组成中,用来存储程序和数据,辅助CPU进行运算处理的重要部分。

内存:内部存贮器,暂存程序/数据——掉电丢失 SRAM、DRAM、DDR、DDR2、DDR3。

外存:外部存储器,长时间保存程序/数据—掉电不丢ROM、ERRROM、FLASH(NAND、NOR)、硬盘、光盘。


4.jpeg


内存是沟通CPU与硬盘的桥梁


内存作用:

暂存放CPU中的运算数据

暂存与硬盘等外部存储器交换的数据

5.jpeg


物理存储器和存储地址空间

有关内存的两个概念:物理存储器和存储地址空间。


物理存储器:实际存在的具体存储器芯片。

主板上装插的内存条

显示卡上的显示RAM芯片

各种适配卡上的RAM芯片和ROM芯片

我们所有的数据都存在内存中,对每一个物理存储单元,分配一个单元,我们称之为编码,可以根据分配的号码找到相应的存储单元,也称为寻址 。


存储地址空间:对存储器编码的范围。

编码:对每个物理存储单元(一个字节)分配一个号码 。

寻址:可以根据分配的号码找到相应的存储单元,完成数据的读写 。

6.jpeg


内存地址

在这里,我们将内存抽象成一个很大很大的一维字符数组。编码就是对内存的每一个字节去分配一个32位或64位的编号(位数与自己的处理器有关)。儿这个内存编号我们称其为内存地址。       内存中的每一个数据都会分配相应的地址,例如:char:占一个字节,分配一个地址 。int:占四个字节分配四个地址等。

7.png



指针和指针变量

内存区中的每一个字节都是有一个编号的,这个编号就是“地址”。如果我们在程序中定义了一个变量,在对这个程序进行编译或者运行的时候,系统会自动给这个变量分配内存单元,确定其地址。


我们将要学习的指针就是内存单元的编号,而指针变量就是存放地址的一个变量。我们平时通常会把指针变量称作指针,但是指针变量与指针的含义是完全不同的。

8.png



从这张图中就可以看出,b_point是指针,其存放了变量b的内存地址,也就是指针变量。


指针基础知识

上面我们了解完了内存以及内存和指针关系之后,那么接下来我们就来开始指针的学习吧!


指针变量的定义和使用

首先我们不要将指针想象的那么特殊,指针就是是一种数据类型,而指针变量也是一种变量,指针变量只想谁,就把谁的地址赋值给指针变量。我们使用“ * ”操作符操作指针变量指向的内存空间。而我们平时使用指针,主要是因为使用指针往往可以生成更高效、更紧凑的代码。


声明一个指针

上面说到指针就是一个变量所以,指针的声明方式与一般的变量声明方式没太大区别:

int *p ;        // 声明一个 int 类型的指针 p
char *p ;      // 声明一个 char 类型的指针 p
int *arr[10]   // 声明一个指针数组,该数组有10个元素,其中每个元素都是一个指针。
int (*arr)[10] // 声明一个数组指针,该指针指向一个 int 类型的一维数组
int **p;       // 声明一个指针 p ,该指针指向一个 int 类型的指针,也就是我们的二级指针。

初始化一个指针

声明一个指针变量并不会自动分配任何内存。在对指针进行间接访问之前,指针必须进行初始化,或是使他指向现有的内存,或者是给他动态分配内存,否则我们并不知道指针指向哪儿,这将是一个很严重的问题,也是我们后面所提到的野指针。所以我们在定义指针后一定要进行初始化,而初始化操作具体如下


方法1:使指针指向现有的内存  

int n = 1;
int *p = &n;  // 指针 p 被初始化,指向变量 x ,其中取地址符 & 用于产生操作数内存地址

方法2:动态分配内存给指针

int *p ;
p = (int *)malloc(sizeof(int) * 10) ;    // malloc 函数用于动态分配内存
free(p) ;    // free 函数用于释放一块已经分配的内存,常与 malloc 函数一起使用。

总结:

#include <stdio.h>
int main()
{
  int a = 0;
  char b = 100;
  printf("%p, %p\n", &a, &b); //输出a, b的地址
  //int * 代表是一种数据类型,int*指针类型,p才是变量名
  int *p;
  p = &a;//将a的地址赋值给变量p,p也是一个变量,值是一个内存地址编号
  printf("%d\n", *p);//p指向了a的地址,*p就是a的值
  char *p1 = &b;
  printf("%c\n", *p1);//*p1指向了b的地址,*p1就是b的值
  return 0;
}

附: &可以取得一个变量在内存中的地址。但是不能取寄存器变量,因为寄存器变量不在内存里,而在CPU里面,所以是没有地址的。

所以这里将上面所讲的部分总结为代码,也希望大家都可以去看懂并理解这些内容。

#include <stdio.h>
int main()
{
  int a = 0 ;
  int b = 11 ;
  int *p = &a ;
  *p = 100 ;
  printf("a = %d, *p = %d\n", a, *p) ;
  p = &b;
  *p = 22;
  printf("b = %d, *p = %d\n", b, *p) ;
}

运行结果:9.png


指针大小

测量指针大小时我们需要使用sizeof()。


sizeof()是一个关键字,它是一个编译时运算符,用于判断变量或数据类型的字节大小。


sizeof 运算符可用于获取类、结构、共用体和其他用户自定义数据类型的大小。其使用语法如下:


sizeof (data type)

其中,data type 是要计算大小的数据类型,其实包括类、结构、共用体以及其他用户自定义数据类型。


并且sizeof()测的是指针变量指向存储地址的大小,在32位平台,所有的指针(地址)都是32位(4字节),同理在64位平台,所有的指针(地址)都是64位(8字节) 。

#include <stdio.h>
int main()
{
  int *p1;
  int **p2;
  char *p3;
  char **p4;
  printf("sizeof(p1) = %d\n", sizeof(p1));
  printf("sizeof(p2) = %d\n", sizeof(p2));
  printf("sizeof(p3) = %d\n", sizeof(p3));
  printf("sizeof(p4) = %d\n", sizeof(p4));
  printf("sizeof(double *) = %d\n", sizeof(double *));
}

运行结果10.png

其中我们可以看出,我们在定义了多级指针之后,通过输出发现,其内存大小是相同的,所以内存大小是不受指针级数的影响。


相关文章
|
27天前
|
存储 C语言
指针和动态内存分配
指针和动态内存分配
78 0
|
7天前
|
存储 C语言
【C语言基础】一篇文章搞懂指针的基本使用
本文介绍了指针的概念及其在编程中的应用。指针本质上是内存地址,通过指针变量存储并间接访问内存中的值。定义指针变量的基本格式为 `基类型 *指针变量名`。取地址操作符`&`用于获取变量地址,取值操作符`*`用于获取地址对应的数据。指针的应用场景包括传递变量地址以实现在函数间修改值,以及通过对指针进行偏移来访问数组元素等。此外,还介绍了如何使用`malloc`动态申请堆内存,并需手动释放。
|
10天前
|
存储 算法 C语言
数据结构基础详解(C语言):单链表_定义_初始化_插入_删除_查找_建立操作_纯c语言代码注释讲解
本文详细介绍了单链表的理论知识,涵盖单链表的定义、优点与缺点,并通过示例代码讲解了单链表的初始化、插入、删除、查找等核心操作。文中还具体分析了按位序插入、指定节点前后插入、按位序删除及按值查找等算法实现,并提供了尾插法和头插法建立单链表的方法,帮助读者深入理解单链表的基本原理与应用技巧。
|
10天前
|
存储 C语言 C++
数据结构基础详解(C语言) 顺序表:顺序表静态分配和动态分配增删改查基本操作的基本介绍及c语言代码实现
本文介绍了顺序表的定义及其在C/C++中的实现方法。顺序表通过连续存储空间实现线性表,使逻辑上相邻的元素在物理位置上也相邻。文章详细描述了静态分配与动态分配两种方式下的顺序表定义、初始化、插入、删除、查找等基本操作,并提供了具体代码示例。静态分配方式下顺序表的长度固定,而动态分配则可根据需求调整大小。此外,还总结了顺序表的优点,如随机访问效率高、存储密度大,以及缺点,如扩展不便和插入删除操作成本高等特点。
|
10天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
10天前
|
存储 C语言
数据结构基础详解(C语言): 栈与队列的详解附完整代码
栈是一种仅允许在一端进行插入和删除操作的线性表,常用于解决括号匹配、函数调用等问题。栈分为顺序栈和链栈,顺序栈使用数组存储,链栈基于单链表实现。栈的主要操作包括初始化、销毁、入栈、出栈等。栈的应用广泛,如表达式求值、递归等场景。栈的顺序存储结构由数组和栈顶指针构成,链栈则基于单链表的头插法实现。
|
10天前
|
存储 算法 C语言
C语言手撕实战代码_二叉排序树(二叉搜索树)_构建_删除_插入操作详解
这份二叉排序树习题集涵盖了二叉搜索树(BST)的基本操作,包括构建、查找、删除等核心功能。通过多个具体示例,如构建BST、查找节点所在层数、删除特定节点及查找小于某个关键字的所有节点等,帮助读者深入理解二叉排序树的工作原理与应用技巧。此外,还介绍了如何将一棵二叉树分解为两棵满足特定条件的BST,以及删除所有关键字小于指定值的节点等高级操作。每个题目均配有详细解释与代码实现,便于学习与实践。
|
10天前
|
存储 算法 C语言
C语言手撕实战代码_二叉树_构造二叉树_层序遍历二叉树_二叉树深度的超详细代码实现
这段代码和文本介绍了一系列二叉树相关的问题及其解决方案。其中包括根据前序和中序序列构建二叉树、通过层次遍历序列和中序序列创建二叉树、计算二叉树节点数量、叶子节点数量、度为1的节点数量、二叉树高度、特定节点子树深度、判断两棵树是否相似、将叶子节点链接成双向链表、计算算术表达式的值、判断是否为完全二叉树以及求二叉树的最大宽度等。每道题目均提供了详细的算法思路及相应的C/C++代码实现,帮助读者理解和掌握二叉树的基本操作与应用。
|
10天前
|
存储 算法 C语言
C语言手撕实战代码_循环单链表和循环双链表
本文档详细介绍了用C语言实现循环单链表和循环双链表的相关算法。包括循环单链表的建立、逆转、左移、拆分及合并等操作;以及双链表的建立、遍历、排序和循环双链表的重组。通过具体示例和代码片段,展示了每种算法的实现思路与步骤,帮助读者深入理解并掌握这些数据结构的基本操作方法。
|
10天前
|
算法 C语言 开发者
C语言手撕实战代码_单链表
本文档详细介绍了使用C语言实现单链表的各种基本操作和经典算法。内容涵盖单链表的构建、插入、查找、合并及特殊操作,如头插法和尾插法构建单链表、插入元素、查找倒数第m个节点、合并两个有序链表等。每部分均配有详细的代码示例和注释,帮助读者更好地理解和掌握单链表的编程技巧。此外,还提供了判断子链、查找公共后缀等进阶题目,适合初学者和有一定基础的开发者学习参考。