Навіщо оголошувати структуру, яка містить лише масив на C?


131

Я натрапив на якийсь код, що містить таке:

struct ABC {
    unsigned long array[MAX];
} abc;

Коли має сенс використовувати таку декларацію?

Відповіді:


184

Це дозволяє передати масив функції за значенням або повернути його за значенням з функції.

Структури можуть передаватися за значенням, на відміну від масивів, які розпадаються на покажчик у цих контекстах.


1
Остерігайтеся робити це з масивами, більш ніж 16 або 32 байтами, для функцій, які не вбудовані в рядки: ефективніше передавати їх за допомогою const-reference, якщо тільки виклику вже не потрібна копія tmp, яку він може знищити. Якщо виклик / повернення не оптимізуються, середній і великий масив (тисячі байтів) - це жахлива річ, яку можна передати за значенням.
Пітер Кордес

85

Ще одна перевага полягає в тому, що він скорочує розмір, тому вам не доведеться використовувати [MAX]весь код, де б ви не оголошували такий об'єкт. Цього можна досягти і за допомогою

typedef char ABC[MAX];

але тоді у вас є набагато більша проблема: ви повинні знати, що ABCце тип масиву (навіть якщо ви цього не можете бачити, коли ви оголошуєте змінні типу ABC), інакше ви будете вражені тим фактом, що ABCозначатиме щось інше у списку аргументів функції проти декларації / визначення змінної.

Ще одна перевага полягає в тому, що структура дозволяє вам згодом додавати більше елементів, якщо вам потрібно, без необхідності переписувати багато коду.


45

Ви можете скопіювати структуру та повернути структуру з функції.

Ви не можете зробити це з масивом - якщо тільки він не є частиною структури!


26

Ви можете скопіювати це так.

struct ABC a, b;
........
a = b;

Для масиву вам потрібно буде використовувати функцію memcpy або цикл, щоб призначити кожен елемент.


6
(тому він дозволяє отримати чистіший код - він не змінить швидкості тощо)
Джон Картер

3
Це корисно. На жаль, ви не можете цього зробити, якщо (a == b)!?! наскільки це непослідовно. Для C ++ він шукає оператора ==. На мові C написано "недійсні операнди до двійкових ==".
Метт

11

Ви можете використовувати struct для створення нового типу даних, наприклад рядка . Ви можете визначити:

struct String {
    char Char[MAX];
};

або ви можете створити Список даних, який ви можете використовувати їх аргументом функцій або повернути їх у ваші методи. Структура є більш гнучкою, ніж масив, тому що вона може підтримувати деякі оператори типу = і ви можете визначити в ній деякі методи.

Сподіваюся, це вам корисно :)


2
По суті, це найближче, що C має створити клас. Мені подобається ця відповідь, тому що вона найближче до того, щоб її вказати.
Nate CK

1
Немає такого поняття, як метод у C. structs in C - це звичайні старі дані. У нього є оператор =, підтримуваний за замовчуванням (що свідчать інші відповіді, це є причиною цього), але це вводить в оману і в основному стосується C ++, а не C.
Джонатан Штернберг

3
@J Sternberg: "Метод" - це лише спосіб мислення про підпрограми як пов'язані з "об'єктами" даних, на які вони впливають. Ви, звичайно, можете створювати "класи" "об'єктів" та "методів", які працюють над ними в C. Мова просто формально не визначає таких речей. Якщо ви хочете створити кращі абстракції на C, введення речей в структуру зазвичай є найкращим способом зробити це.
Nate CK

Крім того, якщо ви дійсно хотіли "створити" методи на C, ви можете використовувати функціональні покажчики (так, так, хитрий синтаксис, відсутність захисту даних тощо), щоб пов’язати функції з даними, над якими вони працюють. У першому аргументі вам належить передати "Я" (ви навіть можете назвати його "цим", якщо хочете), оскільки автоматичне створення цього вказівника всередині функції не існує. Звичайно, це все гімнастика, ви отримуєте такі речі за замовчуванням в C ++, хоча це правда, там можна було б приховати накладні витрати як бонус ...
BenPen

2

Ще одна перевага використання такого типу structполягає в тому, що він застосовує безпеку типу, де б таке structне використовувалося; особливо якщо у вас є два типи, що складаються з масивів однакового розміру, які використовуються для різних цілей, ці типи допоможуть вам уникнути випадкового використання масиву неналежним чином.

Якщо ви не загортаєте масив у a struct, ви все одно можете оголосити typedefдля нього: це має деякі переваги: struct- тип оголошується один раз, • розмір автоматично правильний, • намір коду стає зрозумілішим, • і код є більш ретельним - але ви втрачаєте ◦ сувору безпеку типу, ◦ можливість копіювати та повертати значення типу та ◦ можливість додавати членів пізніше, не порушуючи решту вашого коду. Два typedefs для оголених масивів даного типу дають різні типи, лише якщо вони різної величини. Крім того, якщо ви використовуєте typedefНЕ *в якості аргументу функції, що еквівалентно char *, різко знижуючи безпеку типів.

Підсумовуючи :

typedef struct A_s_s { char m[113]; } A_s_t; // Full type safey, assignable
typedef char   A_c_t[113];                   // Partial type-safety, not assignable

A_s_t          v_s(void);     // Allowed
A_c_t          v_c(void);     // Forbidden

void           s__v(A_s_t);     // Type-safe, pass by value
void           sP_v(A_s_t *);   // Type-safe
void           c__v(A_c_t);     // UNSAFE, just means char * (GRRR!)
void           cP_v(A_c_t *);   // SEMI-safe, accepts any array of 113

1

Структура може містити функції ініціалізації масиву, копіювання та функції fini, що імітують деякі переваги парадигм управління пам'яттю OOP. Насправді, дуже просто розширити цю концепцію, щоб написати загальну утиліту управління пам’яттю (за допомогою структури sizeof (), щоб точно знати, скільки байтів керується) для управління будь-якою визначеною користувачем структурою. Багато баз інтелектуальних виробничих кодів, написаних на C, використовують їх дуже часто і, як правило, ніколи не використовують масив, якщо його сфера дії не є дуже локальною.

Насправді для масиву, вбудованого в структуру, ви можете робити інші "розумні речі", такі як прив'язка перевірки будь-коли, коли ви хочете отримати доступ до цього масиву. Знову ж таки, якщо область застосування масиву дуже обмежена, погано використовувати її і передавати інформацію по програмах. Рано чи пізно ви зіткнетеся з помилками, які триматимуть вас неспати вночі та зіпсують вихідні.


Це не відповідає на питання, чому можна використовувати structмістить, що містить лише масив.
PJTraill
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.