【C语言】指针的理论详解

简介: 【C语言】指针的理论详解

指针简介

  • 指针(pointer)是C语言的一个重要知识点,其使用灵活,功能强大,是C语言的灵魂。
  • 指针?什么是指针?从根本上看,指针是是一个值为内存地址的变量(或数据对象)。指针与底层硬件联系紧密,使用指针可操作数据的地址,实现数据的间接访问。

计算机的存储机制

指针和内存是离不开的,先了解一下内存是怎么定义的。

int a = 0x12345678;    

short b = 0x5A6B;

char c[] = {0x33,0x34,0x35};

看一下地址和内存框图

e3c1a6820fb43881ad23b0211106d4a8_94e24b251bba43bf884e021d37a1c202.png

  • 内存的框图如上,计算机通常把内存分配成上图模式的线性空间
  • 内存分为一个个线性的区域,每个线性区域,都是一个字节为单位的,线性分配下去
  • 每个字节都对应一个地址(独一无二的地址)

int a = 0x12345678;     //0x是十六进制前缀,后面是实际的数据

short b = 0x5A6B;

char c[] = {0x33,0x34,0x35};

在计算机系统里,int类型变量,代表4个字节数据,4*8 = 32个二进制数来表示,就是32位,但是要注意在51单片机里,int类型是16位的。

int a = 0x12345678;  这样定义一个int类型的变量,变量名称为a,把它分开成四个字节来看

0x12 | 34 | 56 | 78

如上图内存的分配可以看到,0x78存储在0x4000地址,0x56存储在0x4001地址,0x34存储在0x4002地址,0x12存储在0x4003地址,总共占用四个字节。

数据的小端(低位)存储在内存地址的低位,就是把小端存在前面,这种分配方式叫小端模式。

另一种存储模式叫大端模式:数据的大端(高位)存储在地址的低位。

现在的计算机普遍运用小端模式分配地址。

short类型的变量当然也是小端模式存储。

char类型的数组,就是按照顺序存储,对照上图。数组在内存中分配的是连续的内存空间。

那如果是short类型的数组呢?

举个例子,short d[] = {0x5A6B, 0X7C8D};

那么存储方式应该是先给数组开辟一段连续的地址,然后按照数组的顺序依次分配地址,数组内地每一个数据都是小端模式 :第0位放在地址低位……

地址              内存

0x4009        0x6B

0x400A        0x5A

0x400B        0x8D

0x400C        0x7C


33f4c79acf030c6a98b003e457c8569a_5f09792bcdd44b3c812f2a3c7efb3d0a.jpeg

定义指针

  • 指针即指针变量,用于存放其他数据单元(变量/数组/结构体/函数等)的首地址。若指针存放了某个数据单元的首地址,则这个指针指向了这个数据单元,若指针存放的值是0,则这个指针为空指针
  • 定义一个指针变量:

de40960977b4c6d9176349d4a35ffc82_16e567dd7f8046fd8784f2d94b48ba0c.png

指针变量的x字节,起码要存的下这个数据,所以不同系统,指针的位宽用不同的长度

用示例演示一下

a44971db2f64bf79c3d898581ba79fad_d266e4f542c34e6bab090d44957dee41.png

可以看到我定义的指针位宽是4个字节,我猜是因为vscode调用 gcc编译器是32位的,所以指针p位宽是4,在Dev-C++里运行,针p位宽是8

e0b4ecc759d70b90e9218e2f37846381_c36d0f1e2f034f61824770244cb1af14.png


指针的操作

  • 若已定义:
           int a;        //定义一个int型的数据          
           int *p;        //定义一个指向int型数据的指针
  • 则对指针p有如下操作方式:

a271c1790b70aefea6102f677fec8f68_d23f5c30bf434faa8686daa1ed0169cb.png

& 取地址

  • 如果&运算符左右都有变量,那么这个符号就代表位运算&
  • 如果&运算符只有右边是变量,那个就代表取地址的意思

* 取内容

  • 如果*运算符左右都有变量,那么这个符号就代表乘法运算符
  • int *中的*只是一个标识,一个代号
  • 如果* 只有后面有变量,而前面没有数据类型,那就是取内容,就是解引用的意思

此时想到狄泰软件学院唐老师说的:“编译运行试试就知道了呗,反正电脑又不会爆炸 !

用示例说话~

9e1816a3ed766e6b0ecc59f43fdbaca7_e0261fd2473e4631abaa9a1971ff6bf8.png

运行结果的第一行是打印a这个变量

第二行是取出a的地址

第三行是取出指针变量p指向德内容a (p取了a的地址,代表p指向a,引用p,就是引用a)(地址间接访问数据)

指针变量的运算一般只有“+”和“-”,没有“”或“/”或乘方开方等等,因为它只是个地址

847438e84ae0d16f48896298b5c882b3_e5fee9012f254f8b971764a72d731883.png

由p++运算 可以看到,p的地址加了一个char类型的数据宽度1,如果改为int类型的变量会怎么样呢?                

20305bbe76448e99de6db7092bf3ca0f_40302959b273437084afabe0ca4d1eab.png

可以看到地址加了4(int型变量的数据宽度就是4)

因为指针越界了,指向了非法的位置,出现了不可预知的错误,所以我才给注释掉~~~

指针的++或--一般用在数组里边,对于这种单独的变量就是做一个演示。


数组与指针

数组是一些相同数据类型的变量组成的集合,其数组名即为指向该数据类型的指针。数组的定义等效于 申请内存、定义指针和初始化

例如:        char c[] = {0x33, 0x34, 0x35};

等效于:     申请内存

                   定义char *c = 0x4000;

                   初始化数组数据

利用下标引用数据也等效于指针取内容

c[0];        等效于:*c;

c[1];        等效于:*c+1;

c[2];        等效于:*c+2;

逐一打印数组元素

ddc22f00949a327dd8221bbb57ae5574_5f538e2afe184dc99ef30fac39fc2b4c.png

之前是习惯了用for循环打印数组

1ffe4e0c288f262f60fddbfa99f4fb1a_04c2b6c647774401ac03bb8c816db384.png

现在就可以用指针了

定义一个指针变量p,变量p++就相当于数组下标加加

31210e42ebf138435f89e74977df3da2_87559e5b1f6343a484c589c4ea2ed491.png

数组取下标就是指针取内容的另一种表示形态

数组a本身就是一个指针

45be89ad0f6ea703a2070839b64de2f5_22fd819f994c40d6ad71cae70b2dbfd7.png

如果将数组类型转换为int类型,效果也是一样的~

因为int类型的变量占4个字节,所以指针每次加1,都会跳4格,但是实际访问数据就是依次访问的(根据变量类型宽度而定)

015a82bbb7bedff93dbd2a4ac900ddf4_7f9e99d7115e4974b2e9d46d0088711c.png

上文提到了申请数组的等效方法:

  1. 申请内存
  2. 定义char *c = 0x4000;
  3. 初始化数组数据

申请内存需要用到 #include <stdlib.h>头文件

malloc(3*4);    //申请内存 3个变量*4个字节 返回值是void*

   //void*是空*   void*可以表示任何类型的指针

定义指针        int *a;

a = malloc(3 * 4);        //定义

然后初始化数据

*a = 0x38;

   *(a + 1) = 0x32;

   *(a + 2) = 0x35;

b0e8ff93a6e514fbb82e8b96de670531_c5fa7aaa9e914665831ec2ca1e4c4a2e.png

这种申请内存的方法,还是第一次接触呢~

数组就是指针

数组名就是指针变量


注意事项

  • 在对指针取内容之前,一定要确保指针指在了合法的地址,否则将会导致程序出现不可预知的错误
  • 同级指针之间才能相互赋值,跨级赋值将会导致编译器报错或警告

a91bb6c1041ef2140da67da7e97afe26_73c6e3031ec344a6a5eeb595ebf96009.png

数组是有越界的,数组越界和指针指向了非法位置是一样的性质

因为数组就是指针,那指针也可以越界的说法

错误示范:

1.定义了指针不赋初值,直接引用指针变量

举个栗子

97e2dea1a1f9b6224aac18a889f7a052_9ae62f6df0024521ad526e06e6e7703a.png

只有定义但是没有赋初值,运行出错了

58f0cd2ecc8c35121f916d34d2e88964_61a674a83624402a8863ce88398d8513.png

0x55是随意赋的地址,地址是非法的,所以运行不出来

赋初值就可以运行了

8a3d920cf574edd738068b7c673ec5d4_510111eb9e614cb6aba33748d45a7223.png

2.同级指针之间才能相互赋值

变量看做零级指针

普通的指针就看做一级指针

指针再取地址就是二级指针

指针级别不同造成的错误:

6d181dd15ba944d3635b7410d08c96c1_9a2d377dddf84a929c7d6f100d97d37b.png

有个警告,从int*类型赋值给int类型,没有进行强制转换

如果是 p = a; 也是不可以的

a = *p;        对p取内容,他们就变成同级指针了,就可以赋值了

p = &a;        对a取地址,也变成同级指针


有关指针的原理,目前就学了这么多!

初级指针

二级指针,函数指针,结构体指针……这些复杂的指针,暂时用不到~_~

9013181c7c2144dc92eb7ff9d84c78e2_90d09cb7133e4626a4063ae10972df03.jpeg


相关文章
|
19天前
|
存储 C语言
【C语言篇】深入理解指针3(附转移表源码)
【C语言篇】深入理解指针3(附转移表源码)
30 1
|
19天前
|
存储 程序员 编译器
【C语言】指针篇-简单快速了解指针-必读指南(1/5)
【C语言】指针篇-简单快速了解指针-必读指南(1/5)
|
5天前
|
存储 C语言
C语言32位或64位平台下指针的大小
在32位平台上,C语言中指针的大小通常为4字节;而在64位平台上,指针的大小通常为8字节。这反映了不同平台对内存地址空间的不同处理方式。
|
4天前
|
存储 算法 C语言
C语言:什么是指针数组,它有什么用
指针数组是C语言中一种特殊的数据结构,每个元素都是一个指针。它用于存储多个内存地址,方便对多个变量或数组进行操作,常用于字符串处理、动态内存分配等场景。
|
5天前
|
存储 C语言
C语言指针与指针变量的区别指针
指针是C语言中的重要概念,用于存储内存地址。指针变量是一种特殊的变量,用于存放其他变量的内存地址,通过指针可以间接访问和修改该变量的值。指针与指针变量的主要区别在于:指针是一个泛指的概念,而指针变量是具体的实现形式。
|
5天前
|
C语言
C语言指针(3)
C语言指针(3)
9 1
|
5天前
|
C语言
C语言指针(2)
C语言指针(2)
9 1
|
11天前
|
存储 搜索推荐 C语言
深入C语言指针,使代码更加灵活(二)
深入C语言指针,使代码更加灵活(二)
|
11天前
|
存储 程序员 编译器
深入C语言指针,使代码更加灵活(一)
深入C语言指针,使代码更加灵活(一)
|
11天前
|
C语言
深入C语言指针,使代码更加灵活(三)
深入C语言指针,使代码更加灵活(三)
深入C语言指针,使代码更加灵活(三)