Длина массива на языке C - PullRequest
1 голос
/ 06 марта 2020

Я предыдущий C# программист, и есть кое-что, чего я не могу понять в отношении C языка (в частности, я пишу код с использованием стандарта C99)

Меня учили, что нет никакого способа чтобы узнать длину массива в C и что мне нужно отправить его длину в качестве параметра функции, которую я хочу использовать, но почему это так? например, в C# мы можем ввести array_name.lenght

plus в двумерных массивах. Почему мне нужно указывать количество столбцов массива? Я имею в виду, почему эта работа:

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

, а это не так:

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

Ответы [ 4 ]

5 голосов
/ 06 марта 2020

в C# например, мы можем набрать array_name.length

Я не использую C#, но, если в подпрограмме вы можете получить длину массива созданная в другом месте, затем информация об этой длине должна была быть сохранена в памяти и передана вместе с массивом. Что-то должно было поместить эту длину в память, и, когда массив передавался в качестве аргумента, что-то должно было включать информацию больше, чем просто длину массива. Так что C# использует память и вычислительное время.

Следствием этого является то, что вы не имеете прямого контроля над компьютером. Вы не можете написать более простую и эффективную программу, если что-то передает дополнительную информацию. Это обязательно расточительно. Это нормально, если вы пишете программы в ситуациях, когда доступно много ресурсов.

C не прилагает дополнительных усилий. Когда передается массив, передается только его местоположение, и это все, что вам нужно для доступа к его элементам. Если конкретной подпрограмме требуется ее длина, вы можете передать ее вручную - это ваш выбор, когда вам это нужно, но у вас также есть возможность не тратить ресурсы, когда они вам не нужны. Вы можете писать более эффективные программы.

в двухмерных массивах, почему я должен указывать количество столбцов массива?

Если мы знаем, что arr массив int, мы знаем, что элемент arr[0] находится в начале, arr[1] сразу после этого, arr[2] после этого и так далее. Чтобы использовать одномерный массив, нам нужно знать только то, где он начинается.

Если мы знаем, array - это двумерный массив int, мы знаем, что a[0][0] находится в начало, arr[0][1] - после этого и так далее, но мы не знаем, где arr[1][0]. Это после некоторого числа элементов arr[0][i], но мы не знаем, сколько, если мы не знаем второе измерение. Следовательно, чтобы использовать двумерный массив, вы должны знать длину второго измерения. Это логическое требование, а не выбор.

Дополнение

Как правило, подпрограмме нужно знать только , какие элементы массива она должна использовать. Не нужно знать, сколько элементов в массиве.

Ситуации, в которых подпрограмме не требуется указывать длину массива, включают:

  • Для вычисления длина строки в буфере, подпрограмма (например, strlen) должна только исследовать каждый байт в буфере, пока не найдет нулевой байт. Не нужно знать, насколько велик весь буфер. (Пример: программа создает буфер из 100 байтов. Она читает байты из терминала, пока не будет найдена новая строка. Пользователь вводит только 12 символов, а затем новую строку. Буфер заполнен 12 байтами и нулевым символом Подпрограмма, проверяющая строку, должна работать только с 13 байтами, а не с 100.)
  • Подпрограмма может работать с фиксированным числом элементов. Например, подпрограмма, помогающая в численном интегрировании, может одновременно принимать три значения функции, подгонять их под кривую и возвращать область под кривой. Основная подпрограмма может иметь целый массив значений функций, и она многократно вызывает подпрограмму для оценки различных точек в массиве, передавая подпрограмме указатель на местоположение для работы. В каждом вызове подпрограмме нужно только знать, что для данного адреса есть три значения. Ему не нужно знать, сколько в полном массиве.
  • Подпрограмма может работать с одинаковым количеством элементов в нескольких массивах. Например, подпрограмма для выполнения дискретного преобразования Фурье может занять несколько элементов N для работы и четыре массива: один для ввода реальных компонентов, один для ввода мнимых компонентов, один для вывода реальных компонентов, и один для вывода мнимых компонентов. Для каждого из массивов процедура использует N элементов. Это число N необходимо передать в процедуру только в одном параметре. Было бы расточительно хранить его в нескольких местах, по одному для каждого массива.

Другое соображение состоит в том, что иногда мы передаем только часть массива в подпрограмму. Если у меня есть какая-то строка в буфере, я бы хотел, чтобы подпрограмма работала только с той частью этой строки, возможно, только с одним словом в проанализированной команде. Для этого я могу передать только указатель на начало этого слова и длину слова для работы. В этом случае подпрограмма не только не должна знать длину массива, она даже не должна знать, где начинается массив. Нужно только знать, над чем его просят работать. Было бы расточительно передавать любую другую информацию.

3 голосов
/ 06 марта 2020

В большинстве языков программирования типы данных: абстракции : то есть, если вы запрашиваете список чисел, он создает структуры в памяти для хранения списка чисел и для отслеживания его чисел. емкость, сколько элементов заполнено и, возможно, являются ли элементы «нулевыми» или содержат значения, и т. д. c.

C - это язык низкого уровня, который не работает с абстракциями; это имеет дело непосредственно с физической памятью. Если вы попросите место для 5 целых чисел, он выделит память для 5 целых чисел. Вы хотели, чтобы он где-то отслеживал число «5», чтобы помнить, что вы выделили 5 целых чисел? Вы не просили об этом - вам придется сделать это самостоятельно.

0 голосов
/ 06 марта 2020

C - это процедурный язык (и ближе к ассемблеру, чем большинство процедурных языков), а не объектно-ориентированный язык. Итак, Алгол (и 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, определите константу symboli c для длины конкретного массива и используйте ее вместо констант жесткого кодирования.

В любом случае языки OO имеют метаданные, связанные с объектами, поэтому почему бы не хранить длину в метаданных? C такого не делает, хотя - он был создан в то время, когда байты были драгоценными, а метаданные воспринимались как слишком большие накладные расходы.

И почему вы должны частично определять размер n размерный массив? Потому что за сценой C выполняет некоторую математику, чтобы умножить, где в памяти существует [x] [y], и опять же, он не хранит метаданные, чтобы помочь вам отслеживать эти измерения.

Учтите, что Pascal, другой процедурный язык, сделал размеры массива частью типа массива . Это была своего рода противоположная крайность - размер и форма отслеживались в системе типов, но на самом деле были довольно драконовскими для использования на практике. Поэтому написание функции для суммирования чисел в двух разных массивах двух разных длин нецелесообразно.

0 голосов
/ 06 марта 2020

В C массив, переданный в качестве параметра функции, преобразуется в указатель на первый элемент массива. Размер массива неявно передается функции. Вы, программист, несете ответственность за передачу правильного размера массива в вашу функцию.

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

Одна из проблем этого подхода заключается в том, что параметр для массива должен указывать только на массив. Он может указывать на любое int, независимо от того, является ли int элементом массива. Если эта ошибка сделана, происходит классическое переполнение буфера.

...