防止用户向接受无符号整数的函数传递负数

5
所以这是代码:
int create_mask(unsigned b, unsigned e)
{
  unsigned int mask=1;

  if(b<e || b<0 || e<0)
  {
    printf("Wrong values, starting bit can't be smaller than ending.\n");
    printf("Both got to be >= 0.\n");
    exit(EXIT_FAILURE);
  }
  while(b>0)
  {
    printf("%u\n", b);
    mask<<=1;
    if(b>e)
      mask|=1;
    b--;
  }

  return ~mask; /* negates mask for later purpose that is clearing corresponding bits */
}

该函数用于创建一些位操作的掩码,但应该接受两个无符号整数b和e,均为非负数。问题是如何防止用户输入负数?当该函数被调用时,如果传入(-1, 0),它会开始循环,并应该因错误而退出。


1
但无符号数永远不为负,那这意味着什么? - harold
首先,不要让函数被调用时带有(-1, 0)。输入一个字符串,然后使用strtoul()将其转换为无符号整数。如果数字为负数,它会报告错误。 - user529758
@harold 发生了从有符号到无符号的隐式转换,它被解释为模运算 1 << width,其中 width 是一个 unsigned int 中的位数。 - user529758
有符号整数-1,当被解释为无符号整数时,将等于最大值。因此它不小于e,也不小于0。 - Taemyr
1
@Taemyr 不一定,只有当机器使用二进制补码表示负数时才是这样。 - user529758
显示剩余4条评论
3个回答

5
您只需输入一个字符串,检查它是否包含一个'-'字符,如果有则产生错误。否则,将其转换为无符号整数并继续进行。(使用strtoul()读取字符串然后转换是优先选择,特别是在您不知道所有scanf()的怪癖时。)
char buf[LINE_MAX];
fgets(buf, sizeof buf, stdin);

if (strchr(buf, '-') != NULL) {
    fprintf(stderr, "input must be non-negative!\n");
    exit(-1);
}

unsigned int n = strtoul(buf, NULL, 0);

sscanf(buf, "%lu", &n); 也可以进行转换,但我认为 strtoul 更好 :) - boleto
@boleto 我使用(并建议使用)strtoul()而不是scanf(),这并不是巧合。 - user529758
如果你要重新设计界面,那么指定一个整数而不是字符串似乎更明智。这样,如果值小于等于0或小于0,你就可以使用assert或throw来处理。 - shawn1874

2

编辑:

您可以输入 long int,然后检查输入是否在无符号整数的范围内(从0到结束范围)。如果是,则赋值给您的变量,否则向用户提出异常,您应该只输入无符号数字。

long int input;
unsigned int valid_input;

scanf("%ld",&input);
if((0<= input) && (input <= 4294967295))
valid_input= (unsigned int)input  ;
else
printf("Unvalid input\n");

正如H2CO3所说,将输入读入字符串并检查第一个字母是否为负号,如果不是,则转换为无符号整数比下面的方法更好,因为未涵盖一半的无符号整数。

您可以将输入获取为int,然后如果它是非负的,则继续执行。如果它是负数,则向用户发出异常,您不应该提供负输入。


4
但这将使得使用某些高无符号值变得不可能(当从无符号转换为有符号时,会溢出到int范围的负半部分)。而有符号整数溢出本来就是未定义行为,因此这不可能是正确的解决方案。 - user529758
没错,这就是问题所在。 - zubergu
@zubergu 再次提醒您,可以查看我在您问题中的第一条评论。 - user529758
@zubergu 然而,根据定义,这些数字实际上等于负数。- 因此,当您要求排除负数时,在某种意义上也要求排除高无符号整数。 - Taemyr
@H2CO3 还有另一种选择。检查 scanf 的返回状态。在扫描时,如果没有得到无符号整数,则可能会得到零作为返回值。 - Gangadhar
2
@Gangadhar 嗯,scanf()在遇到负数时不会退出。它将扫描一个有符号整数,然后将其转换为无符号整数,并报告成功。 - user529758

0

看看这个:停止函数隐式转换

我能够将其适应到您的问题上,并且它可以阻止程序链接。上面线程中的信息似乎部分不正确,因为示例编译得很好。它未能链接是因为没有定义模板特化。实际上,我对于int与unsigned int之间的以下内容感到有些惊讶。

template <class T>
void foo(const T& t);

template <>
void foo<unsigned int>(const unsigned int& t)
{

}

int main(){
  foo((unsigned int) 9); // will compile and link
  unsigned int value(5);
  foo(value);// will compile and link
  foo(9.0); // will not link
  foo(-9); // will not link
  return 0;
}

我认为你可能过于考虑了。这真的是一个问题吗?一开始将id类型设为int会更好吗?是否有一个最小/最大id避免了可被误认为是二进制补码的大数?这似乎是语言中一个不幸的问题,它没有提供任何简单的方法来阻止一个隐式转换。

我在Visual Studio 2010中测试了这个例子。此外,我没有时间编写一个测试类,所以如果你感兴趣,你需要将这个例子改编为一个foo类来查看它是否适用于类的构造函数,或者是否有另一种使用模板的方式来做到这一点。根据其他答案和我的经验,我认为你不会找到一个简单的方法来实现你想要的东西。


谢谢您的回答,但问题标记为C,严格限定为C。 - zubergu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接