【问题标题】:Basic array usage in C?C中的基本数组用法?
【发布时间】:2011-08-08 08:56:02
【问题描述】:

这就是你们在 ANSI-C99 中获取数组大小的方法吗?看起来有点笨拙,来自高等语言。

int tests[7];
for (int i=0; i<sizeof(tests)/sizeof(int); i++) {
    tests[i] = rand();
}

这也是分段错误。

int r = 10000000;
printf ("r: %i\n", r);
int tests[r];

运行它:

r: 10000000
Segmentation fault

10000000 段错误,但 1000000 有效。

如何从中获取更多信息?我应该检查什么以及如何调试这样的东西? C 数组有限制吗?什么是分段错误?

【问题讨论】:

  • 所以!尝试在堆上而不是在堆栈上分配。

标签: c arrays segmentation-fault


【解决方案1】:

在 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,AFAIK,sizeof 为我们提供了以字节为单位的大小。如果不是这种情况,您能否详细说明一下?
  • @unwind:C99 标准说返回值是以字节为单位的大小。 (第 6.5.3.4 节)。但是,它也说了各种使char 和“字节”或多或少相同的东西。
  • @JeremyP - 这不一定提供信息。 Byte 的本义(大约 100 年前,当它第一次被创造时)基本上是单个字符所需的空间。然而,单个字符所需的空间(可能在制表符的穿孔卡片上)当时可能不是 8 位。现代(至少 30 年)通用标准 8 位字节定义并未被普遍接受或“官方”,因此除非 C 标准明确说明一个字节(或一个字符)中有多少位。 .
  • @Steve314: byte 的本义无关。在 C99 中,明确表示 sizeof 以字节为单位返回大小。它还明确表示 sizeof(char) 为 1。它还明确表示在标准范围内,字节和字符是同一事物(还定义了宽字符和多字节字符等术语以解释 UTF-16 和UTF-8)。 C99 没有说一个字节有 8 位。
【解决方案2】:

为什么要计算大小?

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

作为一个主要是 C++ 的程序员,我想说从历史上看,常量经常被定义为枚举值或 #define。不过,在 C 中,这可能是当前的而不是历史的 - 我不知道当前的 C 如何处理“const”。

如果你真的想计算大小,定义一个宏来做。甚至可能有一个标准的。

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

【讨论】:

  • 谢谢。我如何告诉我的堆栈限制以及如何调试它以确定是否是这种情况?
  • @user697111 - 老实说我不知道​​。我知道这个大小限制是存在的,比许多人想象的要小,并且因平台而异,但我从来没有比这更担心。多年来我一直不担心多 KB 的本地变量,但即使是那些也不是典型的——大对象往往都存在于堆中,因为它们往往寿命长,并且比创建它们的函数寿命长。此外,即使我知道我的机器的确切数字,我也无法使用这些知识 - 其他人的机器会有更小的限制。
  • @KDM - 很好的链接 - 我特别喜欢 Steve Jessops 对已接受答案的“沉默的阴谋”评论。
  • 值得补充的一点 - 这个堆栈大小问题是首选迭代算法而不是递归的原因,除非您可以确信您的编译器会将递归转换为迭代。这些保证在函数式语言中很常见,但在 C 中没有保证。在实践中,像 GCC 这样的编译器可以在更多情况下优化迭代,而不仅仅是尾递归,但是(特别是在 C++ 中而不是 C 中)思考某些东西的可能性当它不是问题时会优化。 “优化”通常不是优化 - 这是一个正确性问题。
【解决方案3】:

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

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

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

请注意,在大多数情况下,您可以将tests 视为它是一个数组类型(即,您可以为它下标,您可以将它传递给任何需要数组的函数,等等。 ),但它不是数组类型;它是一个指针类型,所以 sizeof tests / sizeof tests[0] 技巧不起作用。

【讨论】:

    【解决方案4】:

    传统上,数组具有静态大小。所以我们可以做

    #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];
    

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

    【讨论】:

    • 严格来说,这些“堆数组”与 C 数组没有太大区别——尤其是对于 int 或 C++ 认为“普通旧数据”的其他类型。它是 malloc 的“语法糖”,除了构造函数处理,它与 POD 无关。实际上,严格来说,C++ 可以为 new 使用单独的堆——你不应该释放你的新内容,或者删除你 malloc 的内容,无论构造函数/析构函数问题如何——但它仍然只是分配一个至少正确大小的内存块.
    【解决方案5】:

    我怀疑是因为整数溢出。尝试使用 printf 打印值:

    printf("%d", 10000000);
    

    如果它打印一个负数 - 这就是问题所在。

    【讨论】:

    • printf ("%i\n", 10000000); printf ("%i\n", 100000000); printf ("大小: %i\n", sizeof(int)); 10000000 100000000 大小:4
    • 32 位整数不会为此溢出。 24 位有符号整数将失败(仅 - 1000 万作为无符号 24 位可以),但使用 24 位地址(单字节值数组除外)转换为存储位置的数学运算将失败。具有 24 位类型的架构很少见,但过去至少有一些。
    【解决方案6】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-06-07
      • 2014-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多