Как перебрать массив структур

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

// structure
struct MyData {
  int count;
  char name[20];
  float average;
}

Я пробовал итерацию таким образом, но мое приложение вылетает:

struct MyData data[2] = { {3, "name1", 1.0}, {5, "name2", 2.5} };
struct MyData* ptr = data;

while (*ptr != NULL) {
  // print the contents, works ok for 2 elements

  ptr++; // increment the pointer
}

Ответ 1

Как выделяется массив выделенных структур

В вашем случае массив MyData[2] выглядит следующим образом:

| count | name | average | count | name | average |
^ -- your ptr points here 

Это одно непрерывное пространство с размером 3 * sizeof (struct MyData).

Всякий раз, когда вы выполняете операцию ptr++, указатель переместится к следующей структуре массива, что означает, что он учитывает размер одного struct MyData.

| count | name | average | count | name | average |
                         ^ -- after ptr++ your ptr points here

После другого ptr++ ваш указатель укажет на память сразу после вашего массива.

| count | name | average | count | name | average | 
                                                  ^ -- another ptr++ and your ptr points here

Когда вы разыскиваете свой указатель ptr, вы получаете доступ к памяти, которая еще не используется или даже не выделяется. Это поведение undefined и из-за этого сбой приложения.

Как выполнить итерацию?

Есть несколько способов сделать это. Обратите внимание, что не все способы применимы во всех случаях.

Простой для

Очень часто мы просто знаем размер массива. Затем мы можем просто использовать обычный цикл for для итерации содержимого.

int len = 2;
struct MyData data[len] = { {3, "name1", 1.0}, {5, "name2", 2.5} };
struct MyData* ptr = data;
for (int i=0; i<2; i++, ptr++ ) {
   // do your thing with the ptr
   // and note that ptr gets increased inside for
}

Использование sizeof для определения длины массива

struct MyData data[2] = { {3, "name1", 1.0}, {5, "name2", 2.5} };
struct MyData* ptr = data;
struct MyData* endPtr = data + sizeof(data)/sizeof(data[0]);
while ( ptr < endPtr ){
   // do your thing with the ptr
   ptr++;
}

sizeof(data)/sizeof(data[0]) вычисляет количество элементов: получает общий размер массива и делит его на размер одного элемента.

Этот метод имеет свои недостатки. Он не может использоваться, когда массив объявлен как указатель! Например, когда мы передаем массив как параметр функции, он обычно преобразуется в указатель, и мы не можем определить размер массива.

Ответ 2

Если у вас нет контроля над размером массива и вы даже не можете его просить, вы можете попробовать реорганизовать свой код, не используя массив MyData, а массив указателей до MyData. Затем ваш массив должен быть на один слот дольше, чтобы сохранить охрану с NULL значением.

Ваша итерация будет выглядеть так, как вы написали в качестве примера.

// N is the number of MyData instances you have
MyData* vData[N+1];
// vData[i] is filled, and vData[N] = NULL

// ...

MyData* vPtr = vData[0];
while(vPtr) {
    // ...
}

Но это связано с изменением типа вашего массива от MyData[] до MyData*[]. Если вы не можете, вы должны следовать ответам Дариуша.