在VS2010中没有参数原型和64位体系结构的ANSI C之前,第二个和第三个参数消失了

在VS2010中没有参数原型和64位体系结构的ANSI C之前,第二个和第三个参数消失了,c,visual-studio-2010,function-prototypes,C,Visual Studio 2010,Function Prototypes,我知道这很奇怪,但事情是这样的 我正在管理一个非常古老的前ANSI C代码库。信不信由你,下面的代码实际上是编译的 myprog.c: // Prototype. int rec_index(); // Zero arguments. kz = rec_index(1, 19, 2, 0, " ", 0, -1); // Seven arguments. int rec_i

我知道这很奇怪,但事情是这样的

我正在管理一个非常古老的前ANSI C代码库。信不信由你,下面的代码实际上是编译的

myprog.c

// Prototype.
int rec_index();                                           // Zero arguments.

kz = rec_index(1, 19, 2, 0, "  ", 0, -1);                  // Seven arguments.
int rec_index(flag, type, from, to, c, s, status, t)       // Eight arguments.
    int flag, type, from, to, s, status, t;
    char *c;
{ 
    printf("%d %d %d", flag, type, from);

    if (flag < 0 || flag > 2) {
        return -1;
    }

    if (type < 1 || type > 20) {         // Line A.
        return -1;
    }

    if ((flag == 1) && (type < 7 || type == 12 || type == 13)) {     // Line B.
        // Line C.
    }
}
recindex.c

// Prototype.
int rec_index();                                           // Zero arguments.

kz = rec_index(1, 19, 2, 0, "  ", 0, -1);                  // Seven arguments.
int rec_index(flag, type, from, to, c, s, status, t)       // Eight arguments.
    int flag, type, from, to, s, status, t;
    char *c;
{ 
    printf("%d %d %d", flag, type, from);

    if (flag < 0 || flag > 2) {
        return -1;
    }

    if (type < 1 || type > 20) {         // Line A.
        return -1;
    }

    if ((flag == 1) && (type < 7 || type == 12 || type == 13)) {     // Line B.
        // Line C.
    }
}
太好了。该方法返回预期的行为

然而

我们最近开始将软件移植到64位环境。在64位配置中,无论传递给函数的是什么,函数中八个参数中的第二个和第三个参数被读取为零

因此,在函数调用中传入1、19和2,并检查函数中的参数,VisualStudio将它们报告为1、0和0

1 0 0
奇怪的是,第四个到第八个参数正确通过。只有第二个和第三个是错误的

这就像站在某人旁边,拿着一个空钱包,插入3美元,递给他们钱包,然后他们打开钱包发现钱包是空的。价值观去了哪里

但它变得更奇怪了。我将
type
作为19传入,但函数报告其值为零。(这是通过检查内存位置
&键入
)来备份的)让我们看一下

现在我们来看看recindex.c中标记为A行的
if
语句:

    if (type < 1 || type > 20) {         // Line A.
同样,
type
应该是零,
flag
是(实际上未被触及)1。这将使整个
if
条件为真,并落入到第C行的循环中

指令指针按预期跳入循环,但是
type
的值从0变为某个大值,总是不同的,大约5000000左右如果条件有任何副作用,
中没有任何内容,那么该值是如何变化的

我想可能是由于函数原型不一致导致的未定义行为导致了某种堆栈损坏。同样,它实际上在32位环境中工作,并且已经运行了20多年

这里正确的答案是重写函数调用,以便所有调用都采用8个参数,与函数声明相匹配,并使用
int32\t
等类型,而不是
int
,以便我们可以确定大小。我很可能会那样做

然而,我想解释一下为什么我会看到自己的行为

  • 为什么输入函数后,函数的第二个和第三个参数会被重新写为零

  • 为什么其中一个参数的值虽然看起来是零,但在计算时却表现得不像零

  • 当没有任何改变时,是什么导致该值改变

我发现了这一点

我提到(有点困惑)我正在调用这个函数:

int rec_index(flag, type, from, to, ck, sec, status, tert)
    int flag, type, from, to, sec, status, tert;
    char *ck;
{ … }
在我写的帖子中,对这个函数的调用是这样的:

  kz = rec_index(1, 19, 2, 0, "  ", 0, -1);                  // Seven arguments.
但为了避免在公共互联网论坛上分享太多内部片段,我实际上解释了以下内容,真正的代码:

kz = rec_index(1, 19, trp->iztabl, 0, "  ", 0, -1);
我假设不一致的原型导致了腐败。当然不是——真正的问题在于第三个参数,
trp->iztabl
。变量
trp
的类型为
struct-tran*
,是指向结构的指针。在反省实际值并尝试取消引用该值时,我发现
trp
是一个错误的指针。不是空指针,而是坏指针——只是胡言乱语,指向未知的内存区域

当目标代码命中错误指针时,它或者不知道如何使用箭头运算符,或者更可能的是,它会转到
trp
指定的内存中的适当位置,并向前跳转与结构中的
iztabl
位置对应的字节数。尽管如此,人们还是会认为这只是一种胡言乱语

问题/编译器错误/一般烦恼在于,箭头运算符的失败应用程序返回的值是64位指针,因为我们处于64位环境中,而不是定义为
trp->iztabl
的32位int。由于对
rec_index()
的调用由相邻的32位整数组成,因此插入64位指针的行为具有破坏性。这就是为什么第二和第三个论点消失了。
trp->iztabl
的返回值取出它自己的内存位置和它前面的内存位置(由于endianness)


我看到的另一个行为与调试器中重写的值有关。我怀疑调试器的预期行为与此代码提供的不同,因此它在运行时会感到困惑。

当您说“Visual Studio将它们报告为1、0和0”时,您是在IDE中通过Watch窗口查看它们,还是通过
printf
语句查看它们?实际上是在watches中。这里的printf
printf
只是为了说明会发生什么。在这种情况下,我认为调试器很可能被搞糊涂了。(调试器并不完美!)。我怀疑1、19、2的正确值实际上存在(正如通过if语句的程序流所建议的那样)。我希望A行中的
if
计算为true for real并返回,而不是继续。在Visual Studio中打开内存视图时,第二个参数的值也会被更新(错误)。我的猜测是,优化和调试器的组合在ANSI之前的代码中不太合适——可以理解,因为它可能没有经过太多测试。如果您真的很想弄明白这一点,请尝试在关闭所有优化的情况下进行编译,看看它是否表现得更好--抱歉,我不知道Visual Studio,所以我无法直接告诉您如何关闭优化。