C语言入门,深入理解指针(1)

目录

  • [1. 内存和地址](#1. 内存和地址)
  • [2. 指针变量和地址](#2. 指针变量和地址)
  • [3. 指针变量类型的意义](#3. 指针变量类型的意义)
  • [4. 指针运算](#4. 指针运算)

前言

指针是 C 语言中最重要也最让初学者头疼的概念之一。本文将从「内存与地址」讲起,逐步带你理解指针变量、指针类型、指针运算等核心知识,帮助你建立对指针的直观认识,为后续深入学习打下坚实基础。

本文适合C 语言初学者阅读,建议你已掌握基本的变量、数据类型和数组知识。阅读本文前,请确保你了解:变量的创建与赋值、基本数据类型(如 int、char)、数组的基本使用。

1.内存和地址

1.内存

内存作为电脑中很重要的一部分,当程序运行起来时,程序要加载到内存中,数据的存储也是在内存中。

我们先了解一下计算机中常见的单位:

Byte表示字节,bit表示比特位

我们知道当电脑中的CPU处理数据时,会从内存中读取数据,处理完后的数据放回内存中,那么内存是如何做到有效管理的呢?

我们将学生宿舍与内存结合进行说明,一栋学生宿舍里包含很多个房间,而内存也被划分为一个个内存单元;一个房间有8个床位,而一个内存单元就是一个字节,一个字节包含8个bit位;每个房间都有对应的宿舍号,通过宿舍号可以找到正确的宿舍,而一个内存单元也有对应的编号,通过编号可以找到对应的内存单元。宿舍号是宿舍的地址,那么编号就是一个内存单元的地址。

通过以上类比,我们得出1个结论:内存单元的编号=地址=指针(C语言规定)

2.如何理解编址

对于计算机中CPU和内存的联系,我们给上一张关系图:

读取数据:控制总线发出read的信号-->CPU-->想要访问内存空间的地址-->地址总线-->找到对应内存空间-->读取数据-->数据总线-->CPU

CPU和内存通过3种功能的线联系起来,进行数据的传输。

在今天我们主要关心一组线:地址总线,CPU想要访问内存中的某块空间,首先要知道内存存放的地址,才能去访问;而每个内存单元的地址不需要存起来,因为在计算机中内存的地址是通过硬件设计编写的,类似钢琴,每个琴键都有对应的固定发音。地址总线在32位环境下,有32根地址总线,1根线有2种状态,0或1,1根线可以表示2种含义,那么32根线可以表示2^32种含义,每一种含义对应一个地址。

2.指针变量和地址

1.取地址操作符:&

我们既然讲到编址的内容,顺便提问一下变量创建的本质是什么?

答案是在内存中申请一块空间,用来存放数据

举例:

变量a的创建,数据类型是int,向内存申请一块4个字节的空间用来存放10,在编译器和计算机看来,a其实是对应那块空间的地址;而空间的名字叫做a,实际上是方便程序员自己看的。

为了得到a的地址,我们需要运用到取地址操作符&,得到的是a所占4个字节中第1个字节的地址,通过这个地址,顺藤摸瓜就可以知道其他字节的地址。

2.指针变量和解引用操作符(*)

1.指针变量

专门用来存放地址(指针)的变量,就叫做"指针变量 "。

2.如何拆解指针类型

根据上面的代码,为什么指针变量pa的类型是int *?

解释:星号表示pa是一个指针变量 ,int表示p所指向的对象的类型是整型 ,

其他类型指针变量依次类推。

3.解引用操作符(*)

当我们想要使用地址时,此时我们要用到解引用操作符*,这里的星号跟上面的星号表示指针变量的不一样,这里表示通过pa里面的地址找到它所指向的对象 。

比如下面的例子:

*pa通过解引用,根据pa里面的地址找到所指向的对象是变量a,将0赋给变量a,此时a的值变为0。

到此,我们又学习了一种修改数值的方法了。

3.指针变量的大小

在上述代码中,我们创建了指针变量pa,为此我们得向内存申请空间。但是pa这个指针变量的大小是多少?又要向内存申请多少空间呢?

指针变量的大小与地址的存储需要多大空间有关 。

在上面编址部分,我们讲到地址总线,而地址总线的数量跟环境有关,在32位机器下,把32根地址线产生的2进制序列当做一个地址,一个地址就是32个bit位,需要4个字节存储,指针变量的大小也就是4个字节;以此类推,64位机器下,64根地址线做一个地址,一个地址就是64个bit位,需要8个字节存储,指针变量的大小也就是8个字节。

指针变量的大小和类型是无关的,不同指针类型的变量在相同的平台下,大小都是一样的 。

在VS2022编译器进行演示:

在x64环境下运行的结果:

在x86环境下运行的结果:

3.指针变量类型的意义

从上述代码运行结果来看,在相同环境下,指针变量的大小相同,那为什么还要有不同的指针类型呢?下面对指针变量类型的意义进行讲解。

1.指针的解引用

对于上面2段代码,我们通过调试,观察内存的变化,代码1会将a的4个字节都变成0,而代码2会将a的1个字节变成0。

结论:指针的类型可以决定在解引用过程中的权限,也就是一次可以访问并操作多少个字节数。

2.指针±整数

当指针加上或减去一个整数,不同的指针变量类型会对结果产生什么影响呢?

举下面一段代码为例:

首先取出a的地址赋给不同类型的指针变量,紧接着打印pa、pb,还未发生改变前,这2个地址打印出来肯定是一样的,但是当pa+1、pb+1后,地址会怎么变化呢?

我们从运行结果可知:pa到pa+1地址增加了1,这说明了char类型的指针变量加1后会跳过1个字节;pb到pb+1地址增加了4,这说明了int 类型的指针变量加1后会跳过4个字节。

结论:指针的类型决定了指针(地址)向前或向后一步可以走多少字节

3.void*指针

在指针类型中存在着1种类型:void*类型,它可以接受任意类型地址,但不能直接进行指针的±整数和解引用运算。

举例:

将一个int类型变量的地址赋给int类型的指针变量,这不会出现什么问题;但将一个int类型变量的地址赋给char 类型的指针变量,编译器就会警告了,如下图:

然而void的出现,则不会出现不兼容问题,不同类型变量的地址赋给void*类型,它都能够去接收。

但是void*类型的指针无法参与指针相关的运算 ,如下面代码:

那么void类型的指针有什么用呢?一般void 类型的指针用在函数参数的部分,用于接受任何类型数据的地址。

4.指针运算

1.指针±整数

例1.

数组在内存中连续存放,我们只要知道第一个元素的地址,就可以顺藤摸瓜地找到其他元素。

在之前我们对数组的打印,也许是单纯的通过数组下标进行打印的:

但在今天,我们学习了指针的一些基本知识之后,我们也许可以试着用解引用来做:

或

例子2.

我们之前学过如何统计字符串的长度?用strlen函数去统计。

我们通过一个函数结合指针去写一下strlen函数的实现过程:

2.指针-指针

3.指针的关系运算

用指针去打印数组,代码如下:

总结

本文围绕 C 语言中的指针展开,核心要点如下:

  • 地址与指针 :内存单元都有编号,这个编号就是地址,C 语言规定地址即指针。通过取地址操作符 & 可以获取变量的地址。
  • 指针变量 :专门用来存放地址的变量称为指针变量,其类型由「指向对象的类型 + *」构成,如 int *pa 表示 pa 是指向整型的指针变量。
  • 解引用操作符 *:通过指针变量中存放的地址,找到并访问它所指向的对象,从而实现对数据的间接修改。
  • 指针变量的大小:指针变量的大小与类型无关,只与平台相关------32 位环境下为 4 字节,64 位环境下为 8 字节。
  • 指针类型的意义 :指针类型决定了解引用时一次能访问的字节数,也决定了指针 +/- 整数时一次跳过的字节数。
  • void* 指针:可以接收任意类型的地址,但不能直接参与指针的加减和解引用运算,常用于函数参数以接收任意类型数据的地址。
  • 指针运算 :指针可以 +/- 整数实现数组元素的遍历,两个指针相减可以得到元素个数,指针之间还可以进行关系运算(比较大小)。

掌握这些基础,你就已经迈出了理解 C 语言指针的第一步,后续可以进一步学习指针与数组、指针与函数、动态内存分配等进阶内容。

相关推荐
AC赳赳老秦1 小时前
OpenClaw 数据引用规范自动生成:为公开数据构建可信来源标注与标准引用体系
大数据·开发语言·汇编·数据库·人工智能·deepseek·openclaw
无忧.芙桃1 小时前
C++语言原理与实践(十):vector类的底层实现
开发语言·c++·算法
天l志1 小时前
DL/T 645—2007《多功能电能表通信协议》的跨平台 C 语言实现
c语言·电表·dlt645
变与不变8062 小时前
js同步和异步难点重点详解
开发语言·javascript·ecmascript
qq_2518364573 小时前
理发管理系统 —— 会员模块
开发语言·前端·python·flask
Dovis(誓平步青云)3 小时前
几个方案来回选不定?做一个随时切换的候选推荐页
android·java·服务器·开发语言·javascript·数据库·智能化
郝学胜-神的一滴3 小时前
AI 编程智能体 03:拆解当下主流智能体能力
开发语言·c++·人工智能·python·程序人生·游戏
言乐63 小时前
Python文字审核模型
开发语言·windows·python·django·pygame
Cc.Y3 小时前
Java零基础入门:方法(函数)深度掌握
java·开发语言