C语言中的数组长度

slhcrj9b  于 2022-12-03  发布在  其他
关注(0)|答案(4)|浏览(253)

我以前是一个C#程序员,有一些关于C语言的东西我不能理解。(具体来说,我正在使用C99标准编码)
我被教导说,在C中没有办法知道一个数组的长度,我需要把它的长度作为一个参数发送给我愿意使用的函数,但为什么呢?
例如,在C#中,我们可以输入array_name.length
另外,在二维数组中,为什么我必须指定数组的列数?我的意思是为什么这样做:

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

但这并不意味着:

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

j2cgzkjk1#

例如,在C#中,我们可以键入array_name.length
我没有使用C#,但是,如果在一个子例程中,你可以获得在其他地方创建的数组的长度,那么关于该长度的信息必须存储在内存中,并与数组沿着传递。必须有某种东西将该长度存储在内存中,并且,当数组作为参数传递时,必须有某种东西包括不仅仅是数组长度的信息。因此,C#使用内存和计算时间。
这样做的后果是你不能直接控制计算机。只要有东西在传递额外的信息,你就不能写一个更简单更有效的程序。这必然是浪费。只要你在有大量可用资源的情况下写程序,这是可以的。
C语言并不做这种额外的工作。当传递数组时,只传递它的位置,这就是你访问它的元素所需要的全部。如果一个特定的子例程需要它的长度,你可以手动传递它--这是你的选择,当你需要的时候,你可以这样做,但是当你不需要它们的时候,你也可以选择不浪费资源。你可以编写更有效的程序。
在二维数组中,为什么我必须指定数组的列数?
如果我们知道arrint的数组,我们知道元素arr[0]在开始,arr[1]在后面,arr[2]在后面,以此类推。要使用一维数组,我们只需要知道它从哪里开始。
如果我们知道arrayint的二维数组,我们知道a[0][0]在开始,arr[0][1]在后面,以此类推,但我们不知道arr[1][0]在哪里。它在arr[0][i]的一些元素之后,但我们不知道有多少,除非我们知道第二维。因此,为了使用二维数组,你必须知道第二维度的长度。2这是一个逻辑要求,而不是一个选择。

补充

通常,例程只需要知道它应该使用数组中的哪些元素,而不需要知道数组中有多少元素。
例程不需要给定数组长度的情况包括:

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

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

gdrx4gfi

gdrx4gfi2#

在大多数编程语言中,数据类型是 * 抽象 *:也就是说,如果你请求一个数字列表,它将在内存中创建结构来存储一个数字列表,并跟踪它的容量,有多少元素是满的,以及这些元素是否为“空”或包含值等。
C语言是一种低级语言,不处理抽象;它直接处理物理内存。如果你要求空间来存放5个整数,它会为5个整数分配内存。你想让它在某个地方跟踪数字“5”,以便记住你分配了5个整数?你没有要求这样做--你必须自己做。

z31licg0

z31licg03#

在C语言中,作为参数传递给函数的数组被转换为指向数组第一个元素的指针。数组的大小并没有隐式地传递给函数。作为程序员,你有责任将正确的数组大小传递给你的函数。

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

这种方法的一个问题是数组的参数只被假定为指向一个数组。它可以指向任何int,不管该int是否是数组的一个元素。如果犯了这个错误,就会发生典型的缓冲区溢出。

p8h8hvxi

p8h8hvxi4#

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,它把数组维度作为数组 type 的一部分。这是一种相反的极端--大小和形状在类型系统中被跟踪,但实际上在实践中使用起来相当苛刻。所以编写一个函数来对两个不同长度的不同数组中的浮点数求和是不切实际的。

相关问题