【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语言
【c语言】指针就该这么学(1)
本文详细介绍了C语言中的指针概念及其基本操作。首先通过生活中的例子解释了指针的概念,即内存地址。接着,文章逐步讲解了指针变量的定义、取地址操作符`&`、解引用操作符`*`、指针变量的大小以及不同类型的指针变量的意义。此外,还介绍了`const`修饰符在指针中的应用,指针的运算(包括指针加减整数、指针相减和指针的大小比较),以及野指针的概念和如何规避野指针。最后,通过具体的代码示例帮助读者更好地理解和掌握指针的使用方法。
45 0
|
10天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
33 4
|
23天前
|
存储 JavaScript 前端开发
如何优化代码以避免闭包引起的内存泄露
本文介绍了闭包引起内存泄露的原因,并提供了几种优化代码的策略,帮助开发者有效避免内存泄露问题,提升应用性能。
|
23天前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
35 3
|
25天前
|
存储 Rust C#
内存指针解引用
【10月更文挑战第14天】
30 1
|
26天前
|
C语言
【c语言】指针就该这么学(3)
本文介绍了C语言中的函数指针、typedef关键字及函数指针数组的概念与应用。首先讲解了函数指针的创建与使用,接着通过typedef简化复杂类型定义,最后探讨了函数指针数组及其在转移表中的应用,通过实例展示了如何利用这些特性实现更简洁高效的代码。
15 2
|
26天前
|
C语言
如何避免 C 语言中的野指针问题?
在C语言中,野指针是指向未知内存地址的指针,可能引发程序崩溃或数据损坏。避免野指针的方法包括:初始化指针为NULL、使用完毕后将指针置为NULL、检查指针是否为空以及合理管理动态分配的内存。
|
26天前
|
C语言
C语言:哪些情况下会出现野指针
C语言中,野指针是指指向未知地址的指针,通常由以下情况产生:1) 指针被声明但未初始化;2) 指针指向的内存已被释放或重新分配;3) 指针指向局部变量,而该变量已超出作用域。使用野指针可能导致程序崩溃或不可预测的行为。
|
1月前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
25天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
21 0