c语言初阶指针

对指针的基本认识

指针最基本的用法

int a = 10;
int* p = &a;

指针其实就是存储的地址,现在主流的机器有32位与64位,先来看32位的机器,由于有32个位,因此可以表示2^32字节的地址,因为一个地址小单元就是一个字节,所以表示2^32字节,注意,是字节,因此2^32 Byte =(2^32/2^10)MB = (2^32/2^10/2^10)GB = 2^2 GB, 也就是4G,因此它可以存储4GB的数据,于是,一个地址长度就为4字节,指针变量又是存储的地址,因此在32位系统中,指针变量长度为4字节。

同理可得,在64位操作系统中,指针变量长度为8字节

五个结论

接下来是五个结论,理解这四句话才能打好指针的基础

  1. 内存被划分为一个个的内存单元,每个内存单元的大小是1个字节
  2. 每个字节的内存单元都有一个编号,这个编号就是地址,地址在c语言中称为指针
  3. 地址要存储的话,存放在指针变量中
  4. 每个内存单元都有一个唯一的地址来标识
  5. 在32位机器上地址的大小是4个字节,所以指针变量的大小也是4个字节;同理,在64位机器上地址的大小是8个字节,所以指针变量的大小也是8个字节。

指针跳跃

但是,指针存储地址, 地址在同一个机器上位数不变,是不是指针类型的长度就是固定的呢?

请看VCR:

#include <stdio.h>

int main(int argc, const char * argv[]) {
    int a = 10;
    int *pa = &a;
    printf("%lu\n", sizeof((int*)pa));
    printf("%lu\n", sizeof((short*)pa));
    printf("%lu\n", sizeof((char*)pa));
    printf("%lu\n", sizeof((long*)pa));
    return 0;
}

运行结果:

那么,每个类型的指针大小都是固定不变的,需要int*, short*这些指针类型干什么呢?是不是他们就没有意义了呢?

其实

指针类型是有意义的

指针类型决定了指针+1 /-1跳过几个字节

客官请看VCR:

#include <stdio.h>

int main(int argc, const char * argv[]) {
    int a = 10;
    int *pa = &a;
    char* pc = &a;
    
    printf("pa = %p\n", pa);
    printf("pa = %p\n", pa+1);
    
    printf("pc = %p\n", pc);
    printf("pc = %p\n", pc+1);
    
    return 0;
}

运行结果

第一行第三行分别打印的是pa, pc存储的地址, 也就是变量a的地址

可以看到对于int*类型的 pa与pa+1, 他们之间的地址(0x)表示十六进制,地址相差了 e0-dc = 4字节,这也说明pa到下一个地址pa+1跳过了4个字节.

再看pc与pc+1, 他们之间的地址相差了dd-dc = 1字节,这说明pc到下一个地址pc+1跳过了一个字节,刚好是一个字符的大小

因此, 有结论

指针类型是有意义的

指针类型决定了指针+1 /-1跳过几个字节

指针解引用使用

先看指针解引用法最基础的用法:

#include <stdio.h>

int main(int argc, const char * argv[]) {
    int a = 10;
    int *pa = &a;
    *pa = 0;
    printf("%d\n", a);
    return 0;
}

可以看到, *符号是解引用符号, 可以将指针p存储的地址取出来使用, 这里进行了a的数值的更改

更进一步, 使用指针访问数组

int arr[] = {1,2,3,4,5,6,7,8,9,10};
    int *p = arr;

因为, arr是数组名,数组名表示数组首元素的地址等价于int* p = arr[0], 于是,

#include <stdio.h>

int main(int argc, const char * argv[]) {
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    int *p = arr;
    for (int i=0; i<10; i++) {
        *(p+i) = 0;
    }
    for (int i=0; i<10; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

因为i为指针偏移量, 并且p是int*的指针, 一次跳过四个字节, 也就是一次跳过数组的一个元素, 访问数组下标为i的元素的地址,并将他们都置为0

于是,打印数组可以使用p+i找到下标为i的地址, 并解引用

for (int i=0; i<10; i++) {
        printf("%d ", *(p+i));
    }

由此有一个冷门但是炫酷的小结论

因为p存储的是arr的地址

因此,可以理解为

p等价于arr

也就是 p+i等价于arr+i

解引用

*(p+i)等价于*(arr+i)都等价于arr[i]

*(arr+i)等价于arr[i]

那么有没有可能 *(i+arr)等价于i[arr]呢?

我们试试

for (int i=0; i<10; i++) {
        printf("%d ", i[arr]);
    }

!竟然真的可以

为什么呢?

因为编译器在打印或者说访问数组arr[i]时, 会把元素首地址与偏移量取出来, 也就是使用的是*(arr+i), 所以将数组名和下标写反也是没问题滴, 小装逼方法get.

相关推荐
守护者17011 分钟前
JAVA学习-练习试用Java实现“使用Arrays.toString方法将数组转换为字符串并打印出来”
java·学习
学会沉淀。20 分钟前
Docker学习
java·开发语言·学习
Rinai_R35 分钟前
计算机组成原理的学习笔记(7)-- 存储器·其二 容量扩展/多模块存储系统/外存/Cache/虚拟存储器
笔记·物联网·学习
吃着火锅x唱着歌35 分钟前
PHP7内核剖析 学习笔记 第四章 内存管理(1)
android·笔记·学习
ragnwang37 分钟前
C++ Eigen常见的高级用法 [学习笔记]
c++·笔记·学习
罗伯特祥1 小时前
C调用gnuplot绘图的方法
c语言·plot
嵌入式科普2 小时前
嵌入式科普(24)从SPI和CAN通信重新理解“全双工”
c语言·stm32·can·spi·全双工·ra6m5
Web阿成2 小时前
3.学习webpack配置 尝试打包ts文件
前端·学习·webpack·typescript
雷神乐乐3 小时前
Spring学习(一)——Sping-XML
java·学习·spring