C语言之初阶指针

server/2024/9/25 11:01:15/

目录

  • 前言
  • 1. 什么是指针
    • 代码展示
  • 2. 计算机如何编址
  • 3. 指针类型
    • 指针类型的定义
    • 指针类型的意义
  • 4. 野指针
    • 4.1. **指针未初始化**
    • 4.2. **指针越界访问**
    • 4.3. **指针指向的空间被释放**
  • 5. 指针运算
  • 6. 指针和数组
  • 7. 二级指针
  • 8. 指针数组
  • 总结

前言

本篇将深入探讨C语言中至关重要的知识点——指针。鉴于指针内容的丰富性,我将其分为初阶和进阶两部分。初阶部分适合刚刚接触C语言的同学,许多人在学习指针时感到困惑,希望通过我的讲解,能让你们真正理解并掌握指针。现在,让我们立即开始这段指针之旅。

1. 什么是指针

在计算机科学中,指针(Pointer)是编程语言中的一个对象,它存储的是另一个变量的内存地址。通过这个地址,指针可以直接指向存储器中另一个地方的值。由于通过地址可以找到所需的变量单元,因此地址形象地被称为“指针”。换句话说,指针是通过它所存储的地址来找到对应的内存单元。

我们可以将指针类比为家的地址。如果想回家,就必须通过地址找到家的位置。指针就是存储地址的变量,通过指针可以访问对应的位置。

代码展示

#include <stdio.h>
int main()
{int a = 10; // 在内存中开辟一块空间int *p = &a; // 这里我们对变量a,取出它的地址,可以使用&操作符。// 将a的地址存放在p变量中,p就是一个指针变量。return 0;
}

总结:指针就是存储地址的变量。

2. 计算机如何编址

计算机如何为内存中的每个单元分配地址?一个单元的大小是多少?

经过仔细的计算和权衡,我们发现一个字节对应一个地址是最合适的。对于32位的机器,假设有32根地址线,每根地址线在寻址时产生一个电信号(正电/负电,即1或0),就会产生2的32次方个地址。

因此,在32位机器上,地址由32个比特位组成,用4个字节就能表示,所以一个指针占4个字节。对于64位机器,指针则占8个字节。

3. 指针类型

虽然指针的大小在32位机器上是4字节,在64位机器上是8字节,但指针并不是只有一种类型。为什么指针大小已经确定还要分整形指针、字符指针等数据类型呢?指针类型的意义又是什么呢?接下来我们用32位的机器来进行讲解。

指针类型的定义

int num = 10;
p = &num;

要将 &numnum 的地址)保存到 p 中,我们知道 p 就是一个指针变量,那它的类型是怎样的呢?我们给指针变量相应的类型。

char   *pc = NULL;
int    *pi = NULL;
short  *ps = NULL;
long   *pl = NULL;
float  *pf = NULL;
double *pd = NULL;

这里可以看到,指针的定义方式是:type + *。例如,char* 类型的指针是为了存放 char 类型变量的地址,int* 类型的指针是为了存放 int 类型变量的地址。

指针类型的意义

指针±整数

在这里插入图片描述

结果显示,int 类型的指针加1移动了4个字节,而 char 型指针移动了1个字节。

总结:指针类型决定了指针向前或向后走一步的大小。

指针的解引用

在这里插入图片描述
在这里插入图片描述

我们对比这两张图可以发现,char 类型的指针在访问变量 n 时只改变了一个字节的值,而 int 类型指针改变了 n 的4个字节。

总结:指针类型决定了对指针解引用的权限(能操作几个字节)。

4. 野指针

概念:野指针是指指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。指针变量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的。

野指针成因

4.1. 指针未初始化

#include <stdio.h>
int main()
{ int *p; // 局部变量指针未初始化,默认为随机值*p = 20;return 0;
}

4.2. 指针越界访问

#include <stdio.h>
int main()
{int arr[10] = {0};int *p = arr;int i = 0;for(i=0; i<=11; i++){// 当指针指向的范围超出数组arr的范围时,p就是野指针*(p++) = i;}return 0;
}

4.3. 指针指向的空间被释放

在这里插入图片描述
这里a变量在text函数调用完就被释放了,指针pa得到了他的地址,但a的空间已经还给操作系统,所以pa就变成了野指针。

如何规避野指针

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放后及时置 NULL
  4. 指针使用之前检查有效性

5. 指针运算

  1. 指针±整数
  2. 指针-指针
  3. 指针的关系运算

指针±整数

#define N_VALUES 5
float values[N_VALUES];
float *vp;
// 指针±整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{*vp++ = 0;
}

在这里插入图片描述

解释:vp 指向了 values 数组的第一个元素,vp 小于 values 数组的最后一个元素后面的地址,vp就等于0,再加1。

指针-指针

指针-指针的运算,只能是指向同一块空间的指针才能计算。计算结果为指针之间元素的个数。

在这里插入图片描述

指针的关系运算

for (vp = &values[0]; vp < &values[N_VALUES];)
{*vp++ = 0;
}

标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

6. 指针和数组

指针和数组是两个不同的对象。

  • 指针是一个变量,用于存放地址的变量,大小为4或8个字节。
  • 数组是存放相同数据类型的集合,数组的大小由元素个数和元素类型决定。

数组名是数组首元素地址,地址可以放在指针变量里,通过指针来访问数组。

扩展:
在这里插入图片描述

7. 二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?这就是二级指针。

int a = 10;
int *pa = &a;
int **ppa = &pa;

在这里插入图片描述

也可以这样理解,pa 前的 * 是说 pa 是指针变量,指向的数据类型为 int,对于 ppa 来说,* 指的是 ppa 是指针变量,指向的数据类型是 int*

对于二级指针的运算有:

int b = 20;
*ppa = &b; // 等价于 pa = &b;
**ppa = 30; // 等价于 *pa = 30; 等价于 a = 30;

8. 指针数组

指针数组是指针还是数组?答案:是数组。是存放指针的数组。

int* arr[5]; // arr3是一个数组,有五个元素,每个元素是一个整形指针。

总结

指针是C语言中一个强大而灵活的工具,掌握指针的基本概念和用法对于理解C语言的内存管理和高级编程技巧至关重要。通过本文的介绍,你应该对指针的定义、声明、初始化、解引用、运算、与数组的关系、与函数的关系、空指针以及常见错误有了初步的了解。希望这些知识能够帮助你在C语言编程中更好地使用指针。


http://www.ppmy.cn/server/121790.html

相关文章

【C++】托管类和托管函数

托管类和托管函数 1. 托管类 托管类是指在 .NET 环境中运行的类&#xff0c;它们由公共语言运行时&#xff08;CLR&#xff09;管理。托管类具有以下特点&#xff1a; 自动内存管理&#xff1a;托管类的实例由 CLR 的垃圾回收机制管理&#xff0c;自动处理内存的分配和释放。…

pg入门5—pg有哪些系统schema

在 PostgreSQL 中&#xff0c;除了用户创建的 schema 之外&#xff0c;系统还自动创建了一些系统 schema&#xff0c;用于管理数据库的元数据、系统信息以及维护操作。以下是 PostgreSQL 中的常见系统 schema&#xff1a; 1. pg_catalog 用途&#xff1a;pg_catalog 是 Postg…

微服务架构---Ribbon\Feign

Ribbon(负载均衡) Ribbon概述 在 SpringCloud 中&#xff0c; Nacos⼀般配合Ribbon进行使用&#xff0c;Ribbon提供了客户端负载均衡的功能&#xff0c;Ribbon利用从Nacos中读取到的服务信息&#xff0c;在调用服务节点提供的服务时&#xff0c;会合理的进行负载。 Ribbon作…

C语言练习:通讯录

简单版代码讲解&#xff1a; 这个版本不涉及文件操作以及动态内存分配&#xff0c;有助于理解代码。 文件管理 这里我们分了三个文件&#xff0c;.h 文件里给出类型声明和函数声明&#xff0c;contact.c 文件是具体的实现&#xff0c;test.c文件里是游戏的实现逻辑。 test.c…

Pinia从安装到使用

什么是Pinia 添加Pinia到vue项目 使用Pinia实现计数器案例 counter.js import {defineStore} from "pinia"; import {ref} from "vue";export const useCounterStore defineStore(coutner,()>{//定义数据&#xff08;state&#xff09;const count r…

Hadoop的安装

文章目录 一. 到Hadoop官网下载安装文件hadoop-3.4.0.tar.gz。二. 环境变量三. 配置 一. 到Hadoop官网下载安装文件hadoop-3.4.0.tar.gz。 随后点击下载即可 由于Hadoop不直接支持Windows系统&#xff0c;因此&#xff0c;需要修改一些配置才能运行 二. 环境变量 三. 配置 进…

android13 系统默认设置静态IP

android11系统的时候&#xff0c;默认静态IP设置很简单&#xff0c;修改frameworks\base\core\res\res\values\config.xml中的config_ethernet_interfaces字符数组&#xff0c;在里面添加静态IP的参数就可以了。 <string-array translatable"false" name"c…

IDEA类和方法注释模板设置

一、概述 IDEA自带的注释模板不是太好用&#xff0c;我本人到网上搜集了很多资料系统的整理了一下制作了一份比较完整的模板来分享给大家&#xff0c;写这篇文章只是为了让大家省事。 适用于通过在项目工具窗口中调用新建 | Java 类 | 类创建的新 Java 类。 此内置模板…