Как выполнить арифметику указателя c, если указатель находится в структуре? - PullRequest
0 голосов
/ 06 мая 2020

Скажем, у меня есть

  typedef struct exampleFooStruct foo; 
  typedef char* byte_ptr;

  struct exampleFooStruct{
     byte_ptr p;
     uint32_t num;
  }

  // ...

  foo* one =  malloc(sizeof(foo));
  one->p = 0x5;
  one->num = 0x4;

Прежде всего, присваивает ли one-> p значение one-> p равным 0x5 или устанавливает сам адрес на 0x5?

В конечном итоге я хочу установить сам адрес как 0x5, а не значение по этому адресу.

И если я хочу int answer = 0x9, я бы сделал

int answer = one->p + one->num;
//or
int answer = &(one->p) + one->num;

Ответы [ 3 ]

2 голосов
/ 06 мая 2020
foo* one =  malloc(sizeof(foo));

Это выделяет пространство для объекта структуры типа foo и назначает его адрес one. На самом деле это лучше записать как:

foo *one = malloc(sizeof *one);`

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

Обратите внимание, что на этом этапе значение one->p - мусор.

one->p = 0x5;

На самом деле это нарушение ограничения . Фактически, вы должны считать, что это незаконно. one->p - указатель, а 0x5 - значение типа int. Неявного преобразования целых чисел в указатели не существует (кроме особого случая константы нулевого указателя, которая здесь не применяется).

Ваш компилятор должен был хотя бы предупредить вас об этом. Фактически, любой соответствующий C компилятор должен выдавать диагностическое сообщение c. (На мой взгляд, прискорбно, что многие компиляторы по умолчанию рассматривают это как предупреждение, а не фатальную ошибку, но на то есть исторические причины).

Вы можете сделать это законным, выполнив приведение:

one->p = (byte_ptr)0x05;

Это будет хранить в объекте указателя one->p результат преобразования целочисленного значения 5 в значение указателя.

Если вы не выполняете какие-то низкоуровневые встроенные программирование в системе, где вы знаете, что находится по указанному адресу памяти c, это почти наверняка не имеет смысла делать это. 0x05, даже после преобразования, вряд ли будет действительным значением указателя, и любая попытка его использования может взломать sh вашу программу - если вам повезет. (Если вам не повезло, он будет «работать».)

В конечном итоге я хочу установить сам адрес как 0x5, а не значение по этому адресу.

Если это действительно то, что вы хотите сделать, то one->p = (byte_ptr)0x05; - это способ сделать это. Почему ты хочешь это сделать? Что находится в ячейке памяти 0x05?

int answer = one->p + one->num;

one->p - указатель, а one->num - целое число. Вы можете добавить указатель и целое число, но результатом будет указатель, и вы не сможете присвоить его объекту int без приведения. А с приведением типов это, скорее всего, не имеет смысла.

Если вы хотите хранить целочисленные значения, сохраняйте их в целочисленных объектах.

(Если вы хотите сохранить 0x05 в объект, на который указывает one->p, вам сначала нужно настроить, чтобы он указывал на что-то, возможно, с другим вызовом malloc. Но это не то, о чем вы спрашивали, я не буду говорить об этом. Возможно, это то, о чем вы должны были спрашивать, но я бы предпочел не делать никаких предположений.)

И если мне нужен int answer = 0x9, ...

Тогда можно написать int answer = 0x9;. Я предполагаю, что это не то, что вы ищете, но я не могу точно сказать, что это такое.

Наконец, чего вы пытаетесь достичь sh? Может быть, у вас проблема XY ?

1 голос
/ 06 мая 2020

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

Прежде всего, присваивает ли one-> p значение one-> p равным 0x5 или устанавливает сам адрес на 0x5?

Он генерирует сообщение компилятора, поскольку код недействителен C. См. «Указатель на целое число / целое число из указателя без приведения». .

Я в конечном итоге хочу установить сам адрес как 0x5, а не значение по этому адресу.

Далее нужно кастовать: one->p = (byte_ptr)0x5;. Но это серьезный запах кода, даже если адрес 5 действителен для вашей системы. Начнем с того, что использовать char для доступа к необработанным данным всегда неправильно, так как это тип с подразумеваемой подписью. И если вы хотите получить прямой доступ к физической памяти, вам почти наверняка понадобится квалификатор volatile.

, если мне нужен int answer = 0x9, я бы сделал

Ни одной из ваших альтернатив. В первом используется арифметика указателя c, во втором - тоже, но на основе адреса самого указателя. Нет смысла.

Вы бы сделали int answer = (uintptr_t)one->p + one->num;.


В целом, рассмотрите возможность изменения кода на этот:

  typedef struct {
     uint8_t* p;
     uint32_t num;
  } foo;

  // ...

  foo* one =  malloc(sizeof *one);
  one->p = (volatile uint8_t*)0x5;
  one->num = 0x4;

  uint32_t answer = (uintptr_t)one->p + one->num;
1 голос
/ 06 мая 2020

oes one-> p присваивает значение one-> p равным 0x5 или устанавливает сам адрес на 0x5

при условии, что byte_ptr действительно является указателем (адрес ), он будет делать то, что вы, вероятно, ожидаете, и он установит указатель на адрес 0x5

И если мне нужен int answer = 0x9, нужно ли вам сказать компилятору, чтобы указатель как целое число. он установит ответ на 0x9, но эта программа, на мой взгляд, не имеет смысла .: int answer = (int) (one-> p) + one-> num;

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...