С указатель математика со структурами

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

typedef struct{ 
  int num;
  int num2;
  char *string;
} astruct ;

int main (int argc, const char * argv[])
{
  astruct mystruct = { 1234, 4567,"aaaaaaa"};

  astruct *address;
  address = &mystruct;

  // this does print 1234
  printf("address 0x%x has value of:%i\n",address, *address); 
  address = address + sizeof(int);

  //this does NOT print 4567
  printf("address 0x%x has value of:%i\n",address, *address); 
  address = address + sizeof(int);

  //this crashes the program, I wanted to print aaaaaaaa
  printf("address 0x%x has value of:%s\n",address, **address); 

  return 0;
}

3 ответа

Решение

Арифметика указателя увеличивает указатель на количество байтов, на которое указывает. Например,

int a[2];
int *p = a;

assert( &a[0] == p )
assert( &a[1] == p + 1 )

Поэтому ваша линия

address = address + sizeof(int);

на самом деле меняется address в address + sizeof(int) * sizeof(astruct) (используя обычную, а не указательную арифметику).

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

struct A {
    char a;
    int b;
};

Компилятор, скорее всего, даст этой структуре следующую компоновку:

+------------+-------------------+-------------+
| a (1 byte) | (3 bytes padding) | b (4 bytes) |
+------------+-------------------+-------------+

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

В вашем конкретном случае компилятор, вероятно, не вводит отступы, поэтому вы можете использовать эти строки для обновления address:

address = (char *) address + sizeof(int); // OR...
address = (int *) address + 1;

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

Когда вы добавляете к указателю, вы фактически добавляете n * sizeof(*ptr) байт.

Следовательно, на 32-битной машине, где ваша структура будет занимать 12 байтов, добавляя sizeof(int) байты фактически добавили бы 48 байтов к указателю.

Указать на num2 сначала вы должны привести адрес к указателю на целое число:

int *iaddress = (int *)address;

и затем добавьте один к этому, чтобы достигнуть своего значения 4567.

Конструкции разработаны таким образом, чтобы избежать таких расчетов. Вместо того, чтобы перебирать память, вы можете использовать mystruct.num, mystruct.num2 а также mystruct.string, И все же это будет ошибка, так как вы не выделяете память для "aaaaaaa".

Другие вопросы по тегам