繁体   English   中英

C语言中的数组长度

[英]Array length in C language

我以前是 C# 程序员,我对 C 语言有一些不明白的地方(具体来说,我正在使用 C99 标准进行编码)

我被告知没有办法知道 C 中数组的长度,我需要将它的长度作为参数发送给我愿意使用的函数,但这是为什么呢? 例如在 C# 中,我们可以输入array_name.lenght

加上在二维数组中为什么我必须指定数组的列数? 我的意思是为什么这项工作:

void test1 (int arr[][m])
{
}

虽然这没有:

void test2 (int arr[][])
{
}

例如在 C# 中,我们可以输入array_name.length

我不使用 C#,但是,如果在子例程中您可以获得在其他地方创建的数组的长度,那么有关该长度的信息必须存储在内存中并与数组一起传递。 有些东西必须把那个长度放在内存中,当数组作为参数传递时,有些东西必须包含比数组长度更多的信息。 所以 C# 正在使用内存和计算时间。

这样做的结果是您无法直接控制计算机。 只要有东西传递额外的信息,你就无法编写更简单、更高效的程序。 必然是浪费。 只要您是在有大量资源可用的情况下编写程序,就可以了。

C 没有做这个额外的努力。 传递数组时,仅传递其位置,这就是访问其元素所需的全部内容。 如果某个特定的子例程需要它的长度,您可以手动传递它——您可以选择在需要时这样做,但您也可以选择在不需要时不浪费资源。 您可以编写更高效的程序。

在二维数组中为什么我必须指定数组的列数?

如果我们知道arr是一个int数组,我们就知道元素arr[0]在开头, arr[1] arr[2]其后, arr[2]在那之后,依此类推。 要使用一维数组,我们唯一需要知道的是它从哪里开始。

如果我们知道arrayint的二维数组,我们知道a[0][0]在开始, arr[0][1]在之后,依此类推,但我们不知道arr[1][0]是。 它在一定数量的元素arr[0][i] ,但除非我们知道第二维,否则我们不知道有多少。 因此,为了使用二维数组,必须知道第二维的长度。 这是一个逻辑要求,而不是一个选择。

补充

通常,例程只需要知道它应该使用数组的哪些元素。 它不需要知道数组中有多少个元素。

例程不需要指定数组长度的情况包括:

  • 要计算缓冲区中字符串的长度,例程(如strlen )只需要检查缓冲区中的每个字节,直到找到空字节。 它不需要知道整个缓冲区有多大。 (例如:一个程序创建了一个 100 字节的缓冲区。它从终端读取字节,直到找到换行符。用户只键入 12 个字符,然后是一个换行符。缓冲区填充了 12 个字节和一个空字符. 检查字符串的子程序只需要处理 13 个字节,而不是 100 个。)
  • 例程可能会处理固定数量的元素。 例如,一个帮助数值积分的子程序可能一次取三个函数值,将它们拟合成一条曲线,然后返回曲线下的面积。 主例程可能有一个完整的函数值数组,它反复调用子例程来评估数组中的不同点,向子例程传递一个指向要处理的位置的指针。 在每次调用中,子例程只需要知道在给定地址有三个值。 它不需要知道完整数组中有多少。
  • 一个例程可能会处理多个数组中相同数量的元素。 例如,执行离散傅立叶变换的例程可能需要处理多个元素N和四个数组:一个用于实部的输入,一个用于虚部的输入,一个用于实部的输出,以及一种用于虚部的输出。 对于每个数组,例程使用N 个元素。 这个数字N只需要在一个参数中传递给例程。 将它存储在多个位置会很浪费,每个数组一个。

另一个考虑是有时我们只将数组的一部分传递给例程。 如果缓冲区中有一些字符串,我可能希望子例程仅处理该字符串的一部分,也许只是已解析命令中的一个单词。 为此,我可以只传递一个指向该单词开头的指针和要处理的单词长度。 在这种情况下,子程序不仅不需要知道数组的长度,甚至不需要知道数组从哪里开始。 它只需要知道它被要求做什么。 传递任何其他信息都是浪费。

在大多数编程语言中,数据类型是抽象的:也就是说,如果您要求一个数字列表,它将在内存中创建用于存储数字列表的结构,并跟踪其容量,有多少元素已满,以及也许元素是“空”还是包含值等。

C 是一种低级语言,不涉及抽象; 它直接处理物理内存。 如果您要求空间放置 5 个整数,它会为 5 个整数分配内存。 您希望它在某处跟踪数字“5”以记住您分配了 5 个整数? 你没有要求那个——你必须自己做。

在 C 中,作为参数传递给函数的数组被转换为指向数组第一个元素的指针。 数组的大小不会隐式传递给函数。 您,程序员,负责将正确的数组大小传递给您的函数。

int sum(int *num, size_t length)
{
   int total = 0;
   int i;
   for (i = 0; i < length; i++)
   {
      total += num[i];
   }
}

这种方法的问题之一是数组的参数仅假定指向数组。 它可以指向任何 int,无论该 int 是否是数组的元素。 如果发生此错误,则会发生典型的缓冲区溢出。

C 是一种过程语言(并且比大多数过程语言更接近汇编程序),而不是面向对象的语言。 IOW、Algol(和 C)在 Smalltalk(和 C#)之前出现,Smalltalk 教会了我们一些重要的教训。

有时您可以在 C 中使用以下内容:

#define num_elements(array) (sizeof(array) / sizeof(array[0]))

...但是当一个数组被传递给一个函数时,这通常不再起作用。

另一个几乎适用于 C 中任何情况的好方法是:

#define MY_ARRAY_ELEMENTS 1000
int a[MY_ARRAY_ELEMENTS];
foo(a, MY_ARRAY_ELEMENTS);

IOW,为特定数组的长度定义一个符号常量,并使用它而不是硬编码常量。

OO 语言无论如何都有与对象关联的元数据,那么为什么不在元数据中存储长度呢? C 并没有做那种事情——它是在字节非常宝贵的时候创建的,元数据被认为是太多的开销。

为什么必须部分定义 n 维数组的大小? 因为在幕后 C 正在做一些数学运算来乘以内存中 a[x][y] 存在的位置,而且它没有存储元数据来帮助您跟踪这些维度。

考虑另一种过程语言 Pascal 将数组维度作为数组类型的一部分 那是一种相反的极端——大小和形状在类型系统中被跟踪,但实际上在实践中使用起来非常严格。 因此,编写一个函数来对两个不同长度的两个不同数组中的浮点数求和是不切实际的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM