我在测试代码时遇到了问题。我定义了一个宏来获取数组元素的数量,如下所示:
#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);
}
答案 0 :(得分:5)
如果您只部分初始化原始数据类型列表(即:int
s的数组),则其余元素将初始化为0
。
C99标准6.7.8.21
如果括号括起的列表中的初始值设定项少于聚合的元素或成员,或者更少的字符 用于初始化已知大小的数组的字符串文字 是数组中的元素,聚合的其余部分应为 隐式初始化与具有静态存储的对象相同 持续时间。
在您的情况下,您正在尝试确定初始化列表的大小。无法真正想到需要这样做的正当理由,但您可以简单地检查元素何时始终等于零。当然,如果您故意将元素设置为零,则会失败。
您编写的宏将正常工作(即:返回数组中元素的数量),但如果您在接受指向数组的指针作为参数的函数中使用它,它将失败,{{3} }
所有这一切,你无法在任何有意义的意义上确定初始化列表的大小,除非你这样做,你将初始化列表定义为宏:
#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;
}
这会将临时数组的存储生命周期限制为大括号之间的较窄范围。同样,我可以看到这可能作为一个实验很有用,但是无法看到它在生产代码中找到它的方式。
答案 1 :(得分: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关于你的问题在评论中:我将如何连接到数组?数组必须足够大以容纳另一个字符串:
如果使用字符串(而不是数字数组),可以使用字符串函数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|
strlen
从string
的地址开始,计算字符直到遇到第一个NULL。 (或0):
int len strlen(string); //will yield a len of 3
然后,您可以使用strncat(string, newStr, count);
将指定数量的字符 count 连接到已知容量的字符串,从而避免缓冲区溢出。
请记住, size == 20的字符串变量仅限于包含 length == 19的字符串。必须保留第20个位置NULL字符。
答案 2 :(得分:1)
如果我们可以假设最后一个初始化元素不为零(因为它与隐式初始化为零无法区分),这将会这样做:
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 :(得分:0)
如果要确定数组的初始值设定项数,请声明一个数组以保存初始值设定项元素并在其上应用宏ARRAY_SIZE()
。然后使用memcpy()
将初始值设定项复制到{ {1}}数组。
现在您可以轻松获得元素数量。
dst[20]