如何在 C89 中获取 SIZE_MAX

How to get SIZE_MAX in C89

我正在尝试在 C89 中获得 SIZE_MAX

我想到了下面的方法来查找SIZE_MAX

const size_t SIZE_MAX = -1;

因为标准 (§6.2.1.2 ANSI C) 说:

When a signed integer is converted to an unsigned integer with equal or greater size, if the value of the signed integer is nonnegative, its value is unchanged. Otherwise: if the unsigned integer has greater size, the signed integer is first promoted to the signed integer corresponding to the unsigned integer; the value is converted to unsigned by adding to it one greater than the largest number that can be represented in the unsigned integer type 28

加上脚注 28:

In a two's-complement representation, there is no actual change in the bit pattern except filling the high-order bits with copies of the sign bit if the unsigned integer has greater size.

这似乎已经定义了行为,但我不太确定我是否正确理解了该段的措辞。

请注意,这个问题明确是关于 C89 的,所以 this 没有回答我的问题,因为标准有不同的措辞。

如果这不起作用,我想到的另一种方法是:

size_t get_size_max() {
    static size_t max = 0;
    if (max == 0) {
        max -= 1U;
    }

    return max;
}

但是我在标准中找不到任何关于无符号整数下溢的信息,所以我在这里摸索。

您可以使用:

#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)(-1))
#endif

-1 转换为无符号整数类型的行为在 C11 6.3.1.3 "Conversions - Signed and unsigned integers" 节中定义。 C89 有一个等效的定义,编号为 3.2.1.2。事实上,您在问题中引用了 ISO C90 定义 6.2.1.2(ANSI C89 和 ISO C90 之间的区别在于这些部分的编号不同)。

我不建议使用 const 变量,因为它们不能用于常量表达式。


注意:这不能用于 C90 预处理器算法,它只适用于不包含强制转换或单词的整数常量表达式,因此我们不能使用任何 sizeof 技巧。在那种情况下,您可能需要一个特定于系统的定义;预处理器没有检测 typedef 的标准方法。

我建议使用 的回答中描述的宏定义。

在某些情况下,您可能需要一个类似的宏,但作为一个数字常量,以便您可以在预处理器指令中使用它,例如 #if VALUE > 42 ... #endif。我评论说,在这种情况下,辅助程序可以在编译时 运行 来计算和打印定义此类常量的头文件。

显然,当交叉编译到不同的体系结构时,这将不起作用;在这种情况下,头文件必须通过其他方式提供。 (例如,该项目可以有一个预先生成的头文件的子目录,以及每个已知架构的列表,以便用户可以简单地将头文件复制到位。)

为 运行 此类程序创建 Makefile 和相关设施(并且前提是用户没有将头文件复制到位)并不困难。

首先,假设您的程序包含两个源文件,foo.c:

#include <stdlib.h>
extern void hello(void);

int main(void)
{
    hello();
    return EXIT_SUCCESS;
}

和一个bar.c:

#include <stdio.h>
#include "size_max.h"

#define  STRINGIFY_(s) #s
#define  STRINGIFY(s) STRINGIFY_(s)

void hello(void)
{
    fputs("SIZE_MAX = \"" STRINGIFY(SIZE_MAX) "\".\n", stdout);
}

上面的bar.c将SIZE_MAX预处理器宏转换为字符串,并打印出来。如果我们有 #define SIZE_MAX (size_t)(-1),它会打印 SIZE_MAX = "(size_t)(-1)".

请注意 bar.c 包含我们没有的文件 size_max.h。这是我们打算使用我们的帮助程序生成的头文件,size_max.c:

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
    printf("#ifndef SIZE_MAX\n");
    printf("#define SIZE_MAX %lluU\n", (unsigned long long)(size_t)(-1));
    printf("#endif\n");
    return EXIT_SUCCESS;
}

chux 在评论中指出 u 后缀(对于足够大的无符号整数类型)可能是必要的。如果这不是您所需要的,我相信您可以修改宏生成器帮助程序以满足您的需要。

M.M 在评论中指出 ANSI C/ISO C90 不支持 %z,因此上述程序首先使用 (size_t)(-1) 创建常量,然后强制转换和以 unsigned long long 格式打印。

现在,Makefile 可以用 OS 不可知的方式编写,但我懒得在这里这样做,所以我将使用适用于 GNU 工具的值。要使其在其他系统上也能正常工作,只需要修改

的值即可
  • CC,以反映您使用的编译器

  • CFLAGS,以反映您首选的编译器选项

  • LD,反映你的linker,除非和CC

  • 一样
  • LDFLAGS,如果你需要一些 linker 标志(也许 -lm?)

  • RM,反映命令删除不需要的文件

  • 文件名,如果您的构建系统需要一些可执行文件的时髦文件扩展名

无论如何,这是 Makefile:

CC      := gcc
CFLAGS  := -Wall -O2
LD      := $(CC)
LDFLAGS := $(CFLAGS)
RM      := rm -f

# Programs to be built
PROGS   := example

# Relative path to use for executing the header generator helper program
HEADERGEN := ./headergen

# Rules that do not correspond to actual files
.PHONY: all clean headergen

# Default rule is to build all binaries
all: $(PROGS)

# Clean rule removes build files and binaries
clean:
    -$(RM) $(PROGS) $(HELPROG) *.o size_max.h

# Rule to "rebuild" size_max.h
size_max.h: size_max.c
    -@$(RM) $(HEADERGEN) size_max.h
    @$(CC) $(CFLAGS) $^ -o $(HEADERGEN)
    $(HEADERGEN) > size_max.h
    @$(RM) $(HEADERGEN)

# Rule to build object files from .c source files
%.o: %.c size_max.h
    $(CC) $(CFLAGS) -c $<

# Example binary requires foo.o and bar.o:
example: foo.o bar.o size_max.h
    $(LD) $(LDFLAGS) foo.o bar.o -o $@

请注意缩进应使用制表符,而不是空格,因此如果您复制粘贴以上内容,运行 例如sed -e 's|^ *|\t|' -i Makefile 修复它。

在压缩或压缩源代码树之前,运行 make clean 从中删除任何生成的文件。

注意配方先决条件中的额外 size_max.h。它告诉 make 在完成配方之前确保 size_max.h 存在。

这种方法的缺点是您不能在 link 配方中使用 $^ 来引用所有先决条件文件名。 $< 指的是第一个先决条件文件名。如果您使用 GNU make 或兼容的 make,您可以使用 $(filter-out %.h, %^)(列出除头文件之外的所有先决条件)。

如果您的所有二进制文件都是从具有相同名称的单一来源构建的,您可以将最后两个配方替换为

# All programs are built from same name source files:
$(PROGS): %: %.c size_max.h
    $(CC) $(CFLAGS) $< $(LDFLAGS) -o $@

在我的系统上,运行宁

make clean all && ./example

产出

rm -f example  *.o size_max.h
./headergen > size_max.h
gcc -Wall -O2 -c foo.c
gcc -Wall -O2 -c bar.c
gcc -Wall -O2 foo.o bar.o -o example
SIZE_MAX = "18446744073709551615U".

和运行宁

make CC="gcc-5" CFLAGS="-Wall -std=c99 -pedantic -m32" clean all && ./example

产出

rm -f example  *.o size_max.h
./headergen > size_max.h
gcc-5 -Wall -std=c99 -pedantic -m32 -c foo.c
gcc-5 -Wall -std=c99 -pedantic -m32 -c bar.c
gcc-5 -Wall -std=c99 -pedantic -m32 foo.o bar.o -o example
SIZE_MAX = "4294967295U".

请注意,如果您更改编译器选项,如果您编辑 Makefile 或在 运行ning make 时使用不同的 CFLAGS=CC= 选项,make 不会检测到,因此您确实需要然后首先指定 clean 目标,以确保您从一个干净的平台开始,新设置生效。

在正常编辑和构建过程中,如果您不更改编译器或编译器选项,则无需在构建之间 make clean