对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
)编译和运行代码,因此它就像模板方法一样工作!
答案 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“类型说明符” - 强调添加):
因此,如果声明中没有提供类型说明符(包括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
类型的预标准代码。