简体   繁体   English

C语言中的数组长度

[英]Array length in C language

I am a previous C# programmer and there is something I can't understand regarding C language (Specifically, I am coding using the C99 Standard)我以前是 C# 程序员,我对 C 语言有一些不明白的地方(具体来说,我正在使用 C99 标准进行编码)

I was taught that there is no way to know the length of an array in C and that I need to send its length as a parameter to the function I am willing to use but why is that?我被告知没有办法知道 C 中数组的长度,我需要将它的长度作为参数发送给我愿意使用的函数,但这是为什么呢? in C# for example we can type array_name.lenght例如在 C# 中,我们可以输入array_name.lenght

plus in two dimensional arrays why do I have to specify the number of columns of the array?加上在二维数组中为什么我必须指定数组的列数? I mean why this work:我的意思是为什么这项工作:

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

while this doesn't:虽然这没有:

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

in C# for example we can type array_name.length例如在 C# 中,我们可以输入array_name.length

I do not use C#, but, if, in a subroutine you can get the length of an array created elsewhere, then information about that length had to be stored in memory and passed along with the array.我不使用 C#,但是,如果在子例程中您可以获得在其他地方创建的数组的长度,那么有关该长度的信息必须存储在内存中并与数组一起传递。 Something had to put that length in memory, and, when the array was passed as an argument, something had to include information more than just the length of the array.有些东西必须把那个长度放在内存中,当数组作为参数传递时,有些东西必须包含比数组长度更多的信息。 So C# is using memory and computing time.所以 C# 正在使用内存和计算时间。

A consequence of this is you do not have direct control of the computer.这样做的结果是您无法直接控制计算机。 You cannot write a simpler more efficient program as long as something is passing extra information.只要有东西传递额外的信息,你就无法编写更简单、更高效的程序。 It is necessarily wasteful.必然是浪费。 That is fine as long as you are writing programs in situations where plenty of resources are available.只要您是在有大量资源可用的情况下编写程序,就可以了。

C does not make this extra effort. C 没有做这个额外的努力。 When an array is passed, only its location is passed, and that is all you need to access its elements.传递数组时,仅传递其位置,这就是访问其元素所需的全部内容。 If a particular subroutine needs its length, you can pass that manually—it is your choice to do it when you need to, but you also have the option not to waste resources when you do not need them.如果某个特定的子例程需要它的长度,您可以手动传递它——您可以选择在需要时这样做,但您也可以选择在不需要时不浪费资源。 You can write more efficient programs.您可以编写更高效的程序。

in two dimensional arrays why do I have to specify the number of columns of the array?在二维数组中为什么我必须指定数组的列数?

If we know arr is an array of int , we know element arr[0] is at the start, arr[1] is right after that, arr[2] is after that, and so on.如果我们知道arr是一个int数组,我们就知道元素arr[0]在开头, arr[1] arr[2]其后, arr[2]在那之后,依此类推。 To use a one-dimensional array, the only thing we need to know is where it starts.要使用一维数组,我们唯一需要知道的是它从哪里开始。

If we know array is a two-dimensional array of int , we know a[0][0] is at the start, arr[0][1] is after that, and so on, but we do not know where arr[1][0] is.如果我们知道arrayint的二维数组,我们知道a[0][0]在开始, arr[0][1]在之后,依此类推,但我们不知道arr[1][0]是。 It is after some number of elements arr[0][i] , but we do not know how many unless we know the second dimension.它在一定数量的元素arr[0][i] ,但除非我们知道第二维,否则我们不知道有多少。 Therefore, in order to use a two-dimensional array, you must know the length of the second dimension.因此,为了使用二维数组,必须知道第二维的长度。 That is a logical requirement, not a choice.这是一个逻辑要求,而不是一个选择。

Supplement补充

Generally, a routine only needs to know which elements of an array it is supposed to use.通常,例程只需要知道它应该使用数组的哪些元素。 It does not need to know how many elements there are in the array.它不需要知道数组中有多少个元素。

Situations in which a routine does not need to be given the length of an array include:例程不需要指定数组长度的情况包括:

  • To calculate the length of a string in a buffer, a routine (like strlen ) only needs to examine each byte in the buffer until it finds a null byte.要计算缓冲区中字符串的长度,例程(如strlen )只需要检查缓冲区中的每个字节,直到找到空字节。 It does not need to know how big the entire buffer is.它不需要知道整个缓冲区有多大。 (Example: A program creates a buffer of 100 bytes. It reads bytes from the terminal until a new-line is found. The user types only 12 characters and then a new-line. The buffer is filled with 12 bytes and a null character. A subroutine examining the string only needs to work with 13 bytes, not 100.) (例如:一个程序创建了一个 100 字节的缓冲区。它从终端读取字节,直到找到换行符。用户只键入 12 个字符,然后是一个换行符。缓冲区填充了 12 个字节和一个空字符. 检查字符串的子程序只需要处理 13 个字节,而不是 100 个。)
  • A routine might work on a fixed number of elements.例程可能会处理固定数量的元素。 For example, a subroutine to help with numerical integration might take three function values at one time, fit a curve to them, and return the area under the curve.例如,一个帮助数值积分的子程序可能一次取三个函数值,将它们拟合成一条曲线,然后返回曲线下的面积。 The main routine might have an entire array of function values, and it repeatedly calls the subroutine to evaluate different points in the array, passing the subroutine a pointer to the location to work on.主例程可能有一个完整的函数值数组,它反复调用子例程来评估数组中的不同点,向子例程传递一个指向要处理的位置的指针。 In each call, the subroutine only needs to know there are three values for it at the given address.在每次调用中,子例程只需要知道在给定地址有三个值。 It does not need to know how many are in the full array.它不需要知道完整数组中有多少。
  • A routine might work on the same number of elements in multiple arrays.一个例程可能会处理多个数组中相同数量的元素。 For example, a routine to perform a Discrete Fourier Transform might take a number of elements N to work on and four arrays: one for input of the real components, one for input of the imaginary components, one for output of the real components, and one for output of the imaginary components.例如,执行离散傅立叶变换的例程可能需要处理多个元素N和四个数组:一个用于实部的输入,一个用于虚部的输入,一个用于实部的输出,以及一种用于虚部的输出。 For each of the arrays, the routine uses N elements.对于每个数组,例程使用N 个元素。 This number N only needs to be passed to the routine in one parameter.这个数字N只需要在一个参数中传递给例程。 It would wasteful to store it in multiple locations, one for each array.将它存储在多个位置会很浪费,每个数组一个。

Another consideration is that sometimes we pass only part of an array to a routine.另一个考虑是有时我们只将数组的一部分传递给例程。 If I have some string in a buffer, I might want a subroutine to work on only part of that string, perhaps just one word in a command that has been parsed.如果缓冲区中有一些字符串,我可能希望子例程仅处理该字符串的一部分,也许只是已解析命令中的一个单词。 To do this, I can pass just a pointer to the start of that word and the length of the word to work on.为此,我可以只传递一个指向该单词开头的指针和要处理的单词长度。 In this case, the subroutine not only does not need to know the length of the array, it does not even need to know where the array starts.在这种情况下,子程序不仅不需要知道数组的长度,甚至不需要知道数组从哪里开始。 It only needs to know what it is asked to work on.它只需要知道它被要求做什么。 It would be wasteful to pass any other information.传递任何其他信息都是浪费。

In most programming languages, data types are abstractions : that is, if you ask for a list of numbers, it will create structures in memory for storing a list of numbers, and for keeping track of its capacity, how many elements are full, and perhaps whether the elements are "null" or contain values, etc.在大多数编程语言中,数据类型是抽象的:也就是说,如果您要求一个数字列表,它将在内存中创建用于存储数字列表的结构,并跟踪其容量,有多少元素已满,以及也许元素是“空”还是包含值等。

C is a low-level language that doesn't deal in abstractions; C 是一种低级语言,不涉及抽象; it deals directly with physical memory.它直接处理物理内存。 If you ask for space to put 5 integers, it allocates memory for 5 integers.如果您要求空间放置 5 个整数,它会为 5 个整数分配内存。 You wanted it to keep track of the number "5" somewhere to remember that you allocated 5 integers?您希望它在某处跟踪数字“5”以记住您分配了 5 个整数? You didn't ask for that--you'll have to do that yourself.你没有要求那个——你必须自己做。

In C an array passed as a parameter to a function is converted to a pointer to the first element of the array.在 C 中,作为参数传递给函数的数组被转换为指向数组第一个元素的指针。 The size of the array is not implicitly passed to the function.数组的大小不会隐式传递给函数。 You, the programmer, are responsible for passing the correct array size to your function.您,程序员,负责将正确的数组大小传递给您的函数。

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

One of the problems with this approach is the parameter for the array is only assumed to point to an array.这种方法的问题之一是数组的参数仅假定指向数组。 It could point to any int, whether or not that int is an element of an array.它可以指向任何 int,无论该 int 是否是数组的元素。 If this mistake is made a classical buffer overflow occurs.如果发生此错误,则会发生典型的缓冲区溢出。

C is a Procedural language (and closer to assembler than most Procedural languages), not an Object Oriented language. C 是一种过程语言(并且比大多数过程语言更接近汇编程序),而不是面向对象的语言。 IOW, Algol (and C) came way before Smalltalk (and C#), and Smalltalk taught us some important lessons. IOW、Algol(和 C)在 Smalltalk(和 C#)之前出现,Smalltalk 教会了我们一些重要的教训。

Sometimes you can use the following in C:有时您可以在 C 中使用以下内容:

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

...but when an array has been passed to a function, that often doesn't work anymore. ...但是当一个数组被传递给一个函数时,这通常不再起作用。

Another good way, that works in almost any situation in C, is to:另一个几乎适用于 C 中任何情况的好方法是:

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

IOW, define a symbolic constant for the length of a particular array, and use that instead of hardcoding constants. IOW,为特定数组的长度定义一个符号常量,并使用它而不是硬编码常量。

OO languages have metadata associated with objects anyway, so why not store a length in the metadata? OO 语言无论如何都有与对象关联的元数据,那么为什么不在元数据中存储长度呢? C doesn't do that sort of thing though - it was created in a time when bytes were precious, and metadata was seen as too much overhead. C 并没有做那种事情——它是在字节非常宝贵的时候创建的,元数据被认为是太多的开销。

And why do you have to partially define the size of an n dimensional array?为什么必须部分定义 n 维数组的大小? Because behind the scenes C is doing some math to multiply out where in memory a[x][y] exists, and again, it's not storing metadata to help you keep track of those dimensions.因为在幕后 C 正在做一些数学运算来乘以内存中 a[x][y] 存在的位置,而且它没有存储元数据来帮助您跟踪这些维度。

Consider that Pascal, another Procedural language, made the array dimensions part of an array's type .考虑另一种过程语言 Pascal 将数组维度作为数组类型的一部分 That was kind of the opposite extreme - the size and shape were kept track of in the type system, but was actually pretty draconian to use in practice.那是一种相反的极端——大小和形状在类型系统中被跟踪,但实际上在实践中使用起来非常严格。 So writing a function to sum the floats in two different arrays of two different lengths was impractical.因此,编写一个函数来对两个不同长度的两个不同数组中的浮点数求和是不切实际的。

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

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