我正在学习C,在其中一项任务中,我被要求使用1000 * getuid() + 0
为sem_open
生成信号量名称。将有多个信号量,最后一个数字(+ 0
)用于区分它们。
代码片段:
#define SEM_NAME_LENGTH 24
#define SEM_NAME 1000 * getuid() + 0
...
char sem_name[SEM_NAME_LENGTH];
/* convert the sem number (as defined in spec) to a string */
if (sprintf(sem_name, "%u", SEM_NAME) < 0) {
return -1;
}
sem_id = sem_open(sem_name, O_CREAT, S_IRUSR | S_IWUSR, 0);
很明显,SEM_NAME
可以超过unsigned int
,因为getuid
可以返回UINT_MAX
,如果我们将其乘以1000…
我的第一个想法是将sprintf
格式更改为"%llu"
,但后来我收到了一个警告:
format specifies type 'unsigned long long' but the argument has type 'unsigned int'
这意味着编译器仍在考虑表达式是unsigned int
或__uid_t
。
看起来我有以下可能性:
A。手动键入unsigned long long
:
#define SEM_NAME (unsigned long long) 1000 * getuid() + 0
B。将名称定义为变量:
unsigned long long sem_name = 1000 * getuid() + 0;
C。检查溢出/不接受高于UINT_MAX
/1000(坏)的uid
我也很惊讶编译器(gcc
、clang
)自己没有检测到问题。我正在使用-std=gnu99 -Wall -Wextra -Wstrict-prototypes -pedantic
。
C检查是否溢出
根据需要,迂腐的代码检查潜在的范围问题。
uid_t id = getuid();
if (id > UINT_MAX/1000 || id < 0) Handle_OutOfRange(id);
sprintf(sem_name, "%u", (unsigned) (id * 1000u));
更实际的是。。。
有趣的是,代码使用"%u"
,尽管类型不知道是unsigned
,因为getuid()
返回类型uid_t
,可以是signed
或unsigned
,宽度不同于unsigned
。
// potential undefined behavior - UB
sprintf(sem_name, "%u", SEM_NAME)
按照OP的方法提升到宽类型,代码可以使用以下方法提升到最宽类型。IMO,为什么去long long/unsigned long long
?转到最宽的整数类型intmax_t/uintmax_t
。
uid_t id = getuid();
sprintf(sem_name, "%jd", (intmax_t) id * 1000);
通常,不建议OP的#define
隐藏宏SEM_NAME
中的函数调用,因为看起来是常量。备选建议
// #define SEM_NAME 1000 * getuid() + 0
// sprintf(sem_name, "%u", SEM_NAME)
#include <stdint.h>
#define SEM_NAME(id) (INTMAX_C(1000) * (id) + 0)
uid_t id = getuid();
sprintf(sem_name, "%jd", SEM_NAME(id));