glibc 知:手册80:附录A:库中的 C 语言设施

glibc 知:手册80:附录A:库中的 C 语言设施,第1张

文章目录
  • 1. 前言
  • 2. 附录 A 库中的 C 语言设施
    • 2.1. 明确检查内部一致性
    • 2.2. 可变函数
      • 2.2.1. 为什么使用可变参数函数
      • 2.2.2. 如何定义和使用可变参数函数
        • 2.2.2.1. 变量参数的语法
        • 2.2.2.2. 接收参数值
        • 2.2.2.3. 提供了多少参数
        • 2.2.2.4. 调用可变参数函数
        • 2.2.2.5. 参数访问宏
      • 2.2.3. 可变函数示例
    • 2.3. 空指针常量
    • 2.4. 重要数据类型
    • 2.5. 数据类型测量
      • 2.5.1. 整数类型的宽度
      • 2.5.2. 整数类型的范围
      • 2.5.3. 浮点型宏
        • 2.5.3.1. 浮点表示概念
        • 2.5.3.2. 浮点参数
        • 2.5.3.3. IEEE 浮点
      • 2.5.4. 结构成员偏移测量
  • 3. 参考

1. 前言

The GNU C Library Reference Manual for version 2.35

2. 附录 A 库中的 C 语言设施

Appendix A C Language Facilities in the Library

C 库实现的一些功能确实应该被视为 C 语言本身的一部分。这些设施应该记录在 C 语言手册中,而不是库手册中; 但由于我们还没有语言手册,并且已经编写了这些功能的文档,所以我们将其发布在这里。

2.1. 明确检查内部一致性

Explicitly Checking Internal Consistency

在编写程序时,最好在战略位置检查“不可能”的错误或违反基本假设的情况。例如,这些类型的检查有助于调试程序不同部分之间的接口问题。

在头文件 assert.h 中定义的 assert 宏提供了一种方便的方法来中止程序,同时打印有关在程序中检测到错误的位置的消息。

一旦您认为您的程序已调试,您可以通过使用定义的宏 NDEBUG 重新编译来禁用断言宏执行的错误检查。这意味着您实际上不必更改程序源代码来禁用这些检查。

但是禁用这些一致性检查是不可取的,除非它们使程序显着变慢。在其他条件相同的情况下,无论谁在运行程序,更多的错误检查都是好的。一个聪明的用户宁愿让程序明显地崩溃,也不愿让它返回废话而不表明任何可能是错误的。

宏:void assert (int expression)

Preliminary: | MT-Safe | AS-Unsafe heap corrupt | AC-Unsafe mem lock corrupt | See POSIX Safety Concepts.

验证程序员是否相信在程序中此时表达式是非零的。

如果未定义 NDEBUG,则断言测试表达式的值。如果它为假(零),则断言在打印以下形式的消息后中止程序(请参阅中止程序):

file:linenum: function: Assertion `expression' failed.

在标准错误流 stderr 上(参见标准流)。文件名和行号取自 C 预处理器宏 FILE 和 LINE 并指定调用 assert 的位置。使用 GNU C 编译器时,调用 assert 的函数的名称取自内置变量 PRETTY_FUNCTION;对于较旧的编译器,函数名和后面的冒号被省略。

如果在包含 assert.h 之前定义了预处理器宏 NDEBUG,则断言宏被定义为完全不做任何事情。

警告:如果 NDEBUG 生效,即使参数表达式表达式也不会被计算。因此,切勿将 assert 与涉及副作用的参数一起使用。例如,断言 (++i > 0);是个坏主意,因为如果定义了 NDEBUG,我将不会增加。

有时,您要检查的“不可能”条件是 *** 作系统函数返回的错误。这样不仅可以显示程序崩溃的位置,还可以显示返回的错误。assert_perror 宏使这很容易。

宏:void assert_perror (int errnum)

Preliminary: | MT-Safe | AS-Unsafe heap corrupt | AC-Unsafe mem lock corrupt | See POSIX Safety Concepts.

与 assert 类似,但验证 errnum 是否为零。

如果未定义 NDEBUG,assert_perror 会测试 errnum 的值。如果它不为零,assert_perror 在打印以下形式的消息后中止程序:

file:linenum: function: error text

在标准错误流上。文件名、行号和函数名与 assert 相同。错误文本是 strerror (errnum) 的结果。请参阅错误消息。

与 assert 一样,如果在包含 assert.h 之前定义了 NDEBUG,则 assert_perror 宏绝对不会执行任何 *** 作。它不评估参数,因此 errnum 不应该有任何副作用。errnum 最好只是一个简单的变量引用;通常它会是错误的。

这个宏是一个 GNU 扩展。

使用说明:断言工具是为检测内部不一致而设计的;它不适用于报告程序用户的无效输入或不当使用。

assert 和 assert_perror 宏打印的诊断消息中的信息旨在帮助您(程序员)追查错误的原因,但对于告诉您的程序用户为什么他或她的输入无效或为什么无法执行命令。更重要的是,当给定无效输入时,您的程序不应该像 assert 那样中止——它应该在打印错误消息后以非零状态退出(请参阅退出状态),或者可能读取另一个命令或转到下一个输入文件。

有关打印不代表程序错误的问题的错误消息的信息,请参阅错误消息。

2.2. 可变函数

Variadic Functions

ISO C 定义了一种语法,用于声明函数采用可变数量或类型的参数。(此类函数称为可变参数函数或可变参数函数。)但是,语言本身没有为此类函数提供访问其非必需参数的机制。相反,您使用在 stdarg.h 中定义的可变参数宏。

本节介绍如何声明可变参数函数、如何编写它们以及如何正确调用它们。

兼容性说明:许多较旧的 C 方言提供了一种类似但不兼容的机制,用于使用 varargs.h 定义具有可变数量参数的函数。

2.2.1. 为什么使用可变参数函数

Why Variadic Functions are Used

普通 C 函数采用固定数量的参数。定义函数时,您为每个参数指定数据类型。对函数的每次调用都应提供预期数量的参数,以及可以转换为指定类型的类型。因此,如果函数“foo”用 int foo (int, char *) 声明;那么你必须用两个参数调用它,一个数字(任何类型都可以)和一个字符串指针。

但是有些函数执行的 *** 作可以有意义地接受无限数量的参数。

在某些情况下,函数可以通过将所有值作为块进行 *** 作来处理任意数量的值。例如,考虑一个使用 malloc 分配一维数组以保存一组指定值的函数。只要数组的长度对应于该数字,此 *** 作对任意数量的值都有意义。如果没有变量参数的功能,您将不得不为每个可能的数组大小定义一个单独的函数。

库函数 printf(请参阅格式化输出)是另一类函数的示例,其中可变参数很有用。此函数在格式模板字符串的控制下打印其参数(其类型和数量可能不同)。

这些是定义可变参数函数的充分理由,该函数可以处理调用者选择传递的尽可能多的参数。

一些函数,如 open 接受一组固定的参数,但偶尔会忽略最后几个。严格遵守 ISO C 要求将这些函数定义为可变参数;然而,在实践中,GNU C 编译器和大多数其他 C 编译器允许您定义这样的函数以获取一组固定的参数——它可以使用的最多参数——然后只将函数声明为可变参数(或不将其参数声明为全部!)。

2.2.2. 如何定义和使用可变参数函数

How Variadic Functions are Defined and Used

定义和使用可变参数函数涉及三个步骤:

  • 将函数定义为可变参数,在参数列表中使用省略号(“…”),并使用特殊宏访问变量参数。请参阅接收参数值。
  • 在调用它的所有文件中,使用带有省略号(‘…’)的原型将函数声明为可变参数。请参阅变量参数的语法。
  • 通过编写固定参数和附加变量参数来调用函数。请参阅调用可变参数函数。
2.2.2.1. 变量参数的语法

接受可变数量参数的函数必须使用说明的原型声明。你像往常一样写出固定的论点,然后加上“……”来表示附加论点的可能性。ISO C 的语法要求在‘…’之前至少有一个固定参数。例如,

int
func (const char *a, int b,)
{}

定义了一个函数 func,它返回一个 int 并接受两个必需的参数,一个 const char * 和一个 int。这些后面是任意数量的匿名参数。

可移植性说明:对于某些 C 编译器,最后一个必需的参数不得在函数定义中声明为寄存器。此外,这个参数的类型必须是自我提升的:也就是说,默认的提升不能改变它的类型。这排除了数组和函数类型,以及 float、char(无论是否有符号)和 short int(无论是否有符号)。这实际上是 ISO C 的要求。

2.2.2.2. 接收参数值

Receiving the Argument Values

普通固定参数具有单独的名称,您可以使用这些名称来访问它们的值。但是可选参数没有名字——只有“……”。您如何访问它们?

访问它们的唯一方法是按顺序,按照它们被写入的顺序,并且您必须在以下三个步骤过程中使用 stdarg.h 中的特殊宏:

  1. 使用 va_start 初始化 va_list 类型的参数指针变量。初始化时的参数指针指向第一个可选参数。

  2. 您可以通过对 va_arg 的连续调用来访问可选参数。第一次调用 va_arg 给你第一个可选参数,下一次调用给你第二个,依此类推。

    如果您希望忽略任何剩余的可选参数,您可以随时停止。一个函数访问的参数少于调用中提供的参数是完全可以的,但是如果你试图访问太多的参数,你会得到垃圾值。

  3. 您通过调用 va_end 表明您已完成参数指针变量。

    (在实践中,对于大多数 C 编译器,调用 va_end 什么都不做。这在 GNU C 编译器中总是如此。但你最好调用 va_end 以防万一你的程序有一天用特殊的编译器编译。)

有关 va_start、va_arg 和 va_end 的完整定义,请参见参数访问宏。

步骤 1 和 3 必须在接受可选参数的函数中执行。但是,您可以将 va_list 变量作为参数传递给另一个函数,并在那里执行全部或部分步骤 2。

您可以在单个函数调用中多次执行三个步骤的整个序列。如果您想忽略可选参数,您可以零次执行这些步骤。

如果您愿意,您可以拥有多个参数指针变量。您可以根据需要使用 va_start 初始化每个变量,然后可以根据需要使用每个参数指针获取参数。每个参数指针变量将通过相同的参数值集进行排序,但按照自己的节奏。

可移植性说明:对于某些编译器,一旦将参数指针值传递给子例程,就不能在该子例程返回后继续使用相同的参数指针值。为了获得完全的可移植性,您应该将其传递给 va_end。这实际上是 ISO C 要求,但大多数 ANSI C 编译器无论如何都可以正常工作。

2.2.2.3. 提供了多少参数

How Many Arguments Were Supplied

函数没有通用的方法来确定调用它的可选参数的数量和类型。因此,设计函数的人通常会为调用者设计一个约定,以指定参数的数量和类型。您可以为每个可变参数函数定义适当的调用约定,并相应地编写所有调用。

一种调用约定是将可选参数的数量作为固定参数之一传递。如果所有可选参数都属于同一类型,则此约定有效。

一个类似的替代方法是让一个必需的参数成为位掩码,每个可能的目的都有一个位,可以提供一个可选参数。您将按预定义的顺序测试这些位;如果设置了该位,则获取下一个参数的值,否则使用默认值。

必需的参数可以用作指定可选参数的数量和类型的模式。printf 的格式字符串参数就是一个例子(参见格式化输出函数)。

另一种可能性是将“结束标记”值作为最后一个可选参数传递。例如,对于 *** 作任意数量的指针参数的函数,空指针可能指示参数列表的结尾。(这假设空指针对函数没有其他意义。) execl 函数就是这样工作的;请参阅执行文件。

2.2.2.4. 调用可变参数函数

Calling Variadic Functions

您无需执行任何特殊 *** 作即可调用可变参数函数。像往常一样,只需将参数(必需参数,后跟可选参数)放在括号内,用逗号分隔。但是您必须使用原型声明函数并知道参数值是如何转换的。

原则上,定义为可变参数的函数也必须在调用它们时使用函数原型声明为可变参数。(有关如何使用,请参阅变量参数的语法。)这是因为一些 C 编译器使用不同的调用约定将同一组参数值传递给函数,具体取决于该函数是采用可变参数还是固定参数。

在实践中,GNU C 编译器总是以相同的方式传递一组给定的参数类型,无论它们是可选的还是必需的。因此,只要参数类型是自我提升的,您就可以放心地省略声明它们。通常,为可变参数函数声明参数类型是一个好主意,实际上对所有函数也是如此。但是有一些函数非常方便不必声明为可变参数,例如 open 和 printf。

由于原型没有为可选参数指定类型,因此在对可变参数函数的调用中,默认参数提升是在可选参数值上执行的。这意味着 char 或 short int 类型的对象(无论是否有符号)被提升为 int 或 unsigned int,视情况而定;并且 float 类型的对象被提升为 double 类型。因此,如果调用者将 char 作为可选参数传递,则将其提升为 int,并且函数可以使用 va_arg (ap, int) 访问它。

所需参数的转换由函数原型以通常的方式控制:参数表达式被转换为声明的参数类型,就好像它被分配给该类型的变量一样。

2.2.2.5. 参数访问宏

Argument Access Macros

以下是用于检索变量参数的宏的描述。这些宏在头文件 stdarg.h 中定义。

数据类型:va_list

va_list 类型用于参数指针变量。

宏:void va_start (va_list ap, last-required)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

该宏初始化参数指针变量 ap 以指向当前函数的第一个可选参数; last-required 必须是函数的最后一个必需参数。

宏:type va_arg (va_list ap, type)

Preliminary: | MT-Safe race:ap | AS-Safe | AC-Unsafe corrupt | See POSIX Safety Concepts.

va_arg 宏返回下一个可选参数的值,并修改 ap 的值以指向后续参数。因此,va_arg 的连续使用会返回连续的可选参数。

va_arg 返回的值的类型是调用中指定的类型。type 必须是与实际参数的类型匹配的自我提升类型(不是 char 或 short int 或 float)。

宏:void va_end (va_list ap)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

ap 的使用到此结束。在 va_end 调用之后,具有相同 ap 的进一步 va_arg 调用可能不起作用。您应该在从使用相同 ap 参数调用 va_start 的函数返回之前调用 va_end。

在 GNU C 库中,va_end 什么都不做,除非出于可移植性的原因,您永远不需要使用它。

有时需要多次解析参数列表,或者想要记住参数列表中的某个位置。为此,必须复制参数的当前值。但是 va_list 是一种不透明类型,不一定要将一个 va_list 类型的变量的值分配给另一个相同类型的变量。

宏:void va_copy (va_list dest, va_​​list src)

宏:void __va_copy (va_list dest, va_​​list src)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

va_copy 宏允许复制 va_list 类型的对象,即使这不是整数类型。dest 中的参数指针被初始化为指向与 src 中的指针相同的参数。

va_copy 是在 ISO C99 中添加的。当构建严格符合 ISO C90 (‘gcc -std=c90’) 时,它不可用。GCC 在任何标准模式下都提供 __va_copy 作为扩展;在 GCC 3.0 之前,它是此功能的唯一宏。

这些宏不再由 GNU C 库提供,而是由编译器提供。

如果你想使用 va_copy 并移植到 C99 之前的系统,你应该时刻准备好这个宏不可用的可能性。在简单赋值无效的架构上,希望 va_copy 可用,所以如果担心 C99 之前的可移植性,应该总是写这样的东西:

{
  va_list ap, save;#ifdef va_copy
  va_copy (save, ap);
#else
  save = ap;
#endif}
2.2.3. 可变函数示例

Example of a Variadic Function

这是一个完整的示例函数,它接受可变数量的参数。该函数的第一个参数是剩余参数的计数,它们相加并返回结果。虽然微不足道,但这个函数足以说明如何使用可变参数工具。

#include 
#include 

int
add_em_up (int count,...)
{
  va_list ap;
  int i, sum;

  va_start (ap, count);         /* Initialize the argument list. */

  sum = 0;
  for (i = 0; i < count; i++)
    sum += va_arg (ap, int);    /* Get the next argument value. */

  va_end (ap);                  /* Clean up. */
  return sum;
}

int
main (void)
{
  /* This call prints 16. */
  printf ("%d\n", add_em_up (3, 5, 5, 6));

  /* This call prints 55. */
  printf ("%d\n", add_em_up (10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

  return 0;
}
2.3. 空指针常量

Null Pointer Constant

保证空指针常量不指向任何真实对象。您可以将它分配给任何指针变量,因为它的类型为 void *。编写空指针常量的首选方法是使用 NULL。

宏:void * NULL

这是一个空指针常量。

您也可以使用 0 或 (void *)0 作为空指针常量,但使用 NULL 更简洁,因为它使常量的用途更加明显。

如果您使用空指针常量作为函数参数,那么为了完全可移植,您应该确保该函数具有原型声明。否则,如果目标机器有两种不同的指针表示,编译器将不知道该参数使用哪种表示。您可以通过将常量显式转换为正确的指针类型来避免该问题,但我们建议改为为您正在调用的函数添加原型。

2.4. 重要数据类型

Important Data Types

C 中两个指针相减的结果始终是整数,但精确的数据类型因 C 编译器而异。同样,sizeof 结果的数据类型也因编译器而异。ISO C 为这两种类型定义了标准别名,因此您可以以可移植的方式引用它们。它们在头文件 stddef.h 中定义。

数据类型:ptrdiff_t

这是两个指针相减结果的有符号整数类型。例如,声明 char *p1, *p2; 时,表达式 p2 - p1 的类型为 ptrdiff_t。这可能是标准有符号整数类型之一(short int、int 或 long int),但也可能是仅用于此目的的非标准类型。

数据类型:size_t

这是一个无符号整数类型,用于表示对象的大小。sizeof 运算符的结果就是这种类型,诸如 malloc(参见无约束分配)和 memcpy(参见复制字符串和数组)之类的函数接受这种类型的参数来指定对象大小。在使用 GNU C 库的系统上,这将是 unsigned int 或 unsigned long int。

使用说明: size_t 是声明任何包含对象大小的参数或变量的首选方式。

兼容性说明:在 ISO C 出现之前,C 的实现通常使用 unsigned int 表示对象大小,使用 int 表示指针减法结果。他们不一定定义 size_t 或 ptrdiff_t。Unix 系统确实在 sys/types.h 中定义了 size_t,但定义通常是有符号类型。

2.5. 数据类型测量

Data Type Measurements

大多数时候,如果您为程序中的每个对象选择正确的 C 数据类型,您就不必关心它是如何表示的或它使用了多少位。当您确实需要此类信息时,C 语言本身并不提供获取它的方法。头文件limits.h 和float.h 包含的宏可以为您提供完整的详细信息。

2.5.1. 整数类型的宽度

Width of an Integer Type

TS 18661-1:2014 定义了整数类型宽度的宏(值和符号位的数量)。这些宏的一个好处是它们可以在#if 预处理器指令中使用,而 sizeof 不能。以下宏在limits.h 中定义。

CHAR_WIDTH
SCHAR_WIDTH
UCHAR_WIDTH
SHRT_WIDTH
USHRT_WIDTH
INT_WIDTH
UINT_WIDTH
LONG_WIDTH
ULONG_WIDTH
LLONG_WIDTH
ULLONG_WIDTH

这些分别是 char、signed char、unsigned char、short int、unsigned short int、int、unsigned int、long int、unsigned long int、long long int 和 unsigned long long int 类型的宽度。

此外,此类宏在 stdint.h 中定义。除了由宽度指定的类型(参见整数)之外,还定义了以下内容:

INTPTR_WIDTH
UINTPTR_WIDTH
PTRDIFF_WIDTH
SIG_ATOMIC_WIDTH
SIZE_WIDTH
WCHAR_WIDTH
WINT_WIDTH

这些分别是 intptr_t、uintptr_t、ptrdiff_t、sig_atomic_t、size_t、wchar_t 和 wint_t 类型的宽度。

程序需要知道整数类型中有多少位的一个常见原因是使用 unsigned long int 数组作为位向量。您可以通过以下方式访问索引 n 处的位:

vector[n / ULONG_WIDTH] & (1UL << (n % ULONG_WIDTH))

在 ULONG_WIDTH 成为 C 语言的一部分之前,CHAR_BIT 用于计算整数数据类型的位数。

宏:int CHAR_BIT

这是一个字符中的位数。POSIX.1-2001 要求此值为 8。

任何数据类型的位数都可以这样计算:

sizeof (type) * CHAR_BIT

该表达式包括填充位以及值和符号位。在 GNU C 库支持的所有系统上,_Bool 以外的标准整数类型没有任何填充位。

可移植性注意:实际上不能以可移植的方式轻松计算可用位数。

2.5.2. 整数类型的范围

Range of an Integer Type

假设您需要存储一个整数值,其范围从零到一百万。您可以使用的最小类型是哪种?没有一般规则;它取决于 C 编译器和目标机器。您可以使用 limits.h 中的“MIN”和“MAX”宏来确定哪种类型可以工作。

每个有符号整数类型都有一对宏,它们给出了它可以容纳的最小值和最大值。每个无符号整数类型都有一个这样的宏,用于最大值;最小值当然为零。

这些宏的值都是整数常量表达式。char 和 short int 类型的“MAX”和“MIN”宏具有 int 类型的值。其他类型的“MAX”和“MIN”宏的值与宏所描述的类型相同——因此,ULONG_MAX 的类型为 unsigned long int。

SCHAR_MIN

这是可以用有符号字符表示的最小值。

SCHAR_MAX

UCHAR_MAX

这些是可以分别由有符号字符和无符号字符表示的最大值。

CHAR_MIN

这是一个字符可以表示的最小值。如果 char 有符号,则等于 SCHAR_MIN,否则为零。

CHAR_MAX

这是一个字符可以表示的最大值。如果 char 是有符号的,则它等于 SCHAR_MAX,否则等于 UCHAR_MAX。

SHRT_MIN

这是可以用带符号的 short int 表示的最小值。在运行 GNU C 库的大多数机器上,短整数是 16 位的量。

SHRT_MAX

USHRT_MAX

这些是可以分别由有符号短整数和无符号短整数表示的最大值。

INT_MIN

这是可以用有符号整数表示的最小值。在运行 GNU C 库的大多数机器上,int 是一个 32 位的量。

INT_MAX

UINT_MAX

这些是可以分别表示为signed int 类型和unsigned int 类型的最大值。

LONG_MIN

这是可以用带符号的 long int 表示的最小值。在运行 GNU C 库的大多数机器上,长整数是 32 位数量,与 int 大小相同。

LONG_MAX

ULONG_MAX

这些是可以分别由有符号长整数和无符号长整数表示的最大值。

LLONG_MIN

这是可以用带符号的 long long int 表示的最小值。在运行 GNU C 库的大多数机器上,long long 整数是 64 位的数量。

LLONG_MAX

ULLONG_MAX

这些是可以分别由有符号长整型和无符号长整型表示的最大值。

LONG_LONG_MIN

LONG_LONG_MAX

ULONG_LONG_MAX

这些是 LLONG_MIN、LLONG_MAX 和 ULLONG_MAX 的过时名称。它们仅在定义了 _GNU_SOURCE 时可用(请参阅功能测试宏)。在 3.0 之前的 GCC 版本中,这些是唯一可用的名称。

WCHAR_MAX

这是 wchar_t 可以表示的最大值。请参阅扩展字符简介。

头文件 limits.h 还定义了一些附加常量,这些常量参数化各种 *** 作系统和文件系统限制。这些常量在系统配置参数中进行了描述。

2.5.3. 浮点型宏

Floating Type Macros

浮点数的具体表示因机器而异。由于浮点数在内部表示为近似量,因此处理浮点数据的算法通常需要考虑机器浮点表示的精确细节。

C 库中的一些函数本身就需要这些信息; 例如,用于打印和读取浮点数的算法(请参阅输入/输出流)以及用于计算三角函数和无理函数(请参阅数学库)的算法使用它来避免舍入误差和精度损失。实现数值分析技术的用户程序也经常需要这些信息来最小化或计算误差范围。

头文件 float.h 描述了您的机器使用的格式。

2.5.3.1. 浮点表示概念

Floating Point Representation Concepts

本节介绍用于描述浮点表示的术语。

就浮点数的科学或指数表示法而言,您可能已经熟悉这些概念中的大部分。例如,数字 123456.0 可以用指数符号表示为 1.23456e+05,这是一种简写符号,表示尾数 1.23456 乘以以 10 为底的 5 次方。

更正式地说,浮点数的内部表示可以用以下参数来表征:

  • 符号为 -1 或 1。

  • 求幂的基数或基数,大于 1 的整数。这是特定表示的常数。

  • 基数上升到的指数。指数值的上限和下限是特定表示的常数。

    有时,在表示浮点数的实际位中,指数会通过向其添加一个常数来进行偏置,以使其始终表示为无符号数。仅当您有某种理由手动分离构成浮点数的位域时,这一点才重要,GNU C 库对此不提供支持。因此,在接下来的讨论中忽略了这一点。

  • 尾数或有效数是一个无符号整数,它是每个浮点数的一部分。

  • 尾数的精度。如果表示的基数是 b,那么精度是尾数中基数 b 的位数。这是特定表示的常数。

    许多浮点表示在尾数中有一个隐含的隐藏位。这是一个实际上存在于尾数中的位,但不存储在内存中,因为它的值在标准化数字中始终为 1。精度数字(见上文)包括任何隐藏位。

    同样,GNU C 库没有提供处理这种表示的低级方面的工具。

浮点数的尾数表示一个隐式分数,其分母是底数的精度次方。由于最大可表示的尾数比这个分母小一,所以分数的值总是严格小于 1。浮点数的数学值是这个分数、符号和底数的乘积.

如果分数至少为 1/b,我们说浮点数被归一化,其中 b 是基数。换句话说,如果将尾数乘以基数,则尾数将太大而无法容纳。非规格化数有时称为非规格化数;它们包含的精度低于通常的表示形式。

如果数字未归一化,则可以在将尾数乘以底数的同时从指数中减去 1,并得到另一个具有相同值的浮点数。标准化包括重复执行此 *** 作,直到数字标准化。两个不同的归一化浮点数的值不能相等。

(这个规则有一个例外:如果尾数为零,则被认为是规范化的。另一个例外发生在某些机器上,其中指数尽可能小到表示可以容纳。那么不可能从指数中减去 1,所以一个数可以被归一化,即使它的分数小于 1/b。)

2.5.3.2. 浮点参数

Floating Point Parameters

可以通过在程序中包含头文件 float.h 来访问这些宏定义。

以‘FLT_’开头的宏名指的是float类型,以‘DBL_’开头的名字指的是double类型,以‘LDBL_’开头的名字指的是long double类型。(如果 GCC 不支持 long double 作为目标机器上的不同数据类型,则“LDBL_”常量的值等于 double 类型的相应常量。)

在这些宏中,只有 FLT_RADIX 保证为常量表达式。此处列出的其他宏不能可靠地用于需要常量表达式的地方,例如“#if”预处理指令或静态数组的维度。

尽管 ISO C 标准为这些参数中的大多数指定了最小值和最大值,但 GNU C 实现使用描述目标机器的浮点表示的任何值。所以原则上 GNU C 实际上只有在目标机器合适的情况下才满足 ISO C 的要求。在实践中,目前支持的所有机器都是合适的。

FLT_ROUNDS

该值表征浮点加法的舍入模式。以下值表示标准舍入模式:

-1
模式是不确定的。

0
四舍五入接近零。

1
四舍五入到最接近的数字。

2
四舍五入是朝着正无穷大。

3
四舍五入是朝着负无穷大。

任何其他值表示与机器相关的非标准舍入模式。

在大多数机器上,该值为 1,符合 IEEE 浮点标准。

下表显示了 FLT_ROUNDS 的每个可能值的某些值如何舍入,如果表示的其他方面与 IEEE 单精度标准匹配。

                0      1             2             3
 1.00000003    1.0    1.0           1.00000012    1.0
 1.00000007    1.0    1.00000012    1.00000012    1.0
-1.00000003   -1.0   -1.0          -1.0          -1.00000012
-1.00000007   -1.0   -1.00000012   -1.0          -1.00000012

FLT_RADIX

这是指数表示的基数或基数。与本节中描述的其他宏不同,这保证是一个常量表达式。除 IBM 360 及其衍生产品外,我们所知道的所有机器上的值为 2。

FLT_MANT_DIG

这是浮点数据类型的浮点尾数中的 base-FLT_RADIX 位数。由于尾数位数有限,以下表达式产生 1.0(即使在数学上不应该):

float radix = FLT_RADIX;

1.0f + 1.0f / radix / radix // radix

其中基数出现 FLT_MANT_DIG 次。

DBL_MANT_DIG

LDBL_MANT_DIG

这分别是数据类型 double 和 long double 的浮点尾数中的 base-FLT_RADIX 位数。

FLT_DIG

这是浮点数据类型精度的小数位数。从技术上讲,如果 p 和 b 分别是表示的精度和基数,那么十进制精度 q 是十进制数字的最大数量,这样任何具有 q 基数为 10 位的浮点数都可以四舍五入为浮点数p 以 b 为基数,然后再返回,不更改 q 十进制数字。

该宏的值应该至少为 6,以满足 ISO C。

DBL_DIG

LDBL_DIG

这些类似于 FLT_DIG,但分别用于数据类型 double 和 long double。这些宏的值应该至少为 10。

FLT_MIN_EXP

这是浮点类型的最小可能指数值。更准确地说,它是最小的负整数,使得 FLT_RADIX 的这个幂减去 1 可以表示为浮点类型的归一化浮点数。

DBL_MIN_EXP

LDBL_MIN_EXP

这些类似于 FLT_MIN_EXP,但分别用于数据类型 double 和 long double。

FLT_MIN_10_EXP

这是最小的负整数,使得 10 的这个幂减去 1 可以表示为浮点类型的归一化浮点数。这应该是 -37 甚至更低。

DBL_MIN_10_EXP

LDBL_MIN_10_EXP

这些类似于 FLT_MIN_10_EXP,但分别用于数据类型 double 和 long double。

FLT_MAX_EXP

这是浮点类型的最大可能指数值。更准确地说,这是最大的正整数,使得值 FLT_RADIX 的该次幂减去 1 可以表示为 float 类型的浮点数。

DBL_MAX_EXP

LDBL_MAX_EXP

这些类似于 FLT_MAX_EXP,但分别用于数据类型 double 和 long double。

FLT_MAX_10_EXP

这是最大正整数,使得 10 的这个幂减 1 可以表示为浮点类型的归一化浮点数。这应该至少是37。

DBL_MAX_10_EXP

LDBL_MAX_10_EXP

这些类似于 FLT_MAX_10_EXP,但分别用于数据类型 double 和 long double。

FLT_MAX

该宏的值是以浮点类型表示的最大数。它应该至少是 1E+37。该值的类型为浮点数。

最小的可表示数字是 - FLT_MAX。

DBL_MAX

LDBL_MAX

这些类似于 FLT_MAX,但分别用于数据类型 double 和 long double。宏值的类型与其描述的类型相同。

FLT_MIN

该宏的值是以浮点类型表示的最小归一化正浮点数。它应该不超过1E-37。

DBL_MIN

LDBL_MIN

这些类似于 FLT_MIN,但分别用于数据类型 double 和 long double。宏值的类型与其描述的类型相同。

FLT_EPSILON

这是 1 与大于 1 的 float 类型的最小浮点数之间的差。它应该不大于 1E-5。

DBL_EPSILON

LDBL_EPSILON

这些类似于 FLT_EPSILON,但分别用于数据类型 double 和 long double。宏值的类型与其描述的类型相同。这些值不应大于 1E-9。

2.5.3.3. IEEE 浮点

IEEE Floating Point

下面是一个示例,展示了浮点类型测量结果如何用于最常见的浮点表示,由 IEEE 二进制浮点算术标准 (ANSI/IEEE Std 754-1985) 指定。自 1980 年代以来设计的几乎所有计算机都使用这种格式。

IEEE 单精度浮点表示使用以 2 为底。有一个符号位,一个 23 位加上一个隐藏位的尾数(因此总精度为 24 个 base-2 位),以及一个 8 位指数,可以表示 值在 -125 到 128 的范围内(含)。

因此,对于将此表示用于浮点数据类型的实现,相应参数的适当值是:

FLT_RADIX                             2
FLT_MANT_DIG                         24
FLT_DIG                               6
FLT_MIN_EXP                        -125
FLT_MIN_10_EXP                      -37
FLT_MAX_EXP                         128
FLT_MAX_10_EXP                      +38
FLT_MIN                 1.17549435E-38F
FLT_MAX                 3.40282347E+38F
FLT_EPSILON             1.19209290E-07F

以下是双精度数据类型的值:

DBL_MANT_DIG                         53
DBL_DIG                              15
DBL_MIN_EXP                       -1021
DBL_MIN_10_EXP                     -307
DBL_MAX_EXP                        1024
DBL_MAX_10_EXP                      308
DBL_MAX         1.7976931348623157E+308
DBL_MIN         2.2250738585072014E-308
DBL_EPSILON     2.2204460492503131E-016
2.5.4. 结构成员偏移测量

Structure Field Offset Measurement

您可以使用 offsetof 来测量特定结构成员的结构类型中的位置。

宏:size_t offsetof (type, member)

Preliminary: | MT-Safe | AS-Safe | AC-Safe | See POSIX Safety Concepts.

这扩展为一个整数常量表达式,它是结构类型类型中名为 member 的结构成员的偏移量。例如,offsetof (struct s, elem) 是 struct s 中成员 elem 的偏移量,以字节为单位。

如果成员是位字段,此宏将不起作用; 在这种情况下,您会从 C 编译器中得到一个错误。

3. 参考
  • Appendix A C Language Facilities in the Library

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1325935.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存