调用参数超过预期的C函数安全吗?

调用参数超过预期的C函数安全吗?,c,C,我在维基百科上读到了关于cdecl呼叫约定的内容。因为参数是按相反的顺序推送到堆栈上的,所以我相信调用参数比预期多的C函数是安全的 我是对的还是我错过了什么 注意:我不是说可变函数。是的,它是安全的,部分原因是您给出的(参数按相反顺序推送),部分原因是调用约定 C呼叫约定 C调用约定是调用方清理参数。 (另一种选择是被调用方进行清理) 因为调用者知道它推送了多少个参数,所以无论被调用者使用或期望多少个参数,它都知道要正确清理多少个参数 按相反顺序推送参数 当以相反的顺序将参数推送到堆栈上时,第

我在维基百科上读到了关于cdecl呼叫约定的内容。因为参数是按相反的顺序推送到堆栈上的,所以我相信调用参数比预期多的C函数是安全的

我是对的还是我错过了什么


注意:我不是说可变函数。

是的,它是安全的,部分原因是您给出的(参数按相反顺序推送),部分原因是调用约定

C呼叫约定
C调用约定是调用方清理参数。
(另一种选择是被调用方进行清理)

因为调用者知道它推送了多少个参数,所以无论被调用者使用或期望多少个参数,它都知道要正确清理多少个参数


按相反顺序推送参数
当以相反的顺序将参数推送到堆栈上时,第一个参数被推到最后一个。无论推送了多少个参数,被调用方总是知道在堆栈顶部的何处找到param#1。(以及参数2、3等)


如果堆栈约定被颠倒,param 1将首先放在堆栈上,并且可以被任意数量的后续参数“掩埋”;被调用者不知道要查看堆栈的深度。

您做出了一个大错特错的假设:所谓的C调用约定对于C来说不是契约性的。

虽然旧的C编译器被迫使用这样的调用约定(即使是次优的),但由于没有函数原型,现代编译器可以(并且被允许)对除旧式和vararg函数以外的所有函数使用更高效的被调用方清理调用约定。大多数编译器都有一个开关来选择所使用的标准调用约定。

我刚刚快速查看了ISO/IEC 9899(又称C99):没有任何关于调用约定的词语,因此(如评论中所建议的)您显然不应该这样做。即使它可以在某个体系结构、某个操作系统和某个编译器的某个版本上工作,也绝对可以保证当这些参数中只有一个发生更改时,它仍然可以工作。

不是

此代码导致分段错误:

#include <stdio.h>

#define stdcall __attribute__((stdcall))

stdcall void func(int param1, int param2)
{
    printf("%d, %d\n", param1, param2);
}

int main()
{
    void(*f)(int, int, int) = func;
    f(66, 67, 666);
    f(1, 2, 3);

    return 0;
}
#包括
#定义stdcall uu属性(stdcall))
stdcall void func(int-param1,int-param2)
{
printf(“%d,%d\n”,参数1,参数2);
}
int main()
{
void(*f)(int,int,int)=func;
f(66、67、666);
f(1,2,3);
返回0;
}


这只是对其他人所指出的关于调用约定的详细说明。我相信POC有助于说明问题。

还有一个问题的标题与我的完全相同,但与GTK有关。我的是C函数,它不会编译。你到底在说什么?有意思的问题,但哪个行为不端的编译器会接受这个问题?@staticx它会编译,如果你投一个球的话。我没有深入思考这个问题,但我想到的第一个想法是…eek。你真的认为这是一个很好的设计,不能用一种更保守的方式来做,不能对滥用调用约定的哪种方式是安全的做出粗略的假设吗?如果参数按正确的顺序推送,则被调用函数将访问被移动的参数。但谢谢你的回答。没有“C呼叫约定”,只有平台约定。我严重怀疑这个答案。C对呼叫约定一无所知。为什么有人质疑这个答案。这就是printf()在K&R C中工作的原因,尽管只是“期望”1参数,但K&R的时代已经过去了,这是一件好事,因为C89 C的可移植性大大提高了。要么这个问题太模糊,可能适用于预期某些行为(我们不知道,因为作者没有告诉任何关于目标平台、操作系统、编译器的信息),要么这个答案太模糊,因为它只代表了C实现的一个模糊子集(关于C的问题有什么不好的,不是吗?)。我在哪里可以读到更多关于这方面的信息?我怎么能在VisualStudio中看到这些呢?据我记忆所及,当我查看用VS生成的汇编代码时,它给了我一个很好的调用代码,正如abelenky所描述的。有关详细信息,请参阅编译器手册和参考平台ABI。对于要求,C标准本身。顺便说一下,微软喜欢使用CALLY清理<代码> SysSTDLID,它对VALGAR/NO原型进行了CALLY的清除。C++中没有<代码>外部“C”<代码>强制调用约定吗?不,它只应该使编译器输出函数从C中被调用,如果可能的话。但是,上面提到的,所谓的C调用约定不是约定的。C++中的<代码>外部的“C”<代码>强制调用约定吗?它可能因操作系统(及其版本)、编译器(及其版本)、硬件体系结构而有所不同……假设Linux/arm上C程序的调用约定与Linux/x86上使用的相同(例如),甚至都是不安全的,该标准明确允许调用变量函数,其参数比它预期的要多,例如,也传递给它的格式字符串(但问题不在于变量函数)。是的,但@AndréPuel明确没有要求变量函数。
void main()
。经典。
intmain()
相同的结果:)和经典一样!自1989年以来,它一直是
intmain(void)
intmain(intargc,char*argv[])
的。你应该拥有
intmain(void)
使它符合标准。@Nick:在我看来
foo()
foo(void)
总是K&R与C89(分别是K&R,2.ed)。K&R语法对我来说总是非常可怕,