Теперь, когда у нас есть std :: array, что еще остается для массивов в стиле C?

89

std::arrayзначительно превосходит массивы C. И даже если я хочу взаимодействовать с устаревшим кодом, я могу просто использовать std::array::data(). Есть ли причина, по которой мне когда-либо понадобится массив старой школы?

Р. Мартиньо Фернандес
источник

Ответы:

60

Если я что-то не пропустил (я не слишком внимательно следил за самыми последними изменениями в стандарте), большинство применений массивов стиля C все еще остаются. std::arrayдопускает статическую инициализацию, но по-прежнему не учитывает инициализаторы. И поскольку раньше массивы в стиле C реально использовались только std::arrayдля статически инициализированных таблиц в следующих строках:

MyStruct const table[] =
{
    { something1, otherthing1 },
    //  ...
};

используя обычные beginи endшаблонные функции (принятые в C ++ 11) для их перебора. Не говоря уже о размере, который компилятор определяет по количеству инициализаторов.

EDIT: еще одна вещь, которую я забыл: строковые литералы по-прежнему являются массивами в стиле C; т.е. с типом char[]. Я не думаю, что кто-то исключил бы использование строковых литералов только потому, что у нас это есть std::array.

Джеймс Канце
источник
7
Вы можете написать шаблон вариативной функции, который создает массивы без необходимости указывать длину.
правый фолд
2
В C ++ 17 Class Template Deduction поддерживается автоматическое удержание количества инициализаторов. Например, «auto a = std :: array {1, 2, 3};»
Ricky65
Nitpick: тип строковых литераловconst char[]
Bulletmagnet
31

Нет. Говоря прямо. И в 30 символах.

Конечно, вам нужны массивы C для реализации std::array, но на самом деле это не причина, по которой пользователю когда-либо понадобятся массивы C. Кроме того, нет, std::arrayон не менее производительный, чем массив C, и имеет возможность доступа с проверкой границ. И, наконец, для любой программы на C ++ вполне разумно зависеть от стандартной библиотеки - в этом смысле она является стандартной - и если у вас нет доступа к стандартной библиотеке, то ваш компилятор не соответствует требованиям и вопрос помечен как «C ++», а не «C ++ и те вещи, которые не относятся к C ++, которые пропускают половину спецификации, потому что считают это неуместным».

Щенок
источник
1
Хм. Что делать, если вы пишете код на C ++, который вызывается с другого языка, и вам нужно передать что-то в качестве параметра?
asveikau
3
Автономным реализациям разрешено исключать почти всю стандартную библиотеку и при этом оставаться совместимыми. Однако у меня были бы серьезные сомнения относительно компилятора, который нельзя было бы реализовать std::arrayв автономной реализации C ++ 11.
Деннис Зикефуз
11
C ++ 0x Final Draft (Document N3092) § 1.4.7 "Определены два типа реализаций: размещенная и автономная. Для размещенной реализации этот международный стандарт определяет набор доступных библиотек. Автономная реализация - это такая реализация, в которой выполнение может происходят без использования операционной системы и имеют набор библиотек, определяемый реализацией, который включает в себя определенные библиотеки поддержки языка ".. STL не входит в качестве библиотеки" поддержки языка "в автономный компилятор
Эрлз,
24

Похоже, что с массивами C использовать многомерные массивы проще, чем с std::array. Например,

char c_arr[5][6][7];

в отличие от

std::array<std::array<std::array<char, 7>, 6>, 5> cpp_arr;

Также из-за свойства автоматического распада массивов C c_arr[i]в приведенном выше примере будет распадаться на указатель, и вам просто нужно передать оставшиеся измерения как еще два параметра. Я хочу сказать c_arr, что копирование обходится недорого. Однако cpp_arr[i]копирование будет очень затратным.

Сумант
источник
1
Однако вы можете передать многомерное arrayфункции без потери размеров. И если вы передадите его в шаблон функции, то эта функция сможет вывести как размер, так и размер каждого измерения или только одно из них. Это может быть интересно для научных библиотек шаблонов, которые в основном работают с произвольными измерениями.
Себастьян Мах
30
простой template <typename T, int M, int N> using array2d = std::array<std::array<T, N>, M>;должен решить любую из этих проблем.
Miles Rout
6
Ваш пример c_arrявляется очень дорогим для копирования! Для этого вы должны предоставить код самостоятельно. Указатель, на который он будет распадаться, является более близким эквивалентом ссылки, чем копия, и вы можете использовать его std::arrayдля передачи ссылки, если это то, что вы хотите.
ChetS
1
@MilesRout технически не должно быть std::size_tвместо этого int? извините за придирки, но это сделало бы его универсальным.
robbie
1
@ robbie0630 Да, вы можете сделать это, size_tесли хотите, хотя я не могу представить, что существует множество сценариев, в которых необходимы массивы с более чем 4 миллиардами строк или столбцов.
Miles Rout,
14

Как сказал Сумант, многомерные массивы намного проще использовать со встроенными C-массивами, чем с std::array.

При вложении std::arrayможет стать очень трудным для чтения и излишне многословным.

Например:

std::array<std::array<int, 3>, 3> arr1; 

по сравнению с

char c_arr[3][3]; 

Также обратите внимание на это begin(), end()и size()все они возвращают бессмысленные значения при вложении std::array.

По этим причинам я создал свои собственные контейнеры многомерных массивов фиксированного размера array_2dи array_3d. Они аналогичны, std::arrayно для многомерных массивов из 2 и 3 измерений. Они более безопасны и имеют не худшую производительность, чем встроенные многомерные массивы. Я не включил контейнер для многомерных массивов с размерами больше 3, поскольку они необычны. В C ++ 0x можно создать вариативную версию шаблона, которая поддерживает произвольное количество измерений.

Пример двухмерного варианта:

//Create an array 3 x 5 (Notice the extra pair of braces) 

fsma::array_2d <double, 3, 5> my2darr = {{
    { 32.19, 47.29, 31.99, 19.11, 11.19},
    { 11.29, 22.49, 33.47, 17.29, 5.01 },
    { 41.97, 22.09, 9.76, 22.55, 6.22 }
}};

Полная документация доступна здесь:

http://fsma.googlecode.com/files/fsma.html

Вы можете скачать библиотеку здесь:

http://fsma.googlecode.com/files/fsma.zip

Рики65
источник
4
Массивы фиксированного размера в стиле C просты, но если вы хотите изменить размеры, все усложняется. Например, arr[x][y]вы не можете сказать, arrявляется ли это массивом массивов, массивом указателей, указателем на массив или указателем на указатель; все для реализации законны, в зависимости от ваших потребностей. И, вероятно, в большинстве реальных случаев использования многомерных массивов размер необходимо определять во время выполнения.
Кейт Томпсон
Я хотел бы увидеть эту реализацию вариативного шаблона n-мерных массивов! Мета-программирование в лучшем виде!
Steffen
3
@steffen Я сделал попытку несколько лет назад. Вы можете просмотреть его здесь: code.google.com/p/fsma/source/browse/trunk/… . Тестовый пример здесь: code.google.com/p/fsma/source/browse/trunk/… . Я уверен, что это можно сделать намного лучше.
Ricky65 02
5

Массивы в стиле C, доступные в C ++, на самом деле гораздо менее универсальны, чем настоящие массивы C. Разница в том, что в C типы массивов могут иметь размеры во время выполнения . Ниже приведен допустимый код C, но он не может быть выражен ни с помощью массивов C-стиля C ++, ни с помощью array<>типов C ++ :

void foo(int bar) {
    double tempArray[bar];
    //Do something with the bar elements in tempArray.
}

В C ++ вам нужно будет разместить временный массив в куче:

void foo(int bar) {
    double* tempArray = new double[bar];
    //Do something with the bar elements behind tempArray.
    delete[] tempArray;
}

Этого нельзя достичь с помощью std::array<>, потому что barэто неизвестно во время компиляции, для этого требуется использование массивов в стиле C в C ++ или std::vector<>.


В то время как первый пример относительно легко может быть выражен на C ++ (хотя и требует new[]и delete[]), следующее не может быть достигнуто на C ++ без std::vector<>:

void smoothImage(int width, int height, int (*pixels)[width]) {
    int (*copy)[width] = malloc(height*sizeof(*copy));
    memcpy(copy, pixels, height*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y][x] = //compute smoothed value based on data around copy[y][x]
        }
    }
    free(copy);
}

Дело в том, что указатели на линейные массивы int (*)[width]не могут использовать ширину времени выполнения в C ++, что делает любой код манипулирования изображениями намного более сложным в C ++, чем в C. Типичная реализация примера манипулирования изображениями C ++ будет выглядеть следующим образом:

void smoothImage(int width, int height, int* pixels) {
    int* copy = new int[height*width];
    memcpy(copy, pixels, height*width*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y*width + x] = //compute smoothed value based on data around copy[y*width + x]
        }
    }
    delete[] copy;
}

Этот код выполняет в точности те же вычисления, что и приведенный выше код на C, но он должен выполнять вычисление индекса вручную, где бы они ни использовались . В случае 2D это все еще возможно (хотя и дает много возможностей ошибиться при вычислении индекса). Однако в случае с 3D это становится действительно неприятно.

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

cmaster - восстановить монику
источник
7
Следует отметить, что по крайней мере Clang и GCC поддерживают VLA в C ++.
Janus Troelsen
@JanusTroelsen, а также то, что они ужасно ограничены в том, какие типы элементов они поддерживают.
правый фолд
Разве C11 не делает VLA необязательным? Если так, то я думаю, что ваш ответ вводит в заблуждение. Было бы правильно, если бы C99 был стандартом, а не C11.
Z-бозон
1
@Zboson C99 - это стандарт C, и есть компиляторы, которые реализуют его функции VLA ( gccнапример). C11 сделал довольно много интересных вещей необязательными, и я не думаю, что это потому, что они хотят объявить эту функцию вне закона. Я склонен рассматривать это как знак того, что они хотели снизить уровень написания полностью совместимого со стандартами компилятора: VLA довольно сложно реализовать, и большая часть кода может обойтись без него, поэтому имеет смысл для нового компилятора на некоторых новых платформу, чтобы не нужно было сразу внедрять VLA.
cmaster - восстановить Монику
-1

Может быть, std::arrayне медленный. Но я провел несколько тестов, используя простое сохранение и чтение из массива std :: array; См. Результаты теста ниже (на W8.1, обновление 4 VS2013):

ARR_SIZE: 100 * 1000
Avrg = Tick / ARR_SIZE;

test_arr_without_init
==>VMem: 5.15Mb
==>PMem: 8.94Mb
==>Tick: 3132
==>Avrg: 0.03132
test_arr_with_init_array_at
==>VMem: 5.16Mb
==>PMem: 8.98Mb
==>Tick: 925
==>Avrg: 0.00925
test_arr_with_array_at
==>VMem: 5.16Mb
==>PMem: 8.97Mb
==>Tick: 769
==>Avrg: 0.00769
test_c_arr_without_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 358
==>Avrg: 0.00358
test_c_arr_with_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 305
==>Avrg: 0.00305

Судя по отрицательным отметкам, код, который я использовал, находится в pastebin ( ссылка )

Код тестового класса здесь ;

Я не очень разбираюсь в тестах ... Мой код может быть некорректным

K'Prime
источник
6
Результаты тестов без тестового кода или флагов компиляции? Давай, ты сможешь лучше.
Р. Мартиньо Фернандес
FWIW, этот небольшой фрагмент кода уже показывает, что тест сильно ошибочен. Достаточно умный компилятор просто превратит все это вlong test_arr_without_init() { return ARR_SIZE; }
Р. Мартинью Фернандес
Это был просто пример. Я думал, что в этом нет ничего страшного. Я изменил код, чтобы вернуть void, использовал сборку выпуска в VS 2013 с / O2 / Ot / Gl.
K'Prime
Удаление возвращаемого значения означает, что компилятор может превратить все это в void test_arr_without_init() {}настоящее. Вам действительно нужно перепрыгнуть через обручи, чтобы убедиться, что код, который вы измеряете, является кодом, который вы хотите измерить.
Р. Мартиньо Фернандес
-5
  1. реализовать что-то вроде std::array
  2. если вы не хотите использовать STL или не можете
  3. Для производительности
Лу Франко
источник
27
Подскажите, как std::arrayбудет менее производительно, чем массив C.
Xeo
2
Из википедии : «Реализация массива не требует проверки привязки. Однако реализация в boost делает это для operator [], но не для итераторов». - поэтому оператор [] работает медленнее. Я не рассматривал реализации, но любой код в реализации может помешать оптимизатору.
Лу Франко
19
@Aaron McDaid: Это только в at(), это не так operator[], просто как std::vector. Нет никакого снижения производительности или раздувания кода std::array, компилятор предназначен для оптимизации такого рода вещей. И, конечно же, добавление проверенной функции - отличный инструмент для отладки и большое преимущество. @Lou Franco: Весь код C ++ может зависеть от стандартной библиотеки - вот для чего она нужна. @Earlz: Если у вас нет STL, значит, это не C ++, и на этом все.
Щенок
6
@Earlz: Стандарт C ++ содержит стандартную библиотеку. Если вы не можете использовать библиотеку, она не соответствует требованиям. А во-вторых, у вас должен быть чертовски дерьмовый компилятор, чтобы использовать std::arrayего больше, чем эквивалентное использование массива C.
Puppy
5
@Earlz: Есть большая разница между «не совсем соответствующими» и «отсутствующими функциями, которые составляют сотни страниц в спецификации».
Щенок