如何在 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
。
我正在尝试在 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
。