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

调用C函数传入超预期参数是否合法?含函数指针转换场景

关于C语言函数调用传参的合法性问题

背景

我用C语言实现了一个红黑树,为支持可变类型,仅处理const void *类型元素,初始化树时需传入签名为int (*comp)(const void *, const void *)的比较函数。目前一切正常,但我尝试用该C代码构建Python扩展模块,初看很简单,因为Python始终传递对象引用,C例程会将其作为指针接收。

问题

Python对象带有*富比较(rich comparison)*运算符,这意味着在C扩展模块中比较两个任意对象很简单:只需使用int PyObject_RichCompareBool(PyObject *o1, PyObject *o2, int opid)。

但该比较可能返回-1表示对象不可比较。在Python或C++中,抛出异常即可轻松标记异常情况,但C语言没有异常概念,且我无法使用setjmp-longjmp,原因如下:

  • 环境缓冲区需被外层函数和内部函数同时知晓
  • 执行longjmp时需释放已分配内存,但内部函数不知道已分配了什么

初步思路

一个简单的解决方案是给比较函数添加第三个参数以标记异常情况,但在纯C环境下使用该库时,这个第三个参数毫无意义。我想起早年学到的知识:C语言中参数以逆序传入栈中,由调用者清理栈,以支持可变参数函数。这意味着只要前两个参数正确,传入第三个参数给预期仅两个参数的函数应该是无害的。

演示代码

#include <stdio.h>

// declares a type for the comparison functions
typedef int (*func)();

// A simple function for comparing integers - only 2 params
int f1(int a, int b) {
    return a - b;
}

/* Inserts a value into an increasing array
* By convention 0 denotes the end of the array
* No size control implemented for brievety
* The comp function recieves a pointer to an int
* to be able to signal abnormal conditions
* */
int insert(int* arr, int val, func comp) {
    int err = 0;
    while ((0 != *arr) && (comp(*arr, val, &err) < 0)) { // 1
        if (err) return 0;
        ++arr;
    }
    do {
        int tmp = *arr;
        *arr = val;
        val = tmp;
    } while (0 != *arr++);
    return 1;
}
int main() {
    func f = &f1;
    // a simple test with 3 parameters
    int cr = f(3, 1, 5);  // 2
    printf("%d\n", cr);

    // demo usage of the insert function
    int arr[10] = {0};
    int data[] = { 1,5,3,2,4 };
    for (int i = 0; i < sizeof(data) / sizeof(*data); i++) {
        insert(arr, data[i], f1);
    }
    for (int i = 0; i < sizeof(data) / sizeof(*data); i++) {
        printf("%d ", arr[i]);
    }
    return 0;
}

在(1)和(2)处,调用仅需两个参数的函数时传入了三个参数。这段代码在Clang或MSVC中编译甚至不会发出警告,且运行正常,得到预期结果。

核心问题

尽管这段代码在常见实现中运行正常,但我想知道:传入多于预期参数给仅需两个参数的函数是否真的合法,还是会触发未定义行为?

现有调研

  • 相关讨论指出,当使用C调用约定时这种操作通常是安全的,但MSVC的stdcall调用约定不允许这种操作
  • 查阅C11标准草案n1570中的6.7.6.3函数声明符(包括原型)和6.5.2.2函数调用部分,但由于语言障碍,无法明确其中是否允许这种操作

说明

本问题的独特之处在于使用了函数指针转换。

内容的提问来源于stack exchange,提问作者Serge Ballesta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 22:45:11