这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 综合技术 » 基础知识 » 从偏移量bug谈指针运算

共5条 1/1 1 跳转至

从偏移量bug谈指针运算

院士
2026-07-03 15:13:32     打赏

从偏移量bug谈指针运算

这是bug

在我们的项目新功能下,我的组员在写入NorFlash时出现了Flash状态异常,表现的形式是Flash的Ready状态位始终不置位,从而系统卡死在Flash写入函数里面的读取status ready的获取上。示例源代码如下所示:

void flash_program(uint32_t addr, uint8_t *buf, uint32_t len)
{
    int ret = 0;
    uint32_t i;
    uint8_t addr_buf[4];
    addr_buf[0] = addr >> 24;
    addr_buf[1] = addr >> 16;
    addr_buf[2] = addr >> 8;
    addr_buf[3] = (uint8_t)addr;
    for(i = 0; i < 4; i++)
    {
        spi_write_byte(addr_buf[i]);
        ret = flash_status_get();
        if(ret != 0)
        {
            // NOT ready !? something wrong
            while(1)
            {
                ;
            }
        }
    }
    for(i = 0; i < len; i++)
    {
        spi_write_word(*buf);
        ret = flash_status_get();
        if(ret != 0)
        {
            // NOT ready !? something wrong
            while(1)
            {
                ;
            }
        }
        buf++;
    }
}

我同事的代码卡死在第30行。即当写入数据时出现了异常。

在阅读代码之后,很快就发现了第35行存在了bug。此时的buf的偏移量应该偏移4字节,而非单字节,即buf += 4。有的人说了,你传入的是uint32_t类型的数值,应该把buf的定义为uint32_t的类型才对!谁对谁错?程序最优如何编写?这也就引入了今天的话题,指针运算。

先搞懂底层:指针运算的核心底层逻辑

指针是C语言的灵魂,而指针运算正是这把“灵魂钥匙”的核心用法。指针运算本质上是一套有明确规则的“内存导航手册”,只要摸透底层逻辑、避开常见误区,就能用它写出高效、简洁的代码。

很多新手踩坑的根源,是误以为指针加减和普通整数加减是一回事。这里我把最核心的底层逻辑放在最前面,这是所有指针运算的根基: 指针的所有算术操作,都不是直接操作内存字节数,而是以指针指向的数据类型的大小为步长移动。比如一个int*类型的指针,执行p++操作时,指针地址实际偏移的字节数是sizeof(int),在32位系统下就是4字节,而不是1字节。 这个规则是所有指针运算的基础,也是90%指针问题的源头。

指针运算

我们在谈指针运算的前提是两个指针必须是同类型,而且,依我个人的实际经验,指针运算均指向同一块可操作的内存空间,比如指向同一个数组,或malloc出来的内存空间。在示例之前,我们先假期Ptr1与Ptr2两个指针均为类型 Type_t,即 Type_t *Ptr1, *Ptr2;

1. 两个同类型的指针变量,可以比较大小

Ptr1指向的地址 < Ptr2指向的地址,则 Ptr1 < Ptr2;

2. 两个同类型的指针变量,可以相减

Ptr1 - Ptr2 = (Ptr1指向的地址 - Ptr2指向的地址) / sizeof (Type_t);

3. 指针变量加减一个整数的结果是指针

Ptr1 + n (指向的地址)= Ptr1 + n * sizeof(Type_t)

4. 指针变量可以自增,自减

同理上述第3种情况。

5. 下标运算符[]

Ptr1[n] 等同于 *(Ptr1 + n)

指针运算的实战使用经验

C语言里指针合法的算术运算只有三类:指针加减整数、同类型指针相减、指针关系比较,除此之外的所有运算(比如指针乘除、不同类型指针随意加减)都是非法操作,没有任何实际意义,还会带来内存越界的风险。

1. 指针加减整数:数组遍历的最高效写法

指针加减整数是日常开发里用得最多的操作,最典型的场景就是数组遍历。

uint16_t sensor_buf[64];
uint16_t *ptr = sensor_buf;
for (int i = 0; i < 64; i++)
{
    *ptr = get_sensor_value();
    ptr++;
}

2. 同类型指针相减:计算元素数量的专属工具

两个同类型指针相减,得到的结果不是它们之间的字节差,而是它们之间相隔的元素个数,这个特性最经典的应用就是自己实现字符串长度计算。

size_t my_strlen(const char *str)
{
    const char *start = str;
    while (*str != '\0')
    {
        str++;
    }
    return str - start;
}

3. 指针关系运算:内存越界的安全闸门

uint8_t uart_rx_buf[256];
uint8_t *buf_start = uart_rx_buf;
uint8_t *buf_end = uart_rx_buf + 256;
uint8_t *current_ptr = uart_rx_buf;

if (current_ptr < buf_end)
{
    *current_ptr = uart_read_byte();
    current_ptr++;
}

总结:指针运算的本质是内存管理

玩透指针运算之后你会发现,它根本不是什么高深的黑魔法,本质上就是让你直接掌控内存的布局和访问节奏。在嵌入式开发这种资源受限的场景里,用好指针运算不仅能写出效率更高的代码,更能让你从底层理解程序是怎么和硬件内存交互的,这是C语言开发者必须掌握的核心能力。 不用害怕指针,只要守好“步长规则、同类型操作、边界检查”这三条底线,你就能用指针写出既高效又稳定的C语言代码。





关键词: 指针     运算    

助工
2026-07-04 06:57:41     打赏
2楼

指针运算非常方便程序开发,在很大程度上也便于阅读使用。


工程师
2026-07-04 06:59:35     打赏
3楼

其实,这个bug我也在项目开发中写过!

传入的内存空间是uint8_t,而flash写入又是uint32_t类型,很容易出现问题的。


助工
2026-07-04 07:03:20     打赏
4楼

楼主 牛逼

平时还没有这么总结过。指针运算只有三种分类,感觉也不难了


专家
2026-07-04 07:04:54     打赏
5楼

最后那个边界检查的应用,我平时都是再使用一个计数器。

看来,还是指针运算应用更方便。


共5条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]