1

出于对定义和范围的好奇,typedef我在 2 个 .c 文件中编写了以下 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);
}

交换文件

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 个参数,但为什么这段代码会编译并运行?

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

4

2 回答 2

5

Typedef 实际上是一个声明(它为现有类型创建别名),并且绝不限于两个“参数”。请参阅Typedef 声明 (C)基本 typedef 操作数语法

如果您编写typedef T;,则将 T 声明为未指定类型(在 C89 规范中称为“未指定类型”)。它有点像#define X定义(并且只有非常少,但从概念上讲这可能会对您有所帮助),X但预处理器会将其替换为空字符串(即 remove X)。

因此,您将是未指定typedefT,这使得您的swap函数的参数为​​未指定类型。

您在这里看到的是在 C89 中(但不是 C99,它会导致未定义的行为 - 对比 ANSI 3.5.2 和 ISOC99 6.7.2),未指定类型默认为int,这就是您的方法适用于整数但不适用于浮点数的原因Visual Studio(可能默认情况下它不允许隐式整数类型)。但是,如果您不使用 GCC,GCC 将使用浮点数对其进行编译,-Werror您可能应该使用它。

我强烈建议打开一些警告:-Wall在 gcc 中会吐出以下内容,除其他外

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

它在浮点数上“工作”的原因是因为浮点数和整数在您的机器上可能具有相同的大小(32 位)。试试双倍。或字符。或短。

swap真的是这样的:

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

和你打电话

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

尝试一下并自己比较结果。


编辑:我刚刚在 tcc 中尝试过。-Wall遗憾的是,它不会提醒您注意隐式 int 类型。

于 2013-05-14T04:00:19.970 回答
2

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

  • int、signed、signed int 或无类型说明符

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

你的类型定义:

typedef T;

相当于:

typedef int T;

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

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

碰巧的是,当调用尚未声明或原型化的函数时(如调用swap()in时发生的那样main.c),编译器会将默认参数提升应用于算术参数并假设函数返回int. 您的调用swap()传递了两个 type 参数int*,因此不会发生提升(它们是指针参数,而不是算术)。这恰好正是 for 的定义所swap()期望的,因此函数调用有效(并且是明确定义的行为)。

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

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

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

于 2013-05-14T06:07:45.877 回答