Указатель массива структуры


Хорошо, у меня есть такой код:

#include <stdio.h>
#include <stdlib.h>

#define ARR_SIZE 5

struct mys
{
    double first;
    unsigned long second;
    char str[10];
};

int main(int argc, char** argv)
{
    size_t i = 0;
    struct mys thes[ARR_SIZE] = 
        {
            {1.1, 1, "First"},
            {2.2, 2, "Second"},
            {3.3, 3, "Third"},
            {4.4, 4, "Fourth"},
            {5.5, 5, "Fifth"}
        };//load array of structures with values

    for (; i < ARR_SIZE; ++i)
        fprintf(stdout, "second->%lun", thes[i].second);//loop through array thes and print element second

    return (EXIT_SUCCESS);
}

Теперь я хочу получить адрес элемента, называемого second нулевого элемента thes, а затем использовать его для циклического перебора массива thes и вывода каждого второго элемента.

#include <stdio.h>
#include <stdlib.h>

#define ARR_SIZE 5

struct mys
{
    double first;
    unsigned long second;
    char str[10];
};

int main(int argc, char** argv)
{
    size_t i = 0;
    unsigned long * ptr = NULL;//pointer to unsigned long
    struct mys thes[ARR_SIZE] = 
        {
            {1.1, 1, "First"},
            {2.2, 2, "Second"},
            {3.3, 3, "Third"},
            {4.4, 4, "Fourth"},
            {5.5, 5, "Fifth"}
        };

    //first loop
    for (; i < ARR_SIZE; ++i)
        fprintf(stdout, "second->%lun", thes[i].second);

    ptr = &thes[0].second;//get the address of the second element of the zero'th array structure and store it in ptr

    // Now I want to use the above pointer ptr to loop through the array thes and display the second element like I did above, but I can't manage to do that.
    //The output of this loop should be the same as the first loop 

    return (EXIT_SUCCESS);
}

Итак, я реализовал указатель, но у меня возникли проблемы с написанием кода для второго цикла. Любая помощь ценится.

6 2

6 ответов:

for (; i < ARR_SIZE; ++i)
    fprintf(stdout, "second->%lu\n", *( unsigned long * )(( char * )ptr + i * sizeof( struct mys ) ));

Вы не можете (или, что более важно, не должны) этого делать. то, что вы должны сделать, - это сохранить указатель на первый элемент массива, чтобы вы могли его повторить.

struct mys* ptr;

ptr = &thes[0];

for(int i=0; i<10; i++, ptr++)
  fprintf(stdout, "second->%lu\n", ptr->second);

Но вы должны быть осторожны, так как нет конечного элемента, и iteratig [0-10) является правильным только в случае начала с ptr = &thes[0];.

Ваши требования не имеют большого смысла: все, что у вас есть, - это указатель на unsigned long, не осталось никакой информации, которая идентифицирует рассматриваемое число как часть некоторого конкретного массива структур.

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

Зачем вам это нужно?

Я бы сделал это, вычисляя указатель назад к структуре, чтобы сделать итерацию уборщик:

const struct mys *mysp = (struct mys *) ((char *) ptr -
                                         offsetof(struct mys, second));

Это использует offsetof "резервное копирование" от указателя на элемент структуры к самой структуре.

Вы, конечно, можете это сделать. Не приятно, не полезно,но можно сделать, манипулируя указателем.

Чтобы понять, посмотрите на расположение вашего массива структур в памяти, скажем, Ваш массив начинается с адреса 0x0000:

0x0000 : array[0].first  - first part of mys.first
0x0004 : array[0].first  - second part of mys.first
0x0008 : array[0].second
0x000c : array[0].str[0-3] - first four elements of your string
0x0010 : array[0].str[4-7] - next four elements
0x0014 : array[0].str[8-9] - last two elements and padding
0x0018 : array[1].first  - second element !

...

Таким образом, вы можете видеть, что между одним и тем же полем двух последовательных элементов массива у вас есть размер структуры.

Чтобы перебрать одно и то же поле из нескольких последовательных структур в памяти, вам просто нужно объявите указатель, указывающий на элемент первой структуры, и увеличьте его на размер вашей структуры, чтобы получить доступ к тому же полю следующего элемента массива.

В вашем случае это будет выглядеть следующим образом:

int i;
struct mys my_array[SIZE];
char* my_iterator = &my_array[0].second;
for(i=0; i<SIZE; i++) {
    fprintf(stdout, "second->%lu\n", *(unsigned long*)my_iterator);
    my_iterator += sizeof(mys);
}

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

int i;
struct mys my_array[SIZE];
strut mys* my_iterator = &my_array[0].second;
for(i=0; i<SIZE; i++, my_iterator++) {
    fprintf(stdout, "second->%lu\n", *(unsigned long*)my_iterator);
}

Это небольшое объяснение решения Влада. Это записано на c++ (переменные, объявленные в цикле for):

Мы привели наш ptr к unsigned char* (байт ptr), а затем увеличили его на число байт, которое занимает mys, чтобы установить его на следующий элемент second. Затем мы приводим его обратно к ulong и разыменовываем указатель, чтобы получить его значение.

ptr = &thes[0].second; //get the address of the second element of the zero'th array structure and store it in ptr
for (i = 0; i < ARR_SIZE; ++i)
{
    unsigned char* pointer_as_byte = (unsigned char*)ptr; // get ptr as byte ptr to increment it
    unsigned char* pointer_offset = pointer_as_byte += (i * sizeof(struct mys)); // next structure's [second] field
    unsigned long* final_pointer = (unsigned long*)pointer_offset; // cast byte ptr back to ulong ptr

    fprintf(stdout, "second->%lu\n", *final_pointer);
}

Или наоборот, может быть, проще понять:

ptr = &thes[0].second;//get the address of the second element of the zero'th array structure and store it in ptr
for (i = 0; i < ARR_SIZE; ++i)
{
    mys* pointer_as_mys = (mys*)ptr; // get ptr as mys pointer
    pointer_as_mys += i; // increment is to i'th mys.second
    ptr = (unsigned long*)pointer_as_mys; // cast it back to save the offset to ptr

    fprintf(stdout, "second->%lu\n", *ptr);
}

Вы не можете этого сделать. По крайней мере, не просто так. Ваш массив состоит из структур. И они сидят последовательно в памяти. Приращение указателя ptr без каких-либо приведений переместит его на следующий адрес long в блоке памяти, который находится не в следующей структуре, а прямо рядом с ней.