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


88

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 підтримується автоматичне вирахування кількості ініціалізаторів. Наприклад, "auto a = std :: array {1, 2, 3};"
— Ricky65,

Nitpick: тип рядкових літералівconst char[]
— Bulletmagnet

30

Ні, щоб сказати прямо. І в 30 символів.

Звичайно, для реалізації потрібні масиви C std::array, але насправді це не причина, через яку користувач коли-небудь захоче масиви C. Крім того, ні, std::arrayне менш продуктивний, ніж масив С, і має опцію для доступу, перевіреного межами. І, нарешті, цілком розумно, щоб будь-яка програма C ++ залежала від стандартної бібліотеки - це свого роду сенс - це стандарт, і якщо у вас немає доступу до стандартної бібліотеки, то ваш компілятор не відповідає стандартам запитання позначено тегом "C ++", а не "C ++ та ті речі, що не належать до C ++, які пропускають половину специфікації, оскільки вважали це недоречним."


1
Хм Що робити, якщо ви пишете код C ++, який викликається з іншої мови і потрібно щось передавати як параметр?
— асвейкау

3
Окремо стоячі реалізації дозволяють залишати майже всю стандартну бібліотеку і при цьому залишатися сумісними. У мене були б серйозні сумніви щодо компілятора, який не зміг би реалізувати std::arrayв окремо стоячій реалізації C ++ 11.
— Денніс Зікфус,

11
C ++ 0x Final Draft (Документ N3092) § 1.4.7 "Визначено два типи реалізацій: розміщені та автономні. Для розміщеної реалізації цей Міжнародний стандарт визначає набір доступних бібліотек. Окремостояча реалізація - це така, при якій виконання може проходить без переваг операційної системи і має визначений реалізацією набір бібліотек, який включає певні бібліотеки, що підтримують мову ".. STL не включений як бібліотека" підтримки мови "в окремо стоїть компілятор
— Earlz,

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функцію без втрати розмірів. І якщо ви передасте його шаблону функції, тоді ця функція може вивести як розмірність, так і розмір кожного виміру, або лише один із них два. Це може бути цікаво для наукових бібліотек шаблонів, які в основному працюють за довільними розмірами.
— Себастьян Мах

29
простий 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? вибачте за нікчемність, але це зробило б це універсальним.
— Роббі

1
@ robbie0630 Так, ви можете зробити це, size_tякщо хочете, хоча я не уявляю, що існує багато сценаріїв, коли необхідні масиви з понад 4 мільярдами рядків або стовпців.
— Miles Rout

13

Як сказав Сумант, багатовимірні масиви набагато простіше використовувати з вбудованими 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
Масиви фіксованого розміру в стилі С легко, але якщо ви хочете змінити розміри, все ускладнюється. Наприклад, з огляду на це 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,

5

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

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 ++.
— Янус Трольсен

@JanusTroelsen, а також те, що вони жахливо обмежені в тому, які типи елементів вони підтримують.
— праворуч

Чи не робить C11 VLA додатковим? Якщо так, то я думаю, що ваша відповідь вводить в оману. Було б правильно, коли C99 був стандартним, але не C11.
— Z бозон

1
@Zboson C99 - це стандарт C, і є компілятори, які реалізують його функції VLA ( gccнаприклад). C11 зробив чимало цікавих речей необов'язковим, і я не думаю, що це тому, що вони хочуть цю функцію заборонити. Я схильний сприймати це як ознаку того, що вони хотіли знизити рівень написання повністю стандартного сумісного компілятора: VLA досить складно реалізувати, і без багатьох кодів можна обійтися, тому має сенс для нового компілятора на якомусь новому платформу, щоб не потрібно було впроваджувати VLA відразу.
— cmaster - відновити моніку

-1

Може бути, std::arrayце не повільно. Але я зробив порівняльний аналіз, використовуючи простий магазин і читав з std :: array; Перегляньте наведені нижче результати тестування (для W8.1, VS2013, оновлення 4):

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

Відповідно до негативних оцінок, код, який я використовував, знаходиться в пастебіні ( посилання )

Код базового класу знаходиться тут ;

Я не дуже багато знаю про бенчмаркінги ... Мій код може бути хибним


6
Бенчмарк результати без коду тесту або прапорців компіляції? Давай, ти можеш зробити краще.
— Р. Мартіньо Фернандес

FWIW, лише той невеликий фрагмент коду вже показує, що еталон є серйозним недоліком. Досить розумний компілятор просто перетворить все цеlong test_arr_without_init() { return ARR_SIZE; }
— Р. Мартіньо Фернандес

Це був лише приклад. Я думав, що це не велика справа. Я змінив код, щоб повернути void, використовував збірку випусків у VS 2013, з / O2 / Ot / Gl.
— K'Prime

Видалення поверненого значення означає, що компілятор може перетворити все це void test_arr_without_init() {}зараз. Вам дійсно потрібно перестрибнути обручі, щоб переконатися, що код, який ви вимірюєте, є кодом, який ви хочете виміряти.
— Р. Мартіньо Фернандес

-6
  1. реалізувати щось на зразок std::array
  2. якщо ви не хочете використовувати STL або не можете
  3. Для продуктивності

27
Скажіть, як std::arrayце буде менш продуктивним, ніж масив C.
— Xeo

2
З wikipedia : "Реалізація масиву не вимагає виконання прив'язаної перевірки. Однак реалізація в boost робить це для оператора [], але не для ітераторів." - отже оператор [] повільніший. Я не розглядав реалізації, але будь-який код у реалізації може завадити оптимізатору.
— Лу Франко,

19
@ Аарон Макдейд: Це тільки в at(), це не в operator[], як std::vector. Немає зниження продуктивності або роздуття коду std::array, компілятор призначений для оптимізації такого роду речей. І, звичайно, додавання перевіреної функції є чудовим інструментом налагодження та великою перевагою. @Lou Franco: Весь код на C ++ може залежати від стандартної бібліотеки - для цього він і потрібен. @Earlz: Якщо у вас немає доступного STL, то це не C ++, і на цьому все закінчилося.
— Щеня

6
@Earlz: Стандарт C ++ містить стандартну бібліотеку. Якщо ви не можете користуватися бібліотекою, це не відповідає. А по-друге, у вас повинен бути неймовірно дерьмовий компілятор, щоб використання std::arrayбуло більше, ніж еквівалент використання масиву C.
— Щеня

5
@Earlz: Існує велика різниця між "не зовсім відповідними" та "відсутніми функціями, що складають сотні сторінок у специфікації".
— Щеня
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.