如何检查C语言中是否存在"set"?

6
如果我像这样分配一个C数组:
int array[ 5 ];

接着,只需设置一个对象:

array[ 0 ] = 7;

如何检查其他键(array[1]array[2],...)是否存储了值?(在这种情况下,它们当然没有。)

是否有像PHP的isset()函数一样的功能?

if ( isset(array[ 1 ]) ) ...

据我所知,您只能检查是否等于0。 - Robus
1
@Robus 实际上不是这样的。C 数组不应该被初始化为 0。 - log0
6个回答

7

C语言中不存在这样的东西。静态数组的内容总是“设置”的。但是,您可以填充一些特殊值来假装它未初始化,例如:

// make sure this value isn't really used.
#define UNINITIALIZED 0xcdcdcdcd

int array[5] = {UNINITIALIZED, UNINITIALIZED, UNINITIALIZED, UNINITIALIZED, UNINITIALIZED};

array[0] = 7;

if (array[1] != UNINITIALIZED) {
   ...

我猜这是在 C 中唯一可行的选项。 - rano
谢谢,不知道为什么我之前没有想到! - pop850

6

你不能这样做

这些值都是未定义的(因此随机的)。

你可以显式地将所有值归零,以便至少有一个良好的起点。但使用魔数来检测对象是否已初始化被认为是不好的实践(但初始化变量被认为是好的实践)。

int array[ 5 ] = {};

但是,如果您想明确检查它们是否已经在创建时明确设置(而不使用魔数),则需要将该信息存储在另一个结构中。

int array[ 5 ]   = {};  // Init all to 0
int isSet[ 5 ]   = {};  // Init all to 0 (false)

int getVal(int index)          {return array[index];}
int isSet(int index)           {return isSet[index];}
void setVal(int index,int val) {array[index] = val; isSet[index] = 1; }

那要看情况。C 语言可能会初始化值(我认为它会为静态和全局变量进行初始化)。 - knittl
1
@knittl:从问题的上下文来看很难确定,所以我选择了谨慎起见。而且使用初始化列表显式初始化不会造成任何损害。 - Martin York
@Martin:它确实会造成伤害。在几乎所有真实的实现中,静态/全局变量的任何初始化(即使是全部为零)都会将对象从bss节移动到数据节,增加程序的大小。对于自动变量,不必要的初始化会浪费每次调用函数时的时间。 - R.. GitHub STOP HELPING ICE
@R..:我会告诉你:“在性能关键系统中,它会造成伤害。” - Martin York
1
这是{}标准吗?我以为应该是{0}。 - Nyan
显示剩余3条评论

2
在C语言中,在分配内存时,所有元素都将具有值(垃圾值)。因此,您无法真正拥有像您所要求的那样的函数。
但是,您可以使用memset()默认填充一些标准值,如0或INT_MIN,然后编写一个isset()代码。

1
memset 无法使用 INT_MIN 进行填充。它只能填充字节。 - R.. GitHub STOP HELPING ICE
垃圾回收仅适用于自动变量和堆分配。如果没有特别指定,全局和静态数组将被初始化为0。 - dmckee --- ex-moderator kitten
@R..,虽然这是正确的,在大多数现代平台上,INT_MIN~0,并且会用 -1 填充 int[] 的所有元素。 - strager
在有符号整数的补码表示中,INT_MIN 只是 ~0,这种类型很少见,实际上可能根本不存在。在大多数系统(二进制补码),~0==-1 - R.. GitHub STOP HELPING ICE

2

我不了解php,但这里可能有两种情况:

  • php数组实际上是哈希映射(awk可以做到这一点)
  • php数组被填充了可空类型

在任何情况下,对于数组的值来说,“未设置”都是一个有意义的概念。另一方面,内置类型的c数组始终在每个单元格中具有“某些”值。如果数组未初始化且为自动或在堆上分配,则这些值可能是随机的,但它们存在。

要获得php行为:

  • 实现(或找到一个具有)并使用哈希映射而不是数组的库。
  • 将其作为包含isNull字段的结构数组。
  • 将数组初始化为所有单元格中的某个sentinal值。

1

一个解决方案也许是使用一个单独的标志数组。当你赋值其中一个元素时,设置布尔数组中的标志。

你也可以使用指针。你可以使用空指针来表示尚未分配的数据。我在下面举例说明:

int * p_array[3] = {NULL,NULL,NULL};
        p_array[0] = malloc(sizeof(int));
        *p_array[0] = (int)0;
        p_array[2] = malloc(sizeof(int));
        *p_array[2] = (int)4;
        for (int x = 0; x < 3; x++) {
            if (p_array[x] != NULL) {
                printf("Element at %i is assigned and the value is %i\n",x,*p_array[x]);
            }else{
                printf("Element at %i is not assigned.\n",x);
            }
        }

你可以创建一个函数来分配内存并设置数据,另一个函数则类似于 PHP 中的 isset 函数,通过测试 NULL 来为您检测。希望这能帮到你。
编辑:确保在完成后释放内存。另一个函数可用于释放特定元素或整个数组。
我以前使用过 NULL 指针来表示数据尚未被创建或需要重新创建。

1

我喜欢的一种方法是创建两个数组,一个是位数组,标记数组中哪些索引已设置,另一个包含实际值。即使在您不需要知道数组中的项目是否“设置”的情况下,它也可以是有用的优化。将每个元素的1位比特位清零比初始化每个元素的8字节大小的size_t数组要快得多,特别是如果数组在其整个生命周期内仍然稀疏(大部分未填充)。

我使用这个技巧的一个实际例子是在子字符串搜索函数中,使用Boyer-Moore风格的坏字符跳过表。该表需要256个size_t类型的条目,但只需要填充与实际出现在针线中的字符相对应的条目。在非常短的搜索情况下,1kb(或64位上的2kb)的memset会占用CPU使用率,导致其他实现在是否使用表时进行启发式处理。但是,我让跳过表未初始化,并使用256位位数组(只有32字节要提供给memset)来标记使用哪些条目。


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