在哪里可以找到off_t类型的完整定义?

96
我正在使用TCP从客户端向服务器发送文件。为了标记文件的结尾,我想在实际数据之前发送文件大小。因此,我使用stat系统调用来查找文件的大小。这是off_t类型的。我想知道它占用多少字节,以便在服务器端正确读取它。它在<sys/types.h>中定义。但我不理解这个定义。它只是将__off_t或_off64_t定义为off_t。在哪里查找__off_t?另外,是否惯例在头文件中大多数东西都有前缀“__”,当我阅读头文件以更好地理解时,这使我感到害怕。如何更好地阅读头文件?
#ifndef __off_t_defined
# ifndef __USE_FILE_OFFSET64
typedef __off_t off_t;
# else
typedef __off64_t off_t;
# endif
# define __off_t_defined
#endif 

7
以“__”开头的任何内容都是实现保留的(除非标准定义了它的含义,比如“__func__”或“__FILE__”)。间接级别使得实现可以定义自己的类型“__off_t”,而不会干扰您可以合法执行的任何操作。这样就可以更好地隐藏平台特定的头文件部分(例如,单个源代码副本就可以处理在一台机器上的32位和64位编译)。阅读标准头文件是一项重大的任务,因为有很多相互关联的定义。 - Jonathan Leffler
4个回答

122
由于这个答案仍然被投票,我想指出你几乎永远不需要查看头文件。如果您想编写可靠的代码,最好查看标准。因此,“在哪里可以找到off_t的完整定义”的答案是“在标准中而不是头文件中”。遵循标准意味着您的代码将在任何机器上今天和明天都能正常工作。
在这种情况下,off_t没有由C标准定义。它是POSIX标准的一部分,您可以在这里浏览
不幸的是,off_t没有非常严格的定义。我能找到的唯一定义是在sys/types.h页面上:

blkcnt_toff_t应为有符号整数类型。

这意味着您无法确定其大小。如果您正在使用GNU C,则可以使用下面的答案中的指令确保它为64位。或者更好的方法是,在将其放入线路之前将其转换为标准定义大小。这就是像Google的Protocol Buffers项目如何工作的(虽然那是一个C++项目)。

为了完整起见,这是“哪个头文件定义了off_t?”的答案:

在我的机器上(以及大多数使用glibc的机器上),您将在bits/types.h中找到定义(如一个注释所述,在顶部从未直接包括此文件),但它被一堆宏所遮蔽。尝试解开它们的替代方法是查看预处理器输出:

#include <stdio.h>
#include <sys/types.h>

int main(void) {
  off_t blah;

  return 0;
}

然后:

$ gcc -E sizes.c  | grep __off_t
typedef long int __off_t;
....

然而,如果您想知道某个东西的大小,您总可以使用 sizeof() 运算符。

编辑:刚看到你问题中关于 __ 的部分。这个回答有很好的讨论,关键点是以 __ 开头的名称被保留用于实现(因此您不应该以 __ 开始自己的定义)。


1
off_t 在不同的机器(或编译器)上可能有不同的大小,这是事实。请注意,在 GCC 中,您可以使用 -D_FILE_OFFSET_BITS=64 来获得 8 字节的 off_tsize_t 定义。 - Timothy Jones
5
long long在两台不同的机器上的大小不能保证相同 - 其确切大小没有标准化。 - Timothy Jones
@FourOfAKind,off_t是有符号的,因此对于4字节整数而言,它的范围是+/- 2gig,而不是0...4gig。 - user3629249
“shall be signed integer types”. 如果它们代表文件的大小,那么为什么要使用“signed”? - Rodrigo
使用链接网站的搜索框搜索 off_t 没有返回任何结果。但是它确实存在 - 直接的 URL 是 https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html。 - AJM
显示剩余4条评论

50

正如《GNU C库参考手册》所说:

off_t
    This is a signed integer type used to represent file sizes. 
    In the GNU C Library, this type is no narrower than int.
    If the source is compiled with _FILE_OFFSET_BITS == 64 this 
    type is transparently replaced by off64_t.

off64_t
    This type is used similar to off_t. The difference is that 
    even on 32 bit machines, where the off_t type would have 32 bits,
    off64_t has 64 bits and so is able to address files up to 2^63 bytes
    in length. When compiling with _FILE_OFFSET_BITS == 64 this type 
    is available under the name off_t.

因此,如果您想要一种可靠的表示客户端和服务器之间文件大小的方法,您可以:

  1. 使用 off64_t 类型和相应的 stat64() 函数(因为它填充包含 off64_t 类型本身的结构 stat64)。类型 off64_t 保证在32位和64位机器上具有相同的大小。
  2. 如前所述,使用 -D_FILE_OFFSET_BITS == 64 编译您的代码,并使用通常的 off_tstat()
  3. off_t 转换为带有固定大小的 int64_t 类型(C99 标准)。 注意:(我的书 'C in a Nutshell' 说这是 C99 标准,但实现中是可选的)。最新的 C11 标准说:
7.20.1.1 Exact-width integer types

    1 The typedef name intN_t designates a signed integer type with width N ,
    no padding bits, and a two’s complement representation. Thus, int8_t 
    denotes such a signed integer type with a width of exactly 8 bits.
    without mentioning.

关于实施:

7.20 Integer types <stdint.h>

    ... An implementation shall provide those types described as ‘‘required’’,
    but need not provide any of the others (described as ‘‘optional’’).
    ...
    The following types are required:
    int_least8_t  uint_least8_t
    int_least16_t uint_least16_t
    int_least32_t uint_least32_t
    int_least64_t uint_least64_t
    All other types of this form are optional.
因此,一般来说,C标准不能保证具有固定大小的类型。但大多数编译器(包括gcc)支持此功能。
因此,通常情况下,C标准无法确保具有固定大小的类型。但是,大多数编译器(包括gcc)都支持这个特性。

这是一种有符号整数类型,用于表示文件大小。如果它们表示文件的大小,为什么要使用“有符号”呢? - Rodrigo
(1) 因为返回负值以指示错误很方便。 (2) 因为该句应该说“这是一个有符号整数类型,用于表示文件偏移量”,即向前或向后寻找。 - William Cushing

9
如果您在跟踪定义时遇到困难,可以使用编译器的预处理输出,它将告诉您所需了解的所有内容。例如:
$ cat test.c
#include <stdio.h>
$ cc -E test.c | grep off_t
typedef long int __off_t;
typedef __off64_t __loff_t;
  __off_t __pos;
  __off_t _old_offset;
typedef __off_t off_t;
extern int fseeko (FILE *__stream, __off_t __off, int __whence);
extern __off_t ftello (FILE *__stream) ;

如果你查看完整的输出,甚至可以看到确切的头文件位置和定义它的行号:
# 132 "/usr/include/bits/types.h" 2 3 4


typedef unsigned long int __dev_t;
typedef unsigned int __uid_t;
typedef unsigned int __gid_t;
typedef unsigned long int __ino_t;
typedef unsigned long int __ino64_t;
typedef unsigned int __mode_t;
typedef unsigned long int __nlink_t;
typedef long int __off_t;
typedef long int __off64_t;

...

# 91 "/usr/include/stdio.h" 3 4
typedef __off_t off_t;

7
如果您正在编写可移植代码,那么答案是“您无法确定”,好消息是您不需要。您的协议应包括将大小写为(例如)“8个八位组,大端格式”(最好检查实际大小是否适合8个八位组)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接