You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义printf处理uint64_t转字符串遇坏数据及sizeof输出异常

业余内核开发中的两个问题:printf输出乱码与结构体大小异常

问题详情

我在做业余内核开发,目前在处理内存映射,自己实现了printf()函数。其中%llu的处理代码是这样的:

uint64_t llu = va_arg(parameters, unsigned long long);
char buf[21] = {0};
char* ptr = (char*)(&buf[21]);
if (llu == 0) {
    *--ptr = '0';
}
while (llu) {
    *--ptr = (char)('0' + (llu %10));
    llu /= 10;
}
size_t len = strlen(ptr);
if (maxrem < len){
    // TODO: Set errno to EOVERFLOW.
    return -1;
}
if (!print(ptr, len))
    return -1;
written += len;
format++;

%hhu的处理逻辑和这个完全一样,只是用了4字节的缓冲区,循环3次。但运行printf("%hhu %llu\n", 25, 25ULL)时,uint64_t的输出后面会跟着两个乱码字符╛);另外,我定义了一个__attribute__((packed))的结构体mem_map_blk,成员是uint64_t+uint64_t+uint32_t+uint32_t,理论上大小是24字节,但打印sizeof(mem_map_blk)的时候得到了一个超大的数32886564585496。

下面是最小复现代码(在qemu-system-x86_64上内核输出不一样,但问题依然存在):

#include <iostream>
#include <cstdio>
#include <cstdint>

void ull_format(unsigned long long llu){
    char buf[21] = {0};
    char* ptr = (char*)(&buf[21]);
    if (llu == 0) {
        *--ptr = '0';
    }
    while (llu) {
        *--ptr = (char)('0' + (llu%10));
        llu /= 10;
    }
    printf("%s ", ptr);
}

struct __attribute__((packed)) mem_map_blk {
    uint64_t base;
    uint64_t length;
    uint32_t type;
    uint32_t ACPI_attribs;
};

int main() {
    ull_format(25);
    ull_format(sizeof(mem_map_blk));
}

问题1:%llu输出后乱码的解决方法

原因

你写的char buf[21] = {0};只会把缓冲区的第一个元素设为0,剩下的20个元素都是内存里的垃圾值。当你从&buf[21]往前写字符时,生成的字符串ptr后面没有正确的终止符,strlen(ptr)会一直读到内存中第一个0为止,自然就把后面的垃圾字符带进去了。

修复

要么确保整个缓冲区都被初始化为0,要么手动给字符串加终止符:

  • 方法一:用memset清空整个缓冲区
char buf[21];
memset(buf, 0, sizeof(buf));
  • 方法二:直接计算字符串长度,避免依赖strlen(更安全)
// 循环结束后直接计算长度,不用strlen
size_t len = &buf[21] - ptr;
// 手动添加终止符
ptr[len] = '\0';

注意%hhu的处理逻辑也有同样的问题,要一起修复。

问题2:结构体sizeof值异常的解决方法

原因

核心是可变参数的类型不匹配。sizeof的返回值类型是size_t,如果你的目标平台上size_t和unsigned long long的宽度不一样(比如32位平台上size_t是32位),用va_arg(parameters, unsigned long long)去取size_t类型的参数,就会读错内存位置,得到一堆垃圾值。

另外在内核的printf里,用%llu去打印size_t类型的值,本质就是类型不匹配,必然出问题。

修复

  1. 给你的printf添加%zu格式说明符的支持,专门处理size_t类型:
// 处理%zu的分支
size_t sz_val = va_arg(parameters, size_t);
// 然后用和%llu类似的逻辑转字符串,注意缓冲区大小要匹配size_t的最大位数
  1. 临时方案:打印sizeof结果时,显式转成unsigned long long:
ull_format((unsigned long long)sizeof(mem_map_blk));
  1. 检查内核中可变参数的取参逻辑,确保每次va_arg的类型和实际传入的参数类型严格一致,不然会破坏参数栈,后续所有取参都会错。

内容的提问来源于stack exchange,提问作者Carlos Overstreet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.02 05:54:53