如何获得SIZE
我试图在C89中获得SIZE_MAX
。
我想通过以下方式找到SIZE_MAX
:
const size_t SIZE_MAX = -1;
由于标准(§6.2.1.2ANSI C)说:
当一个有符号整数被转换为一个无符号整数,其大小相等或更大时,如果有符号整数的值是非负的,则其值不变。 否则:如果无符号整数具有更大的大小,则有符号整数首先被提升为与无符号整数相对应的有符号整数; 通过向无符号整数类型 28中表示的最大数量添加一个值,将该值转换为无符号
用脚注28:
在二进制补码表示中,除了用符号位的副本填充高位比特(如果无符号整数具有更大尺寸)之外,位模式中没有实际变化。
这似乎是这样定义的行为,但我不太确定是否正确理解了该段落的措辞。
请注意,这个问题明确地是关于C89的,所以这不能回答我的问题,因为标准有不同的措辞。
如果这不起作用,我想到的另一种方法是:
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“转换 - 有符号和无符号整数”一节中定义。 C89有一个等效的定义,编号为3.2.1.2。 事实上,您在您的问题中引用了ISO C90定义6.2.1.2(ANSI C89和ISO C90之间的区别在于各部分的编号不同)。
我不会推荐使用const
变量,因为它们不能在常量表达式中使用。
注意:这不能用于C90预处理器算术,它只适用于不包含转换或单词的整型常量表达式,所以我们不能使用任何sizeof
技巧。 在这种情况下,您可能需要一个系统特定的定义; 预处理器没有检测typedef的标准方法。
我建议使用MM的答案中所述的宏定义。
在某些情况下,您可能需要一个类似的宏,但作为一个数值常量,以便您可以在#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_MAXn");
printf("#define SIZE_MAX %lluUn", (unsigned long long)(size_t)(-1));
printf("#endifn");
return EXIT_SUCCESS;
}
chux在评论中指出u
后缀(对于足够大的无符号整数类型)可能是必要的。 如果这不符合您的要求,我相信您可以修改宏生成器帮助器以满足您的需求。
MM在注释中指出%z
不被ANSI C / ISO C90支持,所以上面的程序首先使用(size_t)(-1)
创建常量,然后使用unsigned long long
格式转换并打印它。
现在,Makefiles可以用操作系统不可知的方式编写,但我懒得在这里做,因此我将使用与GNU工具一起使用的值。 要使其在其他系统上工作,只需修改其值
CC
,以反映您使用的编译器
CFLAGS
,以反映您的首选编译器选项
LD
,以反映您的链接器,除非与CC
相同
LDFLAGS
,如果你需要一些链接器标志(可能是-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
sed -e 's|^ *|t|' -i Makefile
来修复它。
在对源代码树进行压缩或tar之前,请运行make clean
以从中删除所有生成的文件。
请注意配方先决条件中的额外size_max.h
。 它告诉make
在可以完成配方之前确保size_max.h
存在。
这种方法的缺点是你不能在链接食谱中使用$^
来引用所有先决条件文件名。 $<
引用第一个必备文件名。 但是,如果您使用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".
请注意,make不会检测您是否更改编译器选项,如果编辑Makefile或在运行make时使用不同的CFLAGS=
或CC=
选项,则需要先指定clean
目标,以确保从clean
目标开始新的设置生效。
在正常编辑和构建过程中,如果不更改编译器或编译器选项,则不需要在构建之间make clean
。
上一篇: How to get SIZE
下一篇: What is the difference between C, C99, ANSI C and GNU C?