Указатель массива структуры
Хорошо, у меня есть такой код:
#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 ответов:
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
в блоке памяти, который находится не в следующей структуре, а прямо рядом с ней.