【问题标题】:How to count the number of elements of an array如何计算数组的元素个数
【发布时间】:2016-08-31 07:12:02
【问题描述】:

我在测试代码时遇到问题。我定义了一个宏来获取数组的元素个数,如下所示:

#define ARRAY_SIZE(arr) sizeof(arr) / sizeof(arr[0])

这个宏适用于计算初始化器与存储容量匹配的数组元素的数量(例如int buf[] = {1,2,3};),但对于声明为int buf[20] = {1,2,3};的数组不是很有效

现在我知道像这样计算数组元素非常容易,但是大量元素呢?你怎么数他们?计数可能是一个杀手,你知道的!

考虑以下代码:

#include <stdio.h>
#include <string.h>

#define ARRAY_SIZE(arr) sizeof(arr) / sizeof(arr[0])

void g_strcat(void *_Dst, size_t dstSize, size_t bytes, const void *_Src, size_t srcSize);

int main(void)
{
    int dst[20] = { 1,2,3 };
    int src[] = { 4,5,6 };

    size_t dstSize = 3; // dstSize = ARRAY_SIZE(dst) doesn't work
    size_t srcSize = ARRAY_SIZE(src);

    g_strcat(dst, dstSize, sizeof(int), src, srcSize);

    size_t n, newSize = dstSize + srcSize;
    for (n = 0; n < newSize; n++) {
        printf("%d ", dst[n]);
    }
    putchar('\n');
    return 0;
}

void g_strcat(void *_Dst, size_t dstSize, size_t bytes, const void *_Src, size_t srcSize)
{
    memcpy((char *)_Dst + (dstSize * bytes), _Src, srcSize * bytes);
}

【问题讨论】:

  • 你的意思是计算初始化列表的大小而不是数组的大小?
  • 是的,我就是这个意思
  • buf[20] = {1,2,3,0} 的计数是多少?
  • 这听起来很像XY problem为什么您需要知道有多少数组已被显式初始化?
  • @AndrewHenle 差不多。我只是回答它,因为有一个可行的解决方案,但问题本身在现实世界的场景中似乎毫无用处。真的很好奇为什么 OP 需要做到这一点。

标签: c arrays counting


【解决方案1】:

您的宏运行良好。声明:

int dst[20] = { 1,2,3 };

在堆栈内存中创建以下内容:

|1|2|3|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|

即使只用前三个值初始化,数组大小仍然是 20

关于您的问题在 cmets 中:我将如何连接到数组?数组必须足够大以容纳另一个字符串

如果您使用的是字符串(而不是数字数组),则可以使用字符串函数int len = strlen(string); 在连接之前测试字符串变量的现有用法。

sizeof 宏不同,strlen 是一个在字符数组中查找第一个 NULL 字符的函数:

char string[20] = {"yes"};

在内存中创建以下内容:

|y|e|s|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|

strlenstring 的地址开始计算字符,直到遇到第一个 NULL。 (或 0):

int len strlen(string); //will yield a len of 3

然后您可以使用strncat(string, newStr, count); 将指定数量的字符count 连接到一个已知容量的字符串,从而避免缓冲区溢出。

请记住,例如 size == 20 的字符串变量仅限于包含 length == 19 的字符串。第 20 位必须为NULL 字符。

【讨论】:

    【解决方案2】:

    如果我们可以假设最后一个初始化的元素不为零(因为这与被隐式初始化为零无法区分),则可以这样做:

    size_t trailing_zero_bytes(const void* data, size_t size) {
        for (; size > 0; size--) {
            if (((const char*)data)[size - 1] != 0) {
                break;
            }
        }
        return size;
    }
    
    #define ARRAY_SIZE(arr) \
        ((sizeof(arr) - trailing_zero_bytes(arr, sizeof(arr)) + sizeof(arr[0]) + 1) / sizeof(arr[0]))
    

    如果你想以不同的方式计算这两种情况,那你就完全不走运了(除非你使用 Clang 或 GCC-XML 或其他方式解析代码):

    int s1[5] = { 4,5,6 }; // 2 zeros implied
    int s2[5] = { 4,5,6,0 }; // 1 zero implied
    

    以上两种方法都会给3我的方法,对此无能为力。

    【讨论】:

      【解决方案3】:

      如果您仅部分初始化原始数据类型列表(即:ints 的数组),其余元素将初始化为 0

      C99 标准 6.7.8.21

      如果大括号括起来的列表中的初始值设定项少于聚合的元素或成员,或者 用于初始化已知大小的数组的字符串文字 是数组中的元素,聚合的其余部分应为 隐式初始化与具有静态存储的对象相同 持续时间。

      在您的情况下,您正在尝试确定初始化列表的大小。真的想不出需要这样做的正当理由,但您可以简单地检查元素何时开始始终等于零。当然,如果您故意将元素设置为零,这将失败。

      您编写的宏将正常工作(即:返回数组中元素的数量),但如果您在接受指向数组的指针作为参数的函数中使用它,它将失败,as pointer decay causes sizeof to act differently than one might expect.

      话虽如此,您无法在任何有意义的意义上确定初始化列表的大小,除非您执行类似的操作,将初始化列表定义为宏:


      代码清单


      #include <stdio.h>
      #define LIST    {1,2,3}
      
      int main(void)
      {
          int i[20] = LIST;
          int t[] = LIST;
      
          printf("elements in i: %d\n", sizeof(i)/sizeof(int));
          printf("elements in t: %d\n", sizeof(t)/sizeof(int));
      
          return 0;
      }
      

      样本输出


      elements in i: 20
      elements in t: 3
      

      您可以通过将丢弃的数组放入新的块作用域来最大程度地减少内存浪费,即:


      #include <stdio.h>
      #define LIST    {1,2,3}
      
      int main(void)
      {
          int i[20] = LIST;
          int initListSize = 0;
      
          {
              int t[] = LIST;
              initListSize = sizeof(t) / sizeof(int);
          }
      
          printf("elements in t: %d\n", initListSize);
      
          return 0;
      }
      

      这会将临时数组的存储寿命限制在大括号之间的狭窄范围内。同样,我可以看到这可能作为一个实验很有用,但看不到它会在生产代码中找到它的方式。

      【讨论】:

      • s/pointer-to-array/pointer to array element/,指向数组的指针具有数组类型(尽管我认为C编译器允许将指针分配给不同长度的数组。)
      • @juanchopanza 你是对的。我只是使用“指向数组的指针”而不是更详细地介绍,因为我不想混淆 OP 或者必须在我的(已经很长的)帖子中添加更多段落。深入了解指针类型/指针数组等的细微差别本身就是一个冗长的答案。不过感谢您指出这一点,因为如果他/她阅读此内容,它可能会对 OP 有所帮助。
      • 优秀的答案。虽然我发誓,在阅读你的答案之前,我在我的答案中找到了解决方案!
      【解决方案4】:

      如果要确定数组的初始化器数量,请声明一个数组来保存初始化器元素并对其应用宏ARRAY_SIZE()。然后使用memcpy() 将初始化器复制到dst[20] 数组。

      现在您可以轻松掌握元素数量。

      #include <stdio.h>
      #include <string.h>
      
      #define ARRAY_SIZE(arr) sizeof(arr) / sizeof(arr[0])
      
      void g_strcat(void *_Dst, size_t dstSize, size_t bytes, const void *_Src, size_t srcSize);
      
      int main(void)
      {
          int dst[20], src[] = { 4,5,6 };
          int initializer_list[] = { 1,2,3 };
      
          size_t init_size = ARRAY_SIZE(initializer_list);
          memcpy(dst, initializer_list, init_size * sizeof(int));
      
          size_t dstSize = init_size;
          size_t srcSize = ARRAY_SIZE(src);
      
          g_strcat(dst, dstSize, sizeof(int), src, srcSize);
          dstSize += srcSize;
      
          size_t n;
          for (n = 0; n < dstSize; n++) {
              printf("%d ", dst[n]);
          }
          putchar('\n');
          return 0;
      }
      
      void g_strcat(void *_Dst, size_t dstSize, size_t bytes, const void *_Src, size_t srcSize)
      {
          memcpy((char *)_Dst + (dstSize * bytes), _Src, srcSize * bytes);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-16
        • 1970-01-01
        • 2021-05-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多