Тепер, коли у нас є 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.