因为在C语言中,数组长度必须在定义数组时声明,那么使用第一个元素作为长度是否是可接受的做法?例如:
int arr[9]={9,0,1,2,3,4,5,6,7};
然后使用这样的一个函数来处理数组:
int printarr(int *ARR) {
for (int i=1; i<ARR[0]; i++) {
printf("%d ", ARR[i]);
}
}
我认为这没有什么问题,但我更愿意先向有经验的C程序员进行核实。我将是唯一使用该代码的人。
因为在C语言中,数组长度必须在定义数组时声明,那么使用第一个元素作为长度是否是可接受的做法?例如:
int arr[9]={9,0,1,2,3,4,5,6,7};
然后使用这样的一个函数来处理数组:
int printarr(int *ARR) {
for (int i=1; i<ARR[0]; i++) {
printf("%d ", ARR[i]);
}
}
我认为这没有什么问题,但我更愿意先向有经验的C程序员进行核实。我将是唯一使用该代码的人。
从某种意义上来说,这很糟糕,因为你有一个数组,其中元素的含义不同。将元数据与数据存储在一起并不是一件好事。只是稍微推广一下你的想法。我们可以使用第一个元素表示元素大小,然后使用第二个元素表示长度。试着编写一个利用两者的函数 ;)
值得注意的是,使用这种方法,如果数组大于元素可容纳的最大值,对于char
数组来说是非常重要的限制,你会遇到问题。当然,你可以通过使用前两个元素来解决它。如果你有浮点数数组,你也可以使用强制类型转换。但我可以保证,你会因此遇到难以追踪的错误。其中,字节序可能会引起很多问题。
这肯定会让几乎所有经验丰富的C程序员都感到困惑。这并不是真正反对这个想法本身的逻辑论据,而是一种实用主义的论据。即使这是一个好主意(它不是),你也必须和每一个将要涉及你的代码的程序员进行长时间的谈话。
实现相同功能的一个合理方式是使用结构体。
struct container {
int *arr;
size_t size;
};
int arr[10];
struct container c = { .arr = arr, .size = sizeof arr/sizeof *arr };
但是在我会使用类似上述代码的任何情况下,我可能不会使用数组,而是使用动态分配:
const size_t size = 10;
int *arr = malloc(sizeof *arr * size);
if(!arr) { /* Error handling */ }
struct container c = { .arr = arr, .size = size };
需要注意的是,如果您使用指针而不是数组进行初始化,可能会得到“有趣”的结果。
您也可以使用灵活数组,就像Andreas在他的答案中所写的一样。
+-
和*/
时,它总是会以相同的方式工作,但是当涉及到二元操作符例如那些不经常使用的(|
和^
)时,一些编译器将会有所不同。我可以想象,对于一些编译器,sizeof a / sizeof b
可能会被解释为sizeof(a / sizeof(b))
。 - Cosinussizeof(a / sizeof(b))
的编译器不符合标准。 - klutt在C语言中,你可以使用灵活数组成员。也就是说,你可以编写:
struct intarray {
size_t count;
int data[]; // flexible array member needs to be last
};
您可以使用以下方式进行内存分配:
size_t count = 100;
struct intarray *arr = malloc( sizeof(struct intarray) + sizeof(int)*count );
arr->count = count;
这可以用于所有类型的数据。
它使得使用C数组更安全(不如C++容器安全,但比普通的C数组更安全)。
不幸的是,C++标准不支持这种惯用法。许多C++编译器提供它作为扩展,但不能保证。
另一方面,与C++容器不同,这个C FLA习惯用法可能更加显式和高效,因为它不需要额外的间接寻址和/或需要两个分配(想想new vector<int>
)。
如果你坚持使用C,我认为这是一种非常显式和易读的处理带有集成大小的可变长度数组的方式。
唯一的缺点是C++的人不喜欢它,而更喜欢C++容器。
offsetof()
,sizeof
是否保证处理填充? - Voosizeof *arr + sizeof arr->data[0] * count
:D - Antti Haapala -- Слава Україні如果数组的元素是整数,那么它不会出现未定义行为或其他可移植性问题,但是你应该让它计算数组长度,而不是直接写魔法数字9
,以避免打错字。
#include <stdio.h>
int main(void) {
int arr[9]={sizeof(arr)/sizeof(*arr),0,1,2,3,4,5,6,7};
for (int i=1; i<arr[0]; i++) {
printf("%d ", arr[i]);
}
return 0;
}
&arr[1]
,或者定义一个使用“Pascal字符串”而不是“ASCIZ字符串”约定的新字符串库(这样的库实际上更有效率)。对于真正的数组而不仅仅是指向内存的指针,sizeof(arr) / sizeof(*arr)
将产生元素数量,而不需要在任何情况下将其存储在数组中。它只适用于整数类型数组,并且对于char
数组,长度会受到限制。对于其他对象类型或数据结构的数组,它并不实用。更好的解决方案是使用一个结构体:typedef struct
{
size_t length ;
int* data ;
} intarray_t ;
然后:
int data[9] ;
intarray_t array{ sizeof(data) / sizeof(*data), data } ;
data
成员的类型可以是任何类型。只有少数数据类型适合这种黑客方式。因此,我建议不要这样做,因为这会导致在不同类型的数组中实现风格不一致。
在字符缓冲区中,很常用一种类似的方法,在缓冲区的开头存储实际长度。
C 语言中的动态内存分配也使用了这种方法,即分配的内存以一个整数为前缀,保存了所分配内存的大小。
但是通常情况下,这种方法对数组不适用。例如,字符数组可能比类型为 char
的对象可以存储的最大正值(127)要大得多。此外,将这样的数组的子数组传递给函数也很困难。大多数设计用于处理数组的函数在这种情况下都无法工作。
处理数组的通用方法是声明两个参数的函数。第一个参数具有指针类型,指定数组或子数组的初始元素,第二个参数指定数组或子数组的元素数量。
C 还允许声明接受可变长度数组的函数,当它们的大小可以在运行时指定时。
显然答案是否定的。所有编程语言都有预定义的函数与变量类型一起存储。为什么不使用它们呢?对于你的情况,更适合访问计数/长度方法而不是测试第一个值。
if语句有时比预定义函数需要更多的时间。
乍一看,存储计数器似乎没问题,但想象一下你将不得不更新数组。您将需要执行两个操作,一个插入另一个更新计数器。因此,2个操作意味着必须更改2个变量。对于静态数组,可能将它们作为计数器与列表一起使用,但对于动态数组,则否定否定否定。 另一方面,请阅读编程基本概念,您会发现您的想法是错误的,不符合编程原则。
#define MYSIZE 9
,然后int arr[MYSIZE]={0,1,2,3,4,5,6,7};
。现在您知道大小为MYSIZE
。否则,固定大小数组的大小始终为sizeof(array) / sizeof(array[0])
。一些实现具有_countof
宏,如果没有,您可以基于上述内容自己制作。 - Jabberwocky