将值-1作为size_t返回值返回C会产生什么影响?

时间:2016-04-27 03:14:30

标签: 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返回值返回-1,似乎这可能会在某些时候遇到麻烦。

3 个答案:

答案 0 :(得分:2)

有一些API可以使用最大有符号或无符号整数值作为标记值。例如,如果在字符串中找不到std::string::npos的值,并且find()等于std::string::npos,则C ++的std::string::find()方法会返回(std::string::size_type)-1。< / p>

同样,在iOS和OS X上,当在数组中找不到对象时,NSArray的{​​{3}}方法返回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;
    }
}

答案 1 :(得分:1)

你的编译器可能会决定抱怨比较签名与未签名 - GCC或Clang将被激活 * - 但否则“它工作”。在二进制补码机器上(这些天大多数机器),(size_t)-1SIZE_MAX相同 - 实际上,正如评论中的扩展中所讨论的那样,对于一些补码或符号幅度的机器来说它是相同的因为C99和C11标准的第6.3.1.3节中的措辞。

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

  

那么,这只是我可能最终遇到问题的一个边缘情况?

数组必须是char的数组,但要大到足以引起麻烦 - 虽然你可以使用32位机器拥有4 GiB内存,但拥有所有这些内容却相当不可思议内存提交给一个字符数组(并且它不太可能成为64位机器的问题;大多数不会运行到16个exbibytes内存)。所以这不是一个实际的边缘案例。

在POSIX中,有一个ssize_t类型,即size_t大小相同的签名类型。您可以考虑使用它而不是size_t。然而,根据我的经验,它引起(size_t)-1导致的同样焦虑。另外,在32位计算机上,你可以将3 GiB内存块视为char数组,但如果使用ssize_t作为返回类型,则无法使用超过2 GiB - 或者您需要使用SSIZE_MIN(如果它存在;我不确定),而不是-1作为信号值。

* GCC或Clang必须相当努力。仅使用-Wall是不够的;需要-Wextra(或特定的-Wsign-compare选项)才能触发警告。由于我经常用-Wextra编译,我知道这个问题;不是每个人都保持警惕。

比较有符号和无符号的数量是由标准完全定义的,但可能导致反直觉的结果(因为当转换为无符号值时,小的负数看起来非常大),这就是编译器在请求的情况下抱怨的原因。

答案 2 :(得分:0)

通常,如果您想要返回负值并且仍然有一些大小类型的概念,则使用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("%zu\n", foo());
   printf("%ld\n", foo());
   printf("%zu\n", ~(x & 0)); 
   printf("%ld\n", ~(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,它取决于上下文,即以何种方式使用具有该二进制表示的类型。