当前位置: 首页 > news >正文

C语言-指针

C语言-指针

1. 准备知识

1.1 内存地址

  • 字节:字节是内存的容量单位,英文称为 byte,一个字节有8位,即 1byte = 8bits
  • 地址:系统为了便于区分每一个字节而对它们逐一进行的编号,称为内存地址,简称地址。

img

1.2 基地址

  • 单字节数据:对于单字节数据而言,其地址就是其字节编号。
  • 多字节数据:对于多字节数据而言,期地址是其所有字节中编号最小的那个,称为基地址。

img

1.3 取址符

  • 每个变量都是一块内存,都可以通过取址符 & 获取其地址
  • 例如:
int a = 100;
printf("整型变量 a 的地址是: %p\n", &a);

char c = 'x';
printf("字符变量 c 的地址是: %p\n", &c);

double f = 3.14;
printf("浮点变量 f 的地址是: %p\n", &f);
  • 注意:
    • 虽然不同的变量的尺寸是不同的,但是他们的地址的尺寸确实一样的。
    • 不同的地址虽然形式上看起来是一样的,但由于他们代表的内存尺寸和类型都不同,因此它们在逻辑上是严格区分的。

2. 指针入门

2.1 指针的概念

由于翻译的问题,以及口语表达的习惯,在日常表述中,指针在不同的场合会代表以下几个含义:

  • 指地址
    • 比如变量a的地址 &a,这是一个地址当然也是一个指针
    • 我们可以说指针 &a 指向变量 a。
  • 指指针变量
    • 比如 int *p; 此处变量p是指针变量,又常被简称指针。

2.2 指针的定义

int    *p1; // 用于存储 int  型数据的地址,p1 被称为 int  型指针,或称整型指针
char   *p2; // 用于存储 char 型数据的地址,p2 被称为 char 型指针,或称字符指针
double *p3; // 用于存储double型数据的地址,p3 被称为 double 型指针

2.3 指针的赋值

赋给指针的地址,类型需跟指针的类型相匹配。

int a = 100;
p1 = &a; // 将一个整型地址,赋值给整型指针p1

char c = 'x';
p2 = &c; // 将一个字符地址,赋值给字符指针p2

double f = 3.14;
p3 = &f; // 将一个浮点地址,赋值给浮点指针p3

2.4 指针的索引

所谓索引,指的是通过指针,取得其指向的目标

*p1 = 200; // 将 p1 指向的目标(即a)修改为200,等价于 a = 200;
*p2 = 'y'; // 将 p2 指向的目标(即c)修改为'y',等价于 c = 'y';
*p3 = 6.6; // 将 p3 指向的目标(即f)修改为6.6,等价于 f = 6.6;
  • 指针的尺寸
    • 指针尺寸指的是指针所占内存的字节数
    • 指针所占内存,取决于地址的长度,而地址的长度则取决于系统寻址范围,即字长
    • 结论:指针尺寸只跟系统的字长有关,跟具体的指针的类型无关

3. 特殊指针

3.1 野指针

  • 概念:指向一块未知区域的指针,被称为野指针。野指针是危险的。

img

  • 危害:
    1. 引用野指针,相当于访问了非法的内存,常常会导致段错误(segmentation fault)
    2. 引用野指针,可能会破坏系统的关键数据,导致系统崩溃等严重后果
  • 产生原因:
    1. 指针定义之后,未初始化
    2. 指针所指向的内存,被系统回收
    3. 指针越界
  • 如何防止:
    1. 指针定义时,及时初始化
    2. 绝不引用已被系统回收的内存
    3. 确认所申请的内存边界,谨防越界

3.2 空指针

很多情况下,我们不可避免地会遇到野指针,比如刚定义的指针无法立即为其分配一块恰当的内存,又或者指针所指向的内存被释放了等等。一般的做法就是将这些危险的野指针指向一块确定的内存,比如零地址内存。

img

  • 概念:空指针即保存了零地址的指针,亦即指向零地址的指针。
  • 示例:
// 1,刚定义的指针,让其指向零地址以确保安全:
char *p1 = NULL;
int  *p2 = NULL;

// 2,被释放了内存的指针,让其指向零地址以确保安全:
char *p3 = malloc(100); // a. 让 p3 指向一块大小为100个字节的内存
free(p3);               // b. 释放这块内存,此时 p3 相当于指向了一块非法内存
p3 = NULL;              // c. 让 p3 指向零地址

4. 指针运算

  • 指针加法意味着地址向上移动若干个目标
  • 指针减法意味着地址向下移动若干个目标
  • 示例:
int  a = 100;
int *p = &a; // 指针 p 指向整型变量 a

int *k1 = p + 2; // 向上移动 2 个目标(2个int型数据)
int *k2 = p - 3; // 向下移动 3 个目标(3个int型数据)

img

5. 指针语法解剖

  • 任意的指针,不管有多复杂,其定义都由两部分组成。
    • 第1部分:指针所指向的数据类型,可以是任意的类型
    • 第2部分:指针的名字

img

  • 示例:
char   (*p1);            // 第2部分:*p1; 第1部分:char; 
char  *(*p2);            // 第2部分:*p2; 第1部分:char *; 
char **(*p3);            // 第2部分:*p3; 第1部分:char **; 
char   (*p4)[3];         // 第2部分:*p4; 第1部分:char [3]; 
char   (*p5)(int, char); // 第2部分:*p5; 第1部分:char (int, float); 
  • 注解:
  1. 上述示例中,p1、p2、p3、p4、p5本质上并无区别,它们均是指针
  2. 上述示例中,p1、p2、p3、p4、p5唯一的不同,是它们所指向的数据类型不同
  3. 第1部分的声明语句,如果由多个单词组成,C语言规定需要将其拆散写到第2部分的两边

6. 特殊指针

6.1 char型指针

char型指针实质上跟别的类型的指针并无本质区别,但由于C语言中的字符串以字符数组的方式存储,而数组在大多数场合又会表现为指针,因此字符串在绝大多数场合就表现为char型指针。

  • 定义:
char *p = "abcd";

6.2 多级指针

  • 如果一个指针变量 p1 存储的地址,是另一个普通变量 a 的地址,那么称 p1 为一级指针
  • 如果一个指针变量 p2 存储的地址,是指针变量 p1 的地址,那么称 p2 为二级指针
  • 如果一个指针变量 p3 存储的地址,是指针变量 p2 的地址,那么称 p3 为三级指针
  • 以此类推,p2、p3等指针被称为多级指针
  • 示例:
int a = 100;
int   *p1 = &a;  // 一级指针,指向普通变量
int  **p2 = &p1; // 二级指针,指向一级指针
int ***p3 = &p2; // 三级指针,指向二级指针

6.3 void型指针

  • 概念:无法明确指针所指向的数据类型时,可以将指针定义为 void 型指针
  • 要点:
    1. void 型指针无法直接索引目标,必须将其转换为一种具体类型的指针方可索引目标
    2. void 型指针无法进行加减法运算
  • void关键字的三个作用:
    1. 修饰指针,表示指针指向一个类型未知的数据。
    2. 修饰函数参数列表,表示函数不接收任何参数。(预习)int main(void)
    3. 修饰函数返回类型,表示函数不返回任何数据。(预习)void func(int)
  • 示例:
// 指针 p 指向一块 4 字节的内存,且这4字节数据类型未确定
void *p = malloc(4);

// 1,将这 4 字节内存用来存储 int 型数据
*(int *)p = 100;
printf("%d\n", *(int *)p);

// 2,将这 4 字节内存用来存储 float 型数据
*(float *)p = 3.14;
printf("%f\n", *(float *)p);

6.4 const型指针

  • const型指针有两种形式:①常指针 ②常目标指针
  1. 常指针:const修饰指针本身,表示指针变量本身无法修改。

img

  1. 常目标指针:const修饰指针的目标,表示无法通过该指针修改其目标。

img

  • 常指针在实际应用中不常见。
  • 常目标指针在实际应用中广泛可见,用来限制指针的读写权限
  • 示例:
int a = 100;
int b = 200;

// 第1中形式,const修饰p1本身,导致p1本身无法修改
int * const p1 = &a; 

// 第2中形式,const修饰p2的目标,导致无法通过p2修改a
int const *p2 = &a;
const int *p2 = &a;

6.5 函数指针

  • 概念:指向函数的指针,称为函数指针。
  • 特点:函数指针跟普通指针本质上并无区别,只是在取址和索引时,取址符和星号均可省略
  • 示例:
void   f (int); // 函数 f 的类型是: void (int)
void (*p)(int); // 指针 p 专门用于指向类型为 void (int) 的函数

p = &f; // p 指向 f(取址符&可以省略)
p =  f; // p 指向 f

// 以下三个式子是等价的:
  f (666); // 直接调用函数 f
(*p)(666); // 通过索引指针 p 的目标,间接调用函数 f
 p (666); // 函数指针在索引其目标时,星号可以省略
  • 要点:
    1. 函数指针是一类专门用来指向某种类型函数的指针。
    2. 函数的类型不同,所需要的函数指针也不同。
    3. 函数的类型,与普通变量的类型判定一致,即去除声明语句中的标识符之后所剩的语句。

相关文章:

  • android_viewtracker 原理
  • Vue的简单入门 三
  • Qt 坐标体系:逻辑坐标与物理坐标的区别与实践
  • SCI期刊推荐 | 免版面费 | 计算机领域:信息系统、软件工程、自动化和控制
  • Scala 中 val 和对象内部状态的关系
  • 如何搭建本地LLM的应用和开发
  • VBA信息获取与处理第五节:如何在单个工作表中查找某个给定值
  • 通往 AI 之路:Python 机器学习入门-机器学习基本概念
  • C/C++ 内存管理
  • 马斯克:AI游戏前景无限
  • 大模型+知识图谱:重塑企业制度标准管理
  • C++方向的面经
  • Self-Supervised Prompt Optimization
  • HTTP协议(20250305)
  • 设计模式:迭代器模式
  • Oracle常用分析诊断工具(9)——AWR
  • 杨辉三角解法
  • BambuStudio学习笔记:MeshBoolean类
  • C#+Halcon 检测稳定性提升的方式
  • docker:配置 Docker 镜像加速器
  • 深圳网站seo公司/推广app有哪些
  • wordpress怎么让网页支持多国语言/seo常见的优化技术
  • 安国市城乡建设局网站/微博营销成功案例8个
  • html做网站首页/视频外链平台
  • 自己制作宣传图片/正规seo关键词排名网络公司
  • 宁夏百度网站怎么做/河南疫情最新情况