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

深入理解指针(1)

目录

1.内存和地址

1.1 内存

1.2 究竟该如何理解编址

2 指针变量和地址

2.1 取地址操作符(&)

 2.2指针变量和解引⽤操作符(*)

2.2.1 指针变量

2.2.2 如何拆解指针类型

2.2.3 解引用操作符 

2.3 指针变量的⼤⼩

3 指针变量类型的意义

3.1指针的解引⽤

3.2指针+-整数

3.3 void* 指针

4 指针运算

4.1 指针+- 整数

4.2 指针 - 指针

4.3 指针的关系运算


1.内存和地址

1.1 内存

我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,那我们买电脑的时候,电脑上内存是 8GB/16GB/32GB 等,

 其实也是把内存划分为⼀个个的内存单元,每个内存单元的⼤⼩取1个字节。

计算机中常⻅的单位(补充): 

⼀个⽐特位可以存储⼀个2进制的位1或者0
bit - ⽐特位                                                  
Byte - 字节
KB
MB
GB
TB
PB
1B yte = 8b it
1 KB = 1024B yte
1 MB = 1024 KB
1 GB = 1024 MB
1 TB = 1024 GB
1 PB = 1024 TB

在计算机中我们
把内存单元的编号也称为地址。C语⾔中给地址起
了新的名字叫:指针。

1.2 究竟该如何理解编址

CPU访问内存中的某个字节空间,必须知道这个
字节空间在内存的什么位置,⽽因为内存中字节
很多,所以需要给内存进⾏编址(就
计算机中的编址,并不是把每个字节的地址记录
下来,⽽是通过硬件设计完成的。
⾸先,必须理解,计算机内是有很多的硬件单
元,⽽硬件单元是要互相协同⼯作的。所谓的协
同,⾄少相互之间要能够进⾏数据传递。
但是硬件与硬件之间是互相独⽴的,那么如何通
信呢?答案很简单,⽤"线"连起来。
⽽CPU和内存之间也是有⼤量的数据交互的,所
以,两者必须也⽤线连起来。
不过,我们今天关⼼⼀组线,叫做 地址总线。
硬件编址也是如此
我们可以简单理解,32位机器有32根地址总线,
每根线只有两态,表⽰0,1【电脉冲有⽆】,那么
⼀根线,就能表⽰2种含义,2根线就能表⽰4种含
义,依次类推。32根地址线,就能表⽰2^32种含
义,每⼀种含义都代表⼀个地址。
地址信息被下达给内存,在内存上,就可以找到
该地址对应的数据,将数据在通过数据总线传⼊
CPU内寄存器。

指针变量和地址

2.1 取地址操作符(&)

理解了内存和地址的关系,我们再回到C语⾔,在C语⾔中创建变量其实就是向内存申请空间,⽐如:

int a = 10; 

 每个字节都有它自己的地址

0x00000068FB53F9E4
&a取出的是a所占4个字节中地址较⼩的字节的地址。
虽然整型变量占⽤4个字节,我们只要知道了第⼀个字节地址,顺藤摸⽠访问到4个字节的数据也是可⾏的。

 2.2指针变量和解引⽤操作符(*)

2.2.1 指针变量

2.2.2 如何拆解指针类型

指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。

取出a的地址并存储到指针变量p中 

 

例子: 

 

2.2.3 解引用操作符 

& - 取地址操作符

* - 解引用操作符(间接访问操作符)

2.3 指针变量的⼤⼩

int a = 10;

int * pa = &a; //0x0012ff40

pa是指针变量,指针变量也是变量,也是需要向内存申请空间的

pa这个指针变量的大小是多少?向内存申请了多大空间?

地址的存储需要多大空间,指针变量的大小就是多大

32位的机器上,地址是通过32根地址线传递的

那么一个地址就是32个0/1的二进制,需要32个bit位存储 = 4个字节

指针变量的大小就是4个字节

64位的机器上,地址是通过64根地址线传递的

那么一个地址就是64个0/1的二进制,需要64个bit位存储 = 8个字节

指针变量的大小就是8个字节

int main()
{
    char* pc;
    printf("%zu\n", sizeof(pc));
    printf("%zu\n", sizeof(char*));
    printf("%zu\n", sizeof(int*));
    printf("%zu\n", sizeof(short*));
    printf("%zu\n", sizeof(long*));
    printf("%zu\n", sizeof(long long*));
    printf("%zu\n", sizeof(float*));
    printf("%zu\n", sizeof(double*));

    return 0;
}

 

32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。

3 指针变量类型的意义

3.1指针的解引⽤

访问了4个字节 

只访问了一个字节 

 访问了8个字节,非法访问内存

结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。
char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。

3.2指针+-整数

int main()
{
    int a = 10;
    int*  pa = &a;
    char* pc = &a;//int*
    printf("pa  = %p\n", pa);
    printf("pa+1= %p\n", pa+1);//加了4个字节

    printf("pc  = %p\n", pc);
    printf("pc+1= %p\n", pc+1);//加了1个字节
    return 0;
}

我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。这就是指针变量的类型差异带来的变化。指针+1,其实跳过1个指针指向的元素。指针可以+1,那也可以-1。
结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。

3.3 void* 指针

void*指针,无具体类型的指针
但不能直接进行指针的+-整数和解引用的运算

void* 类型的指针可以接收不同类型的地址,但是⽆法直接进⾏指针运算。 

4 指针运算

指针的基本运算有三种,分别是:
指针+- 整数
指针-指针
指针的关系运算

4.1 指针+- 整数

整型数组

之前学过的

int main() 
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }
    return 0;
}

 指针+- 整数

 p+i 就是数组中下标为i元素的地址

*(p+i)就是下标为i的这个元素

反着打印

代码1:

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    //拿到第一个元素的地址
    int* p = &arr[0];
    for (i = sz-1; i >= 0; i--)
    {
        printf("%d ", *(p + i));
    }

    return 0;
}

反着打印

代码2:

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);
    //拿到第一个元素的地址
    int* p = &arr[sz-1];
    for (i = 0; i < sz; i++)
    {
        printf("%d ", *p);
        p--;
    }

    return 0;
}

字符数组

之前学的

int main()
{
    char arr[] = "hello world";
    printf("%s", arr);
    return 0;
}

 int main()
{
    char arr[] = "hello world";
    //拿到第一个元素的地址
    char* pc = &arr[0];
    while (*pc != '\0')
    {
        printf("%c", *pc);
        pc++;
    }
    return 0;
}

代码2: 

int main()
{
    char arr[] = "hello world";
    char* pc = &arr[0];
    while (*pc)
    {
        printf("%c", *pc);
        pc++;
    }
    return 0;

 

4.2 指针 - 指针

|指针-指针|:得到的是两个指针之间的元素个数
前提:两个指针指向了同一块空间,否则不能相减

 int main()
{
    int arr[10] = { 0 };
    printf("%lld\n", &arr[9] - &arr[0]);
    printf("%lld\n", &arr[0] - &arr[9]);
    
    return 0;
}


数组随着下标的增长,地址是由低到高变化的

相似 

想一个函数,求字符串的长度

int main()
{
    char  arr[] = "abcdef";
    printf("%zu\n", strlen(arr));
    return 0;
}

size_t my_strlen(char* p)
{
    size_t count = 0;//计数器
    while (*p)
    {
        count++;
        p++;
    }
    return count;
}

int main()
{
    char arr[] = "abcdef";
    //数组名其实也是数组首元素的地址
    //arr == &arr[0]
    printf("%zu\n", my_strlen(arr));
    return 0;
}

指针 - 指针

size_t my_strlen(char* p)
{
    char* start = p;
    while (*p) //使用p,来找到字符串中的\0
    {
        p++;
    }
    return p - start;
}

int main()
{
    char arr[] = "abcdef";
    //数组名其实也是数组首元素的地址
    //arr == &arr[0]

    printf("%zu\n", my_strlen(arr));

    return 0;
}

4.3 指针的关系运算

利用指针的大小比较

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    //打印数组的内容
    int sz = sizeof(arr) / sizeof(arr[0]);
    int* p = arr;
    while (p < arr + sz)//指针的大小比较
    {
        printf("%d ", *p);
        p++;
    }
    return 0;
}

http://www.xdnf.cn/news/348247.html

相关文章:

  • 3.5/Q1,GBD数据库最新文章解读
  • 深入解析:如何正确处理业务空值与技术异常?从避免滥用`None`和`WebDriverException`谈起
  • CTF-DAY10
  • 设计部绩效考核关键指标与综合评估方法
  • 【金仓数据库征文】金仓数据库 KES:MySQL 迁移实用指南
  • Vue3快速入门/Vue3基础速通
  • GIT设置账户密码特殊字符处理
  • 第三天 车联网云架构
  • 18.Java 序列化与反序列化
  • Puppeteer vs Playwright:全面对比与最佳应用场景指南
  • GIS开发技术介绍
  • Filecoin中lotus节点的搭建部署
  • 【Axure高保真原型】中继器表格批量上传数据
  • 如何解决 Linux 系统文件描述符耗尽的问题
  • LaTeX印刷体 字符与数学符号的总结
  • 【MySQL】进阶知识详解
  • 全球异硬脂酸及其衍生物市场:绿色化学浪潮下的技术迭代与区域增长新逻辑
  • Codeforces Round 1012 (Div. 2)
  • MybatisPlus 发布 3.5.12 版本啦
  • 过曝区域信息补全
  • Python从入门到高手8.3节-元组的常用操作方法
  • 【战略合作】开封大学_阀门产业学院+智橙PLM
  • maven 依赖冲突异常分析
  • 17.thinkphp的分页功能
  • 开发者如何应对浏览器中的身份关联与反追踪问题?
  • 主成分分析(PCA)是什么?简易理解版
  • 使用Compose编排工具搭建Ghost博客系统
  • goner/otel 在Gone框架接入OpenTelemetry
  • [python] 函数1-函数基础
  • 软考职称政策再加码!已有多地发布通知!