【问题标题】:How to check if "set" in c如何检查c中是否“设置”
【发布时间】:2011-04-08 12:38:46
【问题描述】:

如果我像这样分配一个 C 数组:

int array[ 5 ];

然后,只设置一个对象:

array[ 0 ] = 7;

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

有没有像PHP的isset()这样的函数?

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

【问题讨论】:

  • AFAIK 你只能检查 0。
  • @Robus 事实上没有。 C 数组不应该在 0 处初始化。

标签: c arrays int


【解决方案1】:

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 语言中唯一可能的选择
  • 谢谢,不知道为什么我以前没有遇到过这种情况!
【解决方案2】:

我喜欢的一种方法是制作 2 个数组,一个是位数组,标记设置了数组的哪些索引,另一个包含实际值。即使在您不需要知道数组中的某个项目是否“设置”的情况下,它也可能是一个有用的优化。将每个元素 1 位的位数组置零比初始化 size_t 的每个元素 8 字节的数组要快得多,尤其是当数组在其整个生命周期内保持稀疏(大部分未填充)时。

我使用此技巧的一个实际示例是在子字符串搜索函数中,使用 Boyer-Moore 样式的坏字符跳过表。该表需要size_t类型的条目256个,但只需要填写与针串中实际出现的字符对应的条目。在非常短的搜索的情况下,1kb(或 64 位上的 2kb)memset 将主导 cpu 使用,导致其他实现抛出是否使用表的启发式方法。但相反,我让跳过表未初始化,并使用 256 位位数组(只有 32 个字节提供给memset)来标记正在使用的条目。

【讨论】:

    【解决方案3】:

    我不知道 php,但这里发生了两件事之一

    • php 数组实际上是一个哈希映射(awk 就是这样做的)
    • php 数组正在填充可空类型

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

    获取 php 行为:

    • 在数组上实现(或查找库机智)并使用 hashmap 代替。
    • 使其成为包含 isNull 字段的结构数组。
    • 将数组初始化为所有单元格中的某个标记值。

    【讨论】:

      【解决方案4】:

      一种解决方案可能是使用单独的标志数组。当您分配其中一个元素时,请在布尔数组中设置标志。

      您也可以使用指针。您可以使用空指针来表示尚未分配的数据。我在下面做了一个例子:

      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 指针表示数据尚未创建或需要重新创建。

      【讨论】:

        【解决方案5】:

        你不能

        所有值都是未定义的(因此是随机的)。

        您可以明确地将所有值清零,这样您至少有一个良好的起点。但是使用幻数来检测对象是否已被初始化被认为是不好的做法(但初始化变量被认为是好的做法)。

        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:从问题的上下文来看,这是不可能的,所以我在谨慎方面犯了错误。并且使用初始化列表显式初始化也没有坏处。
        • @Martin:它确实有害。在几乎所有实际实现中,静态/全局变量的任何初始化(甚至全为零)都会将对象从 bss 部分移动到数据部分,从而增加程序的大小。而对于自动变量,每次调用函数时不必要的初始化都会浪费时间。
        • @R..:我会告诉你:“在性能关键的系统中它会造成伤害”
        • 是 {} 标准吗?我认为应该是 {0}?
        【解决方案6】:

        在 C 中,所有元素在分配时都会有值(垃圾)。所以你不能真正拥有你所要求的功能。

        但是,默认情况下,您可以使用 memset() 填充一些标准值,例如 0 或 INT_MIN,然后编写 isset() 代码。

        【讨论】:

        • memset 不能用INT_MIN 填充。它只填充字节。
        • 垃圾只有自动变量和堆分配才是真的。如果没有另外指定,全局和静态数组将被初始化为 0。
        • @R..,虽然这是真的,但在大多数现代平台上,INT_MIN~0,并且会用-1 填充int[] 的所有元素。
        • INT_MIN 只是 ~0 在有符号整数的符号/大小表示上,这是最稀有的类型,在实践中可能根本不存在。在大多数系统(二进制补码)上,~0==-1.
        猜你喜欢
        • 2016-12-05
        • 1970-01-01
        • 1970-01-01
        • 2021-12-20
        • 1970-01-01
        • 1970-01-01
        • 2018-06-19
        • 2012-02-21
        • 2013-10-01
        相关资源
        最近更新 更多