C 语言函数返回指针时,如何同时传递错误信息?传统做法要么返回 NULL(丢失错误原因),要么通过额外输出参数传递错误码。Linux 内核采用了一个巧妙的设计:将错误码编码到指针中,一次返回同时包含指针和错误信息。
struct my_struct *ptr = some_function();if (IS_ERR(ptr)) {int err = PTR_ERR(ptr); // 提取错误码return err;}// 正常使用 ptr...
错误码(负数) → 强制转成指针 → 变成一个高地址值 → 这个地址永远不会被分配
// include/linux/err.h#define MAX_ERRNO 4095#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)staticinlinevoid *ERR_PTR(long error){ return (void *)error; }staticinlinelongPTR_ERR(constvoid *ptr){ return (long)ptr; }staticinlineboolIS_ERR(constvoid *ptr){return IS_ERR_VALUE((unsigned long)ptr);}
0x0000000000000000 ────► 用户空间...0xffff800000000000 ────► 内核空间起始...0xffffffffffffefff ────► 有效内核指针最高地址0xfffffffffffff000 ────► 错误码区域起始(-4095)...0xffffffffffffffff ────► 错误码区域结束(-1)
具体原理:
-ENOMEM = -12(void *)-12 把它强制转成指针-12 转换成无符号 64 位整数后是 0xfffffffffffffff4(一个极高的地址)0xfffffffffffff000 ~ 0xffffffffffffffff)IS_ERR() 只需判断指针值是否落在这个"禁区"内:// 核心判断逻辑#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-4095)// -4095 转成无符号 = 0xfffffffffffff001// 任何 >= 这个值的指针都被认为是错误码

struct device *dev = get_device();if (IS_ERR(dev))return ERR_CAST(dev); // struct device * → struct my_obj *
// 返回错误指针的函数struct my_obj *my_obj_create(int id){if (id < 0)return ERR_PTR(-EINVAL);struct my_obj *obj = kzalloc(sizeof(*obj), GFP_KERNEL);if (!obj)return ERR_PTR(-ENOMEM);obj->id = id;return obj;}// 调用方struct my_obj *obj = my_obj_create(42);if (IS_ERR(obj)) {pr_err("create failed: %ld\n", PTR_ERR(obj));return PTR_ERR(obj);}// 使用 obj...kfree(obj);
// ❌ 忘记检查就解引用ptr = ERR_PTR(-ENOMEM);ptr->data = 1; // 崩溃!// ❌ 用 NULL 检查代替 IS_ERRif (!ptr) { } // ERR_PTR 不为 NULL,捕获不到错误// ✅ 正确if (IS_ERR_OR_NULL(ptr)) { }// ❌ 错误码 0 被当作 NULLreturn ERR_PTR(0); // 实际返回 NULL// ❌ 错误码符号混乱return -PTR_ERR(ptr); // 负数变正数,破坏错误码
错误码范围:仅支持 -1 ~ -4095(已覆盖所有标准错误码)
无类型安全:编译器无法区分普通指针和错误指针,需程序员自己保证
下次阅读内核源码时,看到 IS_ERR() 你就知道它在做什么了!
往期推荐: