在之前的文章使用 libserialport 来实现串口通信中有一段来自 stackoverflow 的unsigned long to string函数代码,这是一个将ul类型的数字转成string的代码,原理是计算每个数字对应的字母,然后在一段连续的内存地址上从尾至前排列。也就是下面这段代码的含义

* -- ptr = '0' + res;

由于--运算符优先级比*运算符高,所以先递减指针ptr,然后将这段地址上的值设置为'0'+res的值。

还有ptr += count;这段代码的含义是将ptr的位置向后移动count数量

只有ptr指针存储的数据类型是数组时,指针的运算符才有意义。

由这段代码想到一个简单的对一个字符串ABCDEFGH倒序排列的程序。

最终的程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char var[] = "ABCDEFGH";
    int size = sizeof(var);
    char* str = malloc(size+1);

    if (str == NULL)
    {
        printf("内存分配不成功!\n");
    }
    else
    {
        str += size - 1;

        *str = '\0';    //在末尾手动添加结束标记

        for (int i = 0; i < size - 1; i++)
        {
            *--str = var[i];
        }
        printf("输出 Str '%s'\n", str);
    }
    
    free(str);
    return 0;
}

在整个过程中经历了:算术溢出,内存泄漏,free() Invalid Pointer,取消对NULL指针的引用等等错误。

其中取消对NULL指针的引用错误只需要在malloc分配动态内存后加一个内存是否分配成功的if判断即可清除,算数溢出只需要将VS2019Debug目标改成x86平台即可清除。

而内存泄漏和free() Invalid Pointer则需要借助valgrind工具来解决了。

在使用gcc编译时加上-g选项(和gdb调试一样),然后运行valgrind即可,也可以再加上-v显示详情选项

valgrind --leak-check=full ./target_file

而关于free() Invalid Pointer,需要注意的是当初使用malloc()分配的内存与free()要释放的内存是否还是同一个地址,如果不是,那么就会产生Invalid Pointer错误,如果没有使用free()释放,则会造成内存泄漏。

针对整个代码做一下解释:

int size = sizeof(var); --> 9

A B C D E F G H Null
0 1 2 3 4 5 6 7 8

所以 char* str = malloc(size);,分配了9个字节,保持一致,最后还有一个字节存放NULL

str += size -1;

这里size -1是因为要和for (int i = 0; i < size-1 ; i++)i<size-1保持一致,最后--str递减了8次,也为了去掉var最后的NULL带来的影响。

如果这里选择str += size,那么,最终*--str会比malloc()分配时多出一个1字节,导致最后free()失败,两个内存地址不一致了。

现在终于明白在调试的时候查看指针的内存地址变化的重要性了。


参考资料:
关于*--运算符的优先级,请查阅《C语言程序设计:现代方法》(2007版)第225页
关于指针加上一个整数的含义,请查阅《C Primer Plus》(第六版·中文版)第296页
解决 C 语言中的 Free Invalid Pointer 错误
这种算术溢出是什么情况?
取消VS中“取消对NULL指针的‘、、、’的引用”的警告的方法
valgrind的使用

标签: none

评论已关闭