我建议使用M.M 的答案中描述的宏定义。
在某些情况下,您可能需要一个类似的宏,但作为一个数字常量,以便您可以在 #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 在评论中指出 %z 不受 ANSI C/ISO C90 支持,因此上述程序首先使用 (size_t)(-1) 创建常量,然后将其转换为 unsigned long long 格式并打印出来。
现在,可以以与操作系统无关的方式编写 Makefile,但我懒得在这里这样做,所以我将使用适用于 GNU 工具的值。要使其在其他系统上也能正常工作,只需要修改
无论如何,这是 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 存在。
这种方法的缺点是您不能在链接配方中使用$^ 来引用所有必备文件名。 $< 指第一个必备文件名。不过,如果您使用 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 或在运行 make 时使用不同的 CFLAGS= 或 CC= 选项,make 不会检测到,因此您确实需要先指定 clean 目标,以确保新设置生效后,您可以从头开始。
在正常编辑和构建期间,当您不更改编译器或编译器选项时,无需在构建之间使用make clean。