Теперь, когда у нас есть 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


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.


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 ( ссылка )

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

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


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: Есть большая разница между «не совсем соответствующими» и «отсутствующими функциями, которые составляют сотни страниц в спецификации».
— Щенок
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.