inline函数参数会复制为新变量还是复用原输入变量?
typedef uint32_t StrTableKey_t; typedef struct StrTableEntry_s { char *string; StrTableKey_t keyhash, keyid; size_t usages; } StrTableEntry_t; typedef struct StrTable_s { StrTableEntry_t *entries; StrTableKey_t count; StrTableKey_t capacity; } StrTable_t; inline bool StrTableBinarySearchUnsafe (const StrTable_t *table, StrTableKey_t keyid, size_t *output) { size_t idxlow = 0, idxhigh = table->count - 1; while (idxlow <= idxhigh) { size_t idxmid = (idxlow + idxhigh) >> 1; if (table->entries[idxmid].keyid == keyid) { (*output) = idxmid; return true; } else if (table->entries[idxmid].keyid > keyid) { // 防止溢出bug if (idxmid == 0) { break; } idxhigh = idxmid - 1; } else { idxlow = idxmid + 1; } } return false; } char* StrTableGetValueByKey (const StrTable_t *table, StrTableKey_t keyid) { size_t idx; if (table->count != 0 && StrTableBinarySearchUnsafe(table, keyid, &idx)) { return table->entries[idx].string; } return NULL; }
问题
StrTableBinarySearchUnsafe是项目中被多次调用的通用查找函数,负责在字符串表中按ID查找元素。- 我理解
inline关键字会让编译器把这个函数的指令内联到调用它的StrTableGetValueByKey里,但有个疑问:传递的参数(table、keyid、&idx)会被复制成新变量,还是直接复用原函数里的变量? - 如果参数会被复制,
StrTableGetValueByKey的栈空间占用几乎会翻倍;如果不复制,要是StrTableBinarySearchUnsafe修改了参数,就会影响到StrTableGetValueByKey里的变量。 - 我分别在开启和关闭
-O3优化的情况下做了测试,看起来参数是被复制的,或者编译器能智能判断哪些值需要复制,想确认这个结论是否正确。
编辑1
感谢指出我违反了私有标识符命名规则,原函数名
_StrTableBinarySearchUnsafe已改为StrTableBinarySearchUnsafe。我之前以为下划线开头(不含大写)可以用来标记翻译单元内的私有内容,现在改用后缀
Unsafe来实现同样的标识目的。
解答
内联函数的参数处理没有固定规则,完全由编译器根据优化策略、函数逻辑和目标架构自主决定,结合你的代码和测试场景可以做如下具体分析:
针对你的代码参数:
table是const指针,函数内部仅做读操作,编译器完全可以直接复用StrTableGetValueByKey里的原指针,不需要复制——复制没有任何收益,反而增加额外开销。keyid是值传递的基础类型(uint32_t),编译器大概率会直接把它放到寄存器里复用,甚至不会分配栈空间;就算在无优化(-O0)的情况下生成栈上副本,也是编译器为简化调试做的特殊处理,并非内联本身的强制要求。&idx是指针参数,函数内部通过它修改idx的值——这里不存在"复制"的说法,传递的是idx的地址,内联后编译器会直接把对*output的操作映射到StrTableGetValueByKey里的idx变量,不需要额外中间变量。
关于你的测试结果:
- 开启
-O3时,编译器会做极致优化:所有可复用的变量都会直接复用,甚至可能把idx、keyid这类变量完全优化到寄存器中,连栈空间都不会分配。你看到的"复制"可能是反汇编时的表象(比如寄存器临时移动),本质并非栈上的复制操作。 - 关闭优化(
-O0)时,编译器为方便调试,会给每个函数的参数分配独立栈空间,哪怕内联后也会保留这种"伪函数调用"的栈布局,此时看起来像是参数被复制,但这是调试模式的特殊处理,不是内联的固有行为。
- 开启
关于栈空间翻倍的担忧:
完全没必要——内联函数的核心优势就是消除函数调用开销,同时让编译器有机会合并栈空间。就算在-O0下,栈空间的增加也只是因为编译器保留了调试用的参数副本,实际优化后的运行版本不会有这种问题。另外你的函数参数里没有大结构体,就算复制也不会带来明显的栈开销。
内容的提问来源于stack exchange,提问作者magg
相关产品推荐
相关产品推荐

