在 C 中将值 -1 作为size_t返回值有什么后果

  • 本文关键字:返回值 后果 size 作为 c size-t
  • 更新时间 :
  • 英文 :


我正在读一本教科书,其中一个例子就是这样做的。下面,我以缩写形式重现了该示例:

#include <stdio.h>
#define SIZE 100
size_t linearSearch(const int array[], int searchVal, size_t size);
int main(void)
{
    int myArray[SIZE];
    int mySearchVal;
    size_t returnValue;
    // populate array with data & prompt user for the search value
    // call linear search function
    returnValue = linearSearch(myArray, mySearchVal, SIZE);
    if (returnValue != -1)
        puts("Value Found");
    else
        puts("Value Not Found");
}
size_t linearSearch(const int array[], int key, size_t size)
{
    for (size_t i = 0; i < size; i++) {
        if (key == array[i])
            return i;
    }
    return -1;
}

这有什么潜在的问题吗? 我知道size_t被定义为无符号整数类型,所以如果我返回 -1 作为size_t返回值,这似乎可能会在某个时候带来麻烦。

我想

到了一些使用最大有符号或无符号整数值作为哨兵值的 API。例如,如果在字符串中找不到find()给定的值,并且std::string::npos等于 (std::string::size_type)-1,则C++的 std::string::find() 方法返回 std::string::npos

同样,在 iOS 和 OS X 上,NSArray 的 indexOfObject: 方法在数组中找不到对象时返回NSNotFound。令人惊讶的是,NSNotFound实际上被定义为 NSIntegerMax ,这要么是 32 位平台的INT_MAX,要么是 64 位平台的LONG_MAX,即使NSArray索引通常是NSUInteger(对于 32 位平台unsigned int,对于 64 位平台unsigned long)。

这确实意味着"未找到"和"元素编号 18,446,744,073,709,551,615"(对于 64 位系统)之间没有区别,但这是否是可接受的权衡取决于您。

另一种方法是让函数通过指针参数返回索引,并让函数的返回值指示成功或失败,例如

#include <stdbool.h>
bool linearSearch(const int array[], int val, size_t size, size_t *index)
{
    // find value and then
    if (found)
    {
        *index = indexOfFoundItem;
        return true;
    }
    else
    {
        *index = 0; // optional, in some cases, better to leave *index untouched
        return false;
    }
}

您的编译器可能会决定抱怨将有符号与无符号进行比较——如果被激怒,GCC 或 Clang 会这样做*——但除此之外"它有效"。在二进制补码机器(当今大多数机器)上,(size_t)-1SIZE_MAX相同——事实上,正如评论中详细讨论的那样,由于 C99 和 C11 标准 §6.3.1.3 中的措辞,对于补码或符号量级机器也是如此)。

使用(size_t)-1表示"未找到"意味着您无法区分最大数组中的最后一个条目和"未找到",但这很少是实际问题。

所以,这只是我最终可能遇到问题的一种边缘情况?

但是,该数组必须是一个char数组才能大到足以引起麻烦 - 虽然您可以在32位机器上拥有4 GiB内存,但将所有内存提交到字符数组是相当不可信的(而且64位机器不太可能出现问题;大多数不会运行到16 EB的内存)。所以这不是一个实用的边缘情况。

在POSIX中,有一个ssize_t类型,即size_t大小相同的有符号类型。 您可以考虑使用它而不是 size_t . 然而,根据我的经验,它会引起与(size_t)-1相同的焦虑。 另外,在 32 位机器上,您可以将 3 GiB 的内存块视为 char 数组,但将 ssize_t 作为返回类型,您不能有效地使用超过 2 GiB — 或者您需要使用 SSIZE_MIN(如果存在;我不确定是否如此),而不是-1作为信号值。


*海湾合作委员会或Clang必须被相当严厉地挑衅。 仅仅使用-Wall是不够的;触发警告需要-Wextra(或特定的-Wsign-compare选项)。 由于我经常使用 -Wextra 编译,我知道这个问题;不是每个人都那么警惕。

比较有符号和无符号数量完全由标准定义,但可能导致违反直觉的结果(因为小负数在转换为无符号值时显得非常大),这就是为什么编译器在被要求这样做时会抱怨的原因。

通常,如果您想返回负值并且仍然对大小类型有一些概念,请使用ssize_t . gcc 和 clang 都抱怨,但以下编译。请注意,以下某些是未定义的行为...

#include <stdio.h>
#include <stdint.h>  
size_t foo() {
  return -1;
}
void print_bin(uint64_t num, size_t bytes);
void print_bin(uint64_t num, size_t bytes) {
  int i = 0;
  for(i = bytes * 8; i > 0; i--) {
    (i % 8 == 0) ? printf("|") : 1;
    (num & 1)    ? printf("1") : printf("0");
    num >>= 1;
  }
  printf("n");
}
int main(void){  
   long int x = 0;
   printf("%zun", foo());
   printf("%ldn", foo());
   printf("%zun", ~(x & 0)); 
   printf("%ldn", ~(x & 0));
   print_bin((~(x & 0)), 8);
}

输出为

18446744073709551615
-1
18446744073709551615
-1
|11111111|11111111|11111111|11111111|11111111|11111111|11111111|11111111

我在 64 位机器上。以下二进制

|11111111|11111111|11111111|11111111|11111111|11111111|11111111|11111111

可以表示-118446744073709551615,这取决于上下文,即以何种方式使用具有该二进制表示的类型。

相关内容

  • 没有找到相关文章

最新更新