为什么这个C程序符合并运行

时间:2013-05-14 03:42:11

标签: c typedef c99 implicit c89

typedef的定义和范围有好奇心,我在2 .c文件中写了下面的C代码:

的main.c

#include <stdio.h>

int main()
{
    int a = 5, b = 6;
    printf("a = %d, b = %d\n", a, b);
    swap(&a, &b);
    printf("a = %d, b = %d\n", a, b);
}

swap.c

typedef T;

void swap(T* a, T* b)
{
    T t = *a;
    *a = *b;
    *b = t;
}

令我惊讶的是,代码文件可以使用Visual Studio C编译器(cl.exe /Tc main.c swap.c

进行编译

程序运行正常!根据我的理解,typedef需要2个参数,但为什么这个代码会编译并运行?

为了进一步分析,在main函数中,我声明了另外2个float变量,并且在交换2个整数后也尝试交换两个,但这次它无法编译(使用cl.exe)。令人惊奇的是,可以使用Tiny C(tcc.exe main.c swap.c)编译和运行代码,因此它就像模板方法一样工作!

2 个答案:

答案 0 :(得分:5)

Typedef实际上是一个声明(它为现有类型创建别名),并且绝不限于两个“参数”。请参阅Typedef Declarations (C)Fundamental typedef operand syntax

如果您编写typedef T;,则表示T是未指定的类型(在C89规范中称为“无指定类型”)。它有点(并且只有很少,但在概念上可能会对你有所帮助),例如#define X定义X,但预处理器会用空字符串替换它(即删除X)。 / p>

因此,您typedef T未明确,这会使您的swap函数的参数变为未指定类型。

你在这里看到的是C89中的 (但不是C99,导致未定义的行为 - 对比ANSI 3.5.2与ISOC99 6.7.2),未指定的类型默认为int ,这就是为什么你的方法适用于整数但不适用于Visual Studio中的浮点数(可能默认情况下它不允许隐式整数输入)。但是,GCC会使用浮点数编译它,前提是你没有使用-Werror,你可能应该使用它。

我强烈建议启用一些警告:gcc中的-Wall将吐出以下内容

swap.c:1:9: warning: type defaults to ‘int’ in declaration of ‘T’ [-Wimplicit-int]

浮点数“工作”的原因是因为浮点数和整数可能与机器上的大小相同(32位)。尝试双倍。或者char。或者简短。

swap真的是这样的:

void swap(int *a, int *b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

你打电话

swap((int*)&a, (int*)&b);

尝试并自己比较结果。


编辑:我刚刚在tcc中尝试过。遗憾的是,-Wall并没有提醒你隐式的int输入。

答案 1 :(得分:2)

在C90中(这是MSVC在编译C代码时作为基线),其中一个可能的类型说明符是(C90 6.5.2“类型说明符” - 强调添加):

  • int,signed,signed int或 no type specifiers

因此,如果声明中没有提供类型说明符(包括typedef),则类型默认为int。这通常称为“隐式int”声明。请注意,C99删除了对隐式int的支持(默认情况下,GCC仅在C99模式下编译时发出警告)。

你的typedef:

typedef T;

相当于:

typedef int T;

所以你的swap()定义相当于:

void swap(int* a, int* b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

实际上,当调用一个尚未声明或原型化的函数时(如在swap()中调用main.c时发生的那样),编译器将默认参数提升应用于算术参数并假设该函数返回int。您对swap()的调用会传递两个int*类型的参数,因此不会进行任何提升(它们是指针参数,而不是算术)。这恰好是swap()的定义所期望的,因此函数调用起作用(并且是明确定义的行为)。

现在,调用代码期望swap()返回int,因为没有看到声明,并且您的swap()函数不返回任何内容(void)。这是未定义的行为,但在这种情况下没有明显的问题(尽管它仍然是代码中的错误)。但是,如果您更改swap()的定义,以便它返回int

int swap(int* a, int* b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

未定义的行为消失了,即使swap()似乎没有返回任何内容。由于在调用站点上没有对结果进行任何操作,因此C90允许函数返回表达式。 C90允许这样做,以支持没有void类型的预标准代码。