c-如果有明显的溢出可能性,我应该手动转换为更高的类型吗



我正在学习C,在其中一项任务中,我被要求使用1000 * getuid() + 0sem_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

我也很惊讶编译器(gccclang)自己没有检测到问题。我正在使用-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,可以是signedunsigned,宽度不同于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));

最新更新