Подтвердить что ты не робот

Передача массива структур для работы С++

Извините за вопрос о нобе, я просто немного смущен. Если у меня есть массив структур в основном, что я хочу перейти к функции:

struct MyStruct{
    int a;
    int b;
    char c;
    mayarray[5];
};  
MyStruct StructArray[10]; 

myFunction(StructArray[])

Перейдите к этой функции:

void myFunction(struct MyStruct PassedStruct[])
{
    PassedStruct[0].a = 1;
    PassedStruct[0].b = 2;
    // ... etc
}  

Мой вопрос в том, будет ли функция, подобная этой, изменять данные в StructArray? Мне это нужно. Будет ли это вызвано ссылкой? Я немного запутался. Как я могу изменить его так, что, когда я передам массив структур функции, функция изменит массив StructArray? Я использую визуальную студию btw.
Спасибо.

4b9b3361

Ответ 1

struct MyStruct PassedStruct[]

является главным образом альтернативным синтаксисом для:

struct MyStruct * PassedStruct

Итак, вы получите доступ к исходной структуре и измените ее.

Только одна деталь для изменения, правильный вызов функции не

myFunction(StructArray[]);

а

myFunction(StructArray);

Теперь я попытаюсь объяснить, почему я использовал слово в основном в приведенном выше предложении:

Я дам некоторый намек на разницу между массивами и указателями, почему их не следует путать (даже если бы я не сказал, что они не связаны друг с другом, совсем наоборот) и проблема с передачей параметра MyStruct PassedStruct[] синтаксис.

Это не для новичков, и стандартные эксперты С++ также должны избегать этого чтения (потому что я не хочу вступать в какую-либо войну ISO Standard_, поскольку я вхожу в зону поведения ISO undefined - aka запрещенная территория).

Начнем с массивов:

Представьте себе простую структуру:

struct MyStruct{
    int a;
    int b;
    char c;
};  

MyStruct a1[3]; - это объявление массива, элементы которого имеют вышеуказанный тип структуры. Самое главное, что компилятор делает при определении массива, - это выделить для него пространство. В нашем примере это зарезервированное пространство для 3-х структур. Это зарезервированное пространство может быть в стеке или из глобальных ресурсов памяти, в зависимости от того, где заключен оператор объявления.

Вы также можете инициализировать структуру, объявив ее как:

struct MyStruct a1[3] = {{1, 2}, {3, 4}, {5, 6}};

Обратите внимание, что в этом примере я не инициализировал поле c, а просто a и b. Это разрешено. Я мог бы также использовать синтаксис обозначения, если мой компилятор поддерживает его, как в:

struct MyStruct a1[3] = {{a:1, b:2}, {a:3, b:4}, {a:5, b:6}};

Теперь существует еще один синтаксис для определения массива с использованием пустых квадратных опор, например:

struct MyStruct a2[] = {{1, 2}, {3, 4}, {5, 6}};

Дело здесь в том, что a2 - совершенно нормальный массив, как и a1. Размер массива не является неявным, он предоставляется через инициализатор: у меня есть три инициализатора, поэтому я получаю массив из трех структур.

Я могу определить неинициализированный массив известного размера с помощью этого синтаксиса. Для неинициализированного массива размером 3 я бы:

struct MyStruct a2[] = {{},{},{}};

Пространство выделяется точно так же, как и в предыдущем синтаксисе, здесь нет указателя.

Введем один указатель:

MyStruct * p1;

Это простой указатель на структуру типа MyStruct. Я могу получить доступ к полям с помощью обычного синтаксиса указателя p1->a или (*p1).a. Существует также синтаксис с ароматизированным массивом, чтобы сделать то же самое, что и выше p1[0].a. Все те же, что и выше. Вы просто должны помнить, что p1 [0] является сокращением для (*(p1+0)).

Также помните правило для арифметики указателя: добавление 1 к указателю означает добавление sizeof заостренного объекта к базовому адресу памяти (что вы получаете, когда используете параметр формата% p printf). Арифметика указателя позволяет получить доступ к последовательным идентичным структурам. Это означает, что вы можете получить доступ к структурам по индексу с помощью p1[0], p1[2] и т.д.

Границы не проверяются. В памяти указывается ответственность программиста. Да, я знаю, что ISO говорит по-другому, но это то, что делали все компиляторы, которые я когда-либо пробовал, поэтому, если вы знаете того, кто этого не делает, скажите, пожалуйста.

Чтобы сделать что-нибудь полезное с p1, вы должны указать его на некоторую структуру типа MyStruct. Если у вас есть массив таких структур, доступных как наш a1, вы можете просто сделать p1=a1, а p1 укажет на начало массива. Другими словами, вы также могли бы сделать p1=&a1[0]. Естественно иметь простой синтаксис, поскольку именно он предназначен для арифметики указателей: для доступа к массивам похожих объектов.

Красота этого соглашения заключается в том, что он позволяет полностью унифицировать синтаксис доступа к указателю и массиву. Разница наблюдается только компилятором:

  • когда он видит p1[0], он знает, что он должен получить содержимое переменной с именем p1 и что он будет содержать адрес некоторой структуры памяти.

  • когда он видит a1[0], он знает, что a1 - это некоторая константа, которая должна пониматься как адрес (а не что-то для извлечения в память).

Но как только адрес из p1 или a1 доступен, лечение идентично.

Общей ошибкой является запись p1 = &a1. Если вы это сделаете, компилятор даст вам несколько буквенных слов. Ok, &a1 также является указателем, но то, что вы получаете при принятии адреса a1, является указателем на весь массив. Это означает, что если вы добавите 1 к указателю этого типа, фактический адрес будет перемещаться по этапам из трех структур одновременно.

Фактический тип указателя такого типа (пусть его называют p2) будет MyStruct (*p2)[3];. Теперь вы можете написать p2 = &a1. Если вы хотите получить доступ к первой структуре MyStruct в начале блока памяти, на которую указывает p2, вам придется писать такие вещи, как p2[0][0].a или (*p2)[0].a или (*(*p2)).a или (*p2)->a или p2[0]->a,

Благодаря системе типа и арифметике указателя все они делают точно то же самое: выбор адреса, содержащегося в p2, используйте этот адрес как массив (известный постоянный адрес), как описано выше.

Теперь вы можете понять, почему указатели и массивы являются совершенно разными типами, которые не следует путать, как могут сказать некоторые. В простых словах указатели являются переменными, содержащими адрес, массивы - это постоянные адреса. Пожалуйста, не стреляйте в меня гуру С++, да, я знаю, что это не полная история, и что компиляторы содержат много другой информации, например, адрес, размер объекта point (адресуемого?).

Теперь вы можете удивиться, почему в контексте передачи параметров вы можете использовать пустые квадратные скобки, и это действительно означает указатель.? Без понятия. Кто-то, вероятно, думал, что это выглядит хорошо.

Кстати, по крайней мере с gcc, вы также можете поместить некоторое значение между скобками вместо того, чтобы держать их пустыми. Это не повлияет, вы все равно получите указатель, а не массив, а границы или проверка типов не будут выполнены. Я не проверял в стандарте ISO, должен быть сделан, и если это требуется стандартом или если это конкретное поведение.

Если вам нужна проверка типов для границ, просто используйте ссылку. Это может быть удивительно, но это область, где, если вы используете ссылку, фактический тип параметра изменяется от указателя на массив (а не от указателя к ссылке на указатель, как можно ожидать).

MyStruct StructArray[10]; 
  • header: void myFunction(struct MyStruct * PassedStruct)
  • вызывающий абонент: myFunction(StructArray)
  • статус: работает, вы работаете с указателем в PassedStruct
  • заголовок: void myFunction(struct MyStruct PassedStruct[])
  • вызывающий абонент: myFunction(StructArray)
  • статус: работает, вы работаете с указателем в PassedStruct
  • header: void myFunction(struct MyStruct (& PassedStruct)[10])
  • вызывающий абонент: myFunction(StructArray)
  • статус: работает, вы работаете со ссылкой на массив размером 10
  • header: void myFunction(struct MyStruct (& PassedStruct)[11])
  • вызывающий абонент: myFunction(StructArray)
  • статус: не компилируется, тип несоответствия массива между прототипом и фактическим параметром
  • header: void myFunction(struct MyStruct PassedStruct[10])
  • вызывающий абонент: myFunction(StructArray)
  • status: works, PassedStruct - указатель, размер которого игнорируется.
  • header: void myFunction(struct MyStruct PassedStruct[11])
  • вызывающий абонент: myFunction(StructArray)
  • status: works, PassedStruct - указатель, размер которого игнорируется.

Ответ 2

Несмотря на то, что массивы и указатели концептуально отличаются друг от друга, воды сильно загрязнены функциональными параметрами. Вы не можете напрямую передать массив функции; вы можете передать указатель. В результате язык "полезно" преобразует прототип, например:

void foo (char arr[])

в это:

void foo (char *arr)

И когда вы вызываете функцию, вы не передаете ей полный массив, вы передаете указатель на первый элемент. В результате внутри функции foo он будет иметь указатель на исходный массив, а присвоение элементам arr также изменит массив в вызывающем.

Во всех других ситуациях вне параметров функции синтаксис массива и указателя в объявлениях не эквивалентны и относятся к концептуально различным вещам. Но внутри списка параметров функции, синтаксис массива создает типы указателей.

Ответ 3

Я считаю, что x [] означает то же, что x *, что означает "указатель на тип". Так как это так, вы будете модифицировать передаваемый вами объект (вам нужно будет вызвать его с помощью оператора "amp;, или" address of "), и вы можете считать его ссылкой.

Ответ 4

Когда вы передаете массив как аргумент функции, массив распадается на указатель на первый элемент массива.

Итак, когда внутри вашей функции вы используете [] для доступа к элементам массива, вы действительно выполняете только арифметику указателя с вашим начальным указателем, чтобы получить элементы массива ORIGINAL.

Итак, да, вы изменяете исходный массив. И этот ответ в значительной степени не зависит от того, какой компилятор вы используете (хотя это хорошая практика, IMHO, указать компилятор в вопросе, как вы это сделали)

Ответ 5

Вместо этого вы можете использовать std::vector. Массивы - это очень много C-конструкций, С++ имеет классы-оболочки, чтобы предотвратить именно такие неоднозначности