Почему C нужны массивы, если он имеет указатели?

Зависит от того, что Вы хешируете. Если Ваши ключи являются целыми числами, Вам, вероятно, не нужны очень много объектов, прежде чем HashSet будет быстрее. При манипулировании его на строке тогда, это будет медленнее, и зависит от входной строки.

, Конечно, Вы могли сделать на скорую руку сравнительный тест довольно легко?

7
задан Keith Pinson 26 June 2012 в 15:44
поделиться

6 ответов

Массивы быстрее, чем распределение динамической памяти.

Массивы «выделяются» во «время компиляции», тогда как malloc выделяет во время выполнения. Распределение требует времени.

Кроме того, C не требует, чтобы malloc () и его друзья были доступны в автономных реализациях.


Изменить

Пример массива

#define DECK_SIZE 52
int main(void) {
    int deck[DECK_SIZE];
    play(deck, DECK_SIZE);
    return 0;
}

Пример ] malloc ()

int main(void) {
    size_t len = 52;
    int *deck = malloc(len * sizeof *deck);
    if (deck) {
        play(deck, len);
    }
    free(deck);
    return 0;
}

В версии с массивом пространство для массива deck было зарезервировано компилятором при создании программы (но, конечно, память резервируется / используется только тогда, когда программа выполняется), в версии malloc () пространство для массива deck должно запрашиваться при каждом запуске программы.

Массивы не могут изменять размер, malloc 'd память может увеличиваться при необходимости.

Если вам нужно только фиксированное количество элементов, Если вам нужна память, которая может увеличиваться или уменьшаться во время работы программы, используйте malloc () и его друзей.

9
ответ дан 6 December 2019 в 10:51
поделиться

Массивы - хорошее улучшение синтаксиса по сравнению с работой с указателями. При работе с указателями по незнанию можно совершать всевозможные ошибки. Что, если вы переместите слишком много мест в памяти из-за того, что вы используете неправильный размер байта?

0
ответ дан 6 December 2019 в 10:51
поделиться

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

Они существуют, потому что некоторые требования требуют их.

В таком языке, как BASIC, у вас есть определенные команды, которые разрешены, и это известно из-за языковой конструкции. Итак, в чем преимущество использования malloc для создания массивов с последующим заполнением их строками?

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

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

Массив - это сокращенный способ чтобы указать, например, указание на начало malloc.

Но, представьте, что вы пытаетесь выполнить матричную математику, используя манипуляции с указателями, а не vec [x] * vec [y] . Было бы очень сложно найти ошибки.

1
ответ дан 6 December 2019 в 10:51
поделиться

Неплохой вопрос. Фактически, в раннем C не было типов массивов.

Глобальные и статические массивы выделяются во время компиляции (очень быстро). Другие массивы размещаются в стеке во время выполнения (быстро). Выделение памяти с помощью malloc (для использования в массиве или иначе) происходит намного медленнее. Аналогичная вещь наблюдается при освобождении памяти: динамически выделяемая память высвобождается медленнее.

Скорость - не единственная проблема. Типы массивов автоматически освобождаются, когда они выходят за пределы области видимости, поэтому они не могут быть «утечкой» по ошибке. Вам не нужно беспокоиться о том, что случайно что-то освободите дважды и так далее. Они также упрощают обнаружение ошибок инструментами статического анализа.

Вы можете возразить, что существует функция _alloca () , которая позволяет вам выделять память из стека. Да, нет технической причины, по которой нужны массивы вместо _alloca () . Однако я считаю, что массивы удобнее использовать. Кроме того, компилятору проще оптимизировать использование массива, чем указатель с возвращаемым значением _alloca () в нем, поскольку очевидно, каково смещение выделенного в стеке массива от указателя стека, тогда как если _alloca () обрабатывается как вызов функции черного ящика, компилятор не может сообщить это значение заранее.

РЕДАКТИРОВАТЬ, поскольку tsubasa запросил более подробную информацию о том, как происходит это распределение:

На архитектурах x86 регистр ebp обычно относится к кадру стека текущей функции и используется для ссылки на переменные, выделенные стеком. Например, у вас может быть int , расположенный в [ebp - 8] , и массив char , простирающийся от [ebp - 24] до ] [EBP - 9] . И, возможно, больше переменных и массивов в стеке. (Компилятор решает, как использовать кадр стека во время компиляции. Компиляторы C99 позволяют выделять массивы переменного размера в стеке, это просто вопрос выполнения крошечной работы во время выполнения.)

В коде x86 указатель смещения (такие как [ebp - 16] ) могут быть представлены в одной инструкции. Довольно эффективно.

Теперь важным моментом является то, что все переменные и массивы, выделенные стеком в текущем контексте, извлекаются через смещения из единственного регистра . Если вы вызываете malloc, есть (как я уже сказал) некоторые накладные расходы на обработку при поиске некоторой памяти для вас. Но также malloc дает вам новый адрес памяти. Допустим, он хранится в регистре ebx . Вы больше не можете использовать смещение от ebp , потому что вы не можете сказать, какое это смещение будет во время компиляции. Таким образом, вы в основном «тратите» лишний регистр, который вам не понадобился бы, если бы вы использовали вместо него обычный массив. Если вы разместите больше массивов, у вас будет больше «непредсказуемых» значений указателя, которые усугубят эту проблему.

7
ответ дан 6 December 2019 в 10:51
поделиться

См. этот вопрос, где обсуждается упрочнение пространства и C. Иногда динамическое распределение памяти просто плохо Идея, я работал с библиотеками C, которые полностью лишены malloc () и его друзей.

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

1299] Также важно (как указывали другие) понять, что является частью C и что расширяет его до различных унифицированных стандартов (например, POSIX).

1
ответ дан 6 December 2019 в 10:51
поделиться

Объяснение Денниса Ричи об истории C:

Эмбриональный C

NB существовал так кратко, что не было написано полного его описания. Он предоставил типы int и char, их массивы и указатели на них, объявленные в стиле, типичном

 int i, j;
char c, d;
int iarray [10];
int ipointer [];
char carray [10];
char cpointer [];

Семантика массивов осталась точно такой же, как в B и BCPL: объявления iarray и carray создают ячейки, динамически инициализированные со значением, указывающим на первое из последовательности из 10 целых чисел и символов соответственно. В объявлениях для ipointer и cpointer размер опускается, чтобы утверждать, что память не должна выделяться автоматически. Внутри процедур языковая интерпретация указателей была идентична интерпретации переменных массива: объявление указателя создавало ячейку, отличающуюся от объявления массива только тем, что программист должен был назначить референт, вместо того, чтобы позволить компилятору выделить пространство и инициализировать ячейку.

Значения, хранящиеся в ячейках, привязанных к именам массивов и указателей, были машинными адресами, измеренными в байтах, соответствующей области хранения. Следовательно, косвенное обращение через указатель не подразумевает дополнительных затрат времени выполнения для масштабирования указателя от слова до байтового смещения. С другой стороны, машинный код для индексирования массива и арифметики указателя теперь зависел от типа массива или указателя: для вычисления iarray [i] или ipointer + i предполагалось масштабирование слагаемого i по размеру указанного объекта.

Эта семантика представляет собой простой переход от B, и я экспериментировал с ними в течение нескольких месяцев. Проблемы стали очевидны, когда я попытался расширить обозначение типов, особенно добавив структурированные (записи) типы. Казалось, что структуры должны интуитивно отображаться в памяти машины, но в структуре, содержащей массив, не было подходящего места для размещения указателя, содержащего основание массива, или какого-либо удобного способа упорядочить его. инициализирован. Например, int inumber; имя символа [14]; };

Я хотел, чтобы структура не просто характеризовала абстрактный объект, но также описывала набор битов, которые могут быть прочитаны из каталога. Где компилятор мог спрятать указатель на имя, которое требовала семантика? Даже если бы структуры мыслились более абстрактно, а пространство для указателей можно было бы каким-то образом скрыть, как я мог бы справиться с технической проблемой правильной инициализации этих указателей при выделении сложного объекта, возможно, такого, который определял структуры, содержащие массивы, содержащие структуры произвольной глубины?

Решение представляет собой решающий скачок в эволюционной цепочке между нетиповым BCPL и типизированным C. Оно устранило материализацию указателя в памяти и вместо этого вызвало создание указателя, когда имя массива упоминается в выражении. Правило, живущее сегодня » s C заключается в том, что значения типа массива преобразуются, когда они появляются в выражениях, в указатели на первый из объектов, составляющих массив.

Подводя итог своими словами - если бы name выше был просто указателем, любая из этих структур содержала бы дополнительный указатель, разрушая идеальное отображение его на внешний объект (например, запись в каталоге) .

0
ответ дан 6 December 2019 в 10:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: