C语言中基本的数组用法?

3
这是您们在 ANSI-C99 中获取数组大小的方式吗?相比更高级的语言,这种方式似乎有点笨拙。
int tests[7];
for (int i=0; i<sizeof(tests)/sizeof(int); i++) {
    tests[i] = rand();
}

此外,出现了“Segmentation Faults”。
int r = 10000000;
printf ("r: %i\n", r);
int tests[r];

运行它:

r: 10000000
Segmentation fault

10000000个段错误,但1000000个工作。

我该如何获取更多信息?我应该检查什么以及如何调试此类问题?C数组是否有限制?什么是段错误?


尝试在堆上分配内存,而不是在栈上分配。 - J T
6个回答

3
在C语言中获取数组大小很容易。这将以字节为单位给出数组的大小。
sizeof(x)

但我猜你需要的是元素数量,在这种情况下,它将是:
sizeof(x) / sizeof(x[0])

你可以为此编写一个简单的宏:
#define NumElements(x)  (sizeof(x) / sizeof(x[0]))

例如:

int a[10];
int size_a = sizeof(a); /* size in bytes */
int numElm = NumElements(a); /* number of elements, here 10 */

在这里,“byte”应该被读作“char”,它不一定是8位,可能更大。 - unwind
@unwind,AFAIK,sizeof给出的是以字节为单位的大小。您能否详细说明在什么情况下不是这样? - BiGYaN
@unwind:C99标准规定返回值是以字节为单位的大小(第6.5.3.4节)。然而,它也指出了一些使得“char”和“byte”更或者更少相同的事情。 - JeremyP
@JeremyP - 这并不一定具有信息价值。Byte(大约100年前首次创造该词)的最初含义基本上是一个字符所需的空间。然而,单个字符所需的空间(可能在制表机上的打孔卡上)当时可能不是8位。现代(至少30年来)通用的8位字节定义不是普遍接受或“官方”的,因此除非C标准明确说明字节(或字符)中有多少位... - user180247
@Steve314:byte 的原始含义已经不重要了。在 C99 中,sizeof 明确规定返回字节大小。它还明确表示 sizeof(char) 为1。它还明确表示,在标准的范围内,一个字节和一个字符是相同的东西(还定义了诸如宽字符和多字节字符之类的术语来解释 UTF-16 和 UTF-8 等内容)。C99 没有任何地方说一个字节有8位。 - JeremyP
@JeremyP - unwind提出了位数的问题。你说标准使char和byte基本上是一样的东西。我只是想说,并不是很有信息量——如果你不知道char的大小,那么说char和byte基本上是一样的东西并不能告诉你byte的大小。关于“C99没有地方说一个字节有8个位”,我不确定,但怀疑这就是我写评论的原因。在答案中,虽然byte的含义无关紧要,因为sizeof的定义是以字节为单位的,并且不会因为“byte”的含义而变化。 - user180247

2

为什么要计算大小?

定义一个包含大小的常量,并在声明数组时使用该常量。每当需要数组的大小时,请引用该常量。

作为一名主要使用C++的程序员,我会说历史上常量通常被定义为枚举值或# define。在C中,这可能是当前而不是历史,但我不知道当前的C如何处理“const”。

如果您真的想计算大小,请定义一个宏来执行此操作。甚至可能有一个标准的宏。

segfault的原因很可能是您尝试声明的数组大约有40兆字节,且声明为局部变量。大多数操作系统都限制堆栈的大小。将数组保留在堆或全局存储器中,对于大多数系统来说,一个变量的40兆字节可能都没问题,尽管某些嵌入式系统可能仍会抱怨。在像Java这样的语言中,所有对象都在堆上,只有引用保留在堆栈上。这是一个简单灵活的系统,但通常比将数据存储在堆栈上效率低得多(堆分配开销,可避免的堆碎片,间接访问开销...)。


谢谢。我该如何告诉我的堆栈限制,以及我如何调试它来确定是否是这种情况? - user697111
@user697111 - 老实说,我一点也不清楚。我知道这个大小限制存在,并且比许多人想象中要小,而且因平台而异,但我从未超过过多担心它。多年来,我从未担心过多千字节的本地变量,但即使是这些也不太典型——大对象往往会在堆上生存,因为它们往往具有长寿命,并且会超过创建它们的函数。此外,即使我知道我的计算机的确切数字,我也不能使用该知识——其他人的计算机将具有较小的限制。 - user180247
1
也许这会有帮助:https://dev59.com/c3VD5IYBdhLWcg3wNIvc - Dakshinamurthy Karra
@KDM - 好链接 - 我特别喜欢Steve Jessops对被接受的答案的“沉默阴谋”的评论。 - user180247
值得一提的是,这个堆栈大小问题是倾向于使用迭代算法而不是递归算法的原因,除非你能确信你的编译器会将递归转换为迭代。在函数式语言中,这样的保证很常见,但在 C 语言中没有保证。实际上,像 GCC 这样的编译器可以在更多情况下优化为迭代,但(特别是在 C++ 而不是 C 中)认为某些东西会进行优化而实际上并没有进行优化是一个问题。这种“优化”通常不是优化,而是正确性问题。 - user180247

1

传统上,数组具有静态大小。因此我们可以这样做

#define LEN 10
int arr[LEN];

但不包括

int len;
scanf("%d", &len);
int arr[len]; // bad!

由于我们在编译时知道数组的大小,因此获取数组的大小往往是微不足道的。我们不需要使用sizeof,因为我们可以通过查看声明来确定大小。

C++提供了堆数组,例如

int len;
scanf("%d", &len);
int *arr = new int[len];

但由于这涉及指针而不是堆栈数组,我们必须将大小存储在变量中,并手动传递。


2
严格来说,这些“堆数组”与C数组并没有太大的区别 - 尤其是对于int或其他C++认为是“普通旧数据”的类型。它只是一个malloc的“语法糖”,除了构造函数处理外,对于POD来说无关紧要。实际上,严格来说,C++可能会为new使用单独的堆 - 无论构造函数/析构函数问题如何,您都不应该释放您new的内容,也不应该删除您malloc的内容 - 但它仍然只是分配至少正确大小的内存块。 - user180247

1

C语言中的数组不知道它们有多大,所以是的,你必须使用sizeof array / sizeof array[0]技巧来获取数组中元素的数量。

至于segfault问题,我猜测你试图分配10000000 * sizeof int字节时超出了堆栈大小。一个经验法则是,如果你需要超过几百个字节,使用malloccalloc动态分配内存,而不是尝试创建一个大的auto变量:

int r = 10000000;
int *tests = malloc(sizeof *test * r);

请注意,您可以在大多数情况下(即,您可以对其进行下标操作,可以将其传递给任何期望数组的函数等)将tests视为数组类型,但它并不是数组类型;它是指针类型,因此sizeof tests / sizeof tests[0]技巧不起作用。

0

堆栈溢出!尝试在堆上分配内存,而不是在栈上。


0
我怀疑这是由于整数溢出引起的。尝试使用printf打印该值:
printf("%d", 10000000);

如果它打印出一个负数,那就是问题所在。

printf ("%i\n", 10000000); printf ("%i\n", 100000000); printf ("Size: %i\n", sizeof(int)); 10000000 100000000 Size: 4 - user697111
一个32位整数不会溢出。一个24位有符号整数会失败(只有-1000万作为无符号24位是可以的),但是使用24位地址转换存储位置的数学运算(除了单字节值的数组)将会失败。具有24位类型的架构很少见,但过去至少有一些。 - user180247

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