为什么C有这么多不同的类型?

为什么C有这么多不同的类型?,c,type-conversion,C,Type Conversion,我编写了一个简单的计时器函数来计算start和end double mytimer(struct timeval *start, struct timeval *end) { return (end->tv_sec - start->tv_sec) + (end->tv_usec - start->tv_usec)*1e-6; } gcc给出了以下警告: 警告:从“\uu suseconds\u t”转换为“double”可能会改变其值 警告:从“时间”转换

我编写了一个简单的计时器函数来计算
start
end

double mytimer(struct timeval *start, struct timeval *end)
{
    return (end->tv_sec - start->tv_sec) + (end->tv_usec - start->tv_usec)*1e-6;
}  
gcc给出了以下警告:

警告:从“\uu suseconds\u t”转换为“double”可能会改变其值
警告:从“时间”转换为“双精度”可能会改变其值

以下是timeval的定义:

struct timeval {
    time_t      tv_sec;     /* seconds */
    suseconds_t tv_usec;    /* microseconds */
};
所以我的问题是为什么C定义了这么多不兼容的类型,而不是简单地使用基本类型,比如
int
short
。。。?它一点也不友好。
我如何对这些类型进行算术运算

更新
你们大多数人似乎忽视了我的第二个问题。什么是标准的添加方式,如<代码>时间> t/<代码>和>代码> SuxOnsds < /P> < P>作为正确的蝴蝶正确指出,C++语言和标准库设计的目的是在编译时强制逻辑正确。 这意味着我们的目标是一个在某些模糊情况下会做错误事情的程序不会编译(或者会编译,但会警告您)的情况

这意味着您可以在软件在测试工具中运行之前修复这些逻辑错误,更不用说在客户面前了

结果是,正确编写的C++代码在运行前可以被证明是“正确的”,这意味着你花费了很多时间来追踪你可能会遇到的模糊错误。 P>魔术的C++是执行这一惊人的壮举,同时提供了一个奇妙的效率和代码优化。 注意:所谓“正确”,我的意思是它将可靠地执行您认为您要它执行的操作。还是要由你来写正确的逻辑

关于问题:

所以我的问题是为什么C定义了这么多不兼容的类型,而不是简单地使用诸如int short。。。?它一点也不友好。 它们不兼容是为了故意阻止您将它们相互转换。它们代表不同的概念,就像速度和距离是不同的概念一样。它们之间没有直接的转换

我如何对这些类型进行算术运算? 使这些类型可以安全地转换为算术的中间结果,而不会丢失精度。在本例中,tv_sec和tv_usec是整数类型,因此即使它们彼此不兼容,它们也可以分别转换为双精度

例如:

double mytimer(struct timeval *start, struct timeval *end)
{
    return double(end->tv_sec - start->tv_sec) + 
           double(end->tv_usec - start->tv_usec) * 1e-6;
}

因为
time\u t
etc包含的内容是实现定义的,所以没有任何规定它们应该包含作为整数的秒数,就像代码中的注释所暗示的那样。原因是他们希望这些类型可以在不同的系统之间移植


实际上,
time.h
确实相当麻烦,因此大多数程序最终都会调用特定于系统的函数。

原因是int等内置类型依赖于平台。因此,在一台计算机上,int可能足以存储时间值,而在另一台计算机上,则需要较长的时间。为了允许人们编写在所有平台上运行的程序,引入了诸如time\t之类的类型,这些类型通常只是适用于特定平台的某些基本类型的别名定义。事实上,这在一开始需要更多的学习努力,但从长远来看,这种努力将带来巨大的回报

有道理,不是吗


[编辑]:至于奇怪的警告:编译器警告将时间和秒数转换为双精度可能会丢失一些信息。这是因为这两种类型都是整数类型,其位数都大于double的尾数部分。在您的情况下,这只适用于非常大的时间值,因此您可以忽略这些警告。(但编译器如何知道时间t值通常与double匹配呢?因此他发出了这个警告。)事实上,如果不使代码依赖于平台,就无法做到这一点

原因称为类型安全。并非所有类型的表达式在工作程序中都有意义。类型安全意味着编译器将拒绝允许不安全、无效或不适当的操作

从长远来看,拒绝这些糟糕代码的编译器可以节省程序员的工作,因为程序员在没有帮助的情况下检测问题比编译器需要更多的时间

C和C++有多种类型的安全特性。其他编程语言拥有更多,其他语言拥有更少。这仅仅意味着某些编程风格在不同的编程语言中更有效

警告是一种不太规范的类型安全形式-它们会导致编译器对可疑的内容发出警告,而不是完全拒绝它们。

避免警告

double mytimer(struct timeval * start, struct timeval * end) {
    long usec = end->tv_usec - start->tv_usec;
    long sec = end->tv_sec - start->tv_sec;
    return 1.0 * sec + 1e-6 * usec;
}
如果您看到
中定义的数据类型,您会发现

typedef long time_t;
...
typedef long suseconds_t;

struct timeval
没有特定的格式说明符,但结构成员的类型为long。希望这能解决您的问题。

[N.B.自从我第一次发布这个答案以来,我几乎完全重写了它。]

第一个问题的答案是,C有这么多类型,试图平衡支持所有不同字号机器的需求,并具有合理的可移植性。事情变得更加复杂,因为人们希望能够合理地便携地支持诸如“数据结构的大小”、“文件中的偏移量”和“真实世界中的时间”之类的专门数量,尽管有时这些特殊数量最终不是由语言规范或编译器决定的,而是由底层操作系统决定的

通常,从大整数类型转换为浮点类型时,有两个问题:

  • 浮点型
    return (sometype)(end->tv_sec - start->tv_sec) +
           (sometype)(end->tv_usec - start->tv_usec) / 1e6;
    
    float f = (float)2000000123L - (float)2000000000L;
    printf("%f\n", f);
    
    double d = (double)9000000000000001234LL - (double)9000000000000000000LL;
    printf("%f\n", d);
    
    f = 2000000123L - 2000000000L;
    d = 9000000000000001234LL - 9000000000000000000LL;
    
    f = (float)(2000000123L - 2000000000L);
    d = (double)(9000000000000001234LL - 9000000000000000000LL);
    
    long double ld = (long double)9000000000000001234LL - (long double)9000000000000000000LL;
    printf("%Lf\n", ld);
    
    return difftime(end->tv_sec - start->tv_sec) +
           (double)(end->tv_usec - start->tv_usec) / 1e6;