我正在读一本教科书,其中一个例子就是这样做的。下面,我以缩写形式重现了该示例:
#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)-1
与SIZE_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
可以表示-1
或18446744073709551615
,这取决于上下文,即以何种方式使用具有该二进制表示的类型。