2017-02-11 2 views
1

Я беру курс по программированию, ориентированному на машины, и у меня есть проблема с пониманием типов данных при обращении к адресу памяти. Например, если я хочу получить доступ к куску памяти в позиции 0x40020000. Я бы написал это так.В чем разница между типами данных при обращении к памяти?

#define NAME_OF_THE_REGISTER *((unsigned int *)(0x40020000)) 

Но в чем разница между использованием какого-либо другого типа данных? Например unsigned short или unsigned char. Я знаю, что у них разный диапазон, но если я изменю этот код на unsigned short, я все равно могу получить доступ к памяти, и все будет работать нормально.

В «примерных» кодах мой университет предоставляет им возможность переключиться между unsigned char, volatile unsigned char, unsigned int, volatile unsigned int и т. Д., И я не могу понять, почему. Пример

#define  PORT   0x40020000 
#define  PORTNAME  ((volatile unsigned int *)  (PORT)) 
#define  PORTOFFSET1  ((volatile unsigned short *) (PORT + 0x4)) //offset to access Output register 

В чем преимущества и недостатки между ними?

ответ

5

Рассмотрим распределение памяти:

100 01 02 03 04 
104 05 06 ff ff 
108 07 08 09 10 

На 32-битной, прямой порядок байтов машины, код

printf("%d\n", *(char *)104); 
printf("%d\n", *(short int *)104); 
printf("%d\n", *(int *)104); 
printf("%u\n", *(unsigned int *)104); 

, скорее всего, печать

5 
1541 
-63995 
4294903301 

(Если это не так Очевидно, что эти числа исходят из того, что они являются десятичными представлениями соответственно: 0x05, 0x0605, 0xffff0605, которые интерпретируются как подписанный int и 0xffff0605 интерпретируется как unsigned int.)

Итак, да, тип, на который вы указываете указатель, определяет способ доступа и интерпретации памяти компилятором. (Строго говоря, он определяет, как компилятор будет генерировать код, и как сгенерированный код будет доступ и интерпретировать память.)

  • Размер заостренный к типу (char, short, int , long и т. Д.) Будет определять, сколько байтов доступно. (В зависимости от аппаратного обеспечения он также определяет, существуют ли какие-либо ограничения на выравнивание.)
  • Независимо от того, подписан ли тип заостренного типа или без знака, будет ли он интерпретировать интерпретируемую память. (Конечно, большая часть отличия заключается в том, как вы или остальная часть вашего кода интерпретирует значения позже. Например, напечатайте ли вы что-то с помощью %d или %u, что приводит к тому, что делает больше или больше различий, чем вы выбираете подписанный или неподписанный тип.)
  • Используются ли отступники, такие как volatile, могут не вносить изменения вообще.
+0

'% d' печать 4294903301 кажется маловероятным – harold

+0

@harold Действительно. Опечатка. Теперь исправлено. Благодарю. –

1

Тип информирует компилятор о размере переменной, хранящейся в указанном месте. Поэтому использование неправильного типа приведет к извлечению (или перемещению) только для части фактических значений (или более прочитанного, если больше) в указанной ячейке памяти, когда будет отменено. Поэтому это важно и, следовательно, почему вы, возможно, захотите использовать volatile - говорит компилятору оставить это в одиночестве везде, где он встречается.

Во втором примере (макро) PORTOFFSET1 заменяет собой местоположение, которое имеет смещение 4 (для краткости типа) из адреса, используемого в поле #define PORTNAME. Это говорит о том, что как ячейки памяти PORTNAME, так и PORTOFFSET1 относятся к непрерывному блоку памяти, для которого первые четыре байта содержат int, а следующие два - короткие.

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

+0

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

+0

@Steve жаль, что я, возможно, что-то пропустил. Насколько мне известно, волатильная система в текущем контексте останавливает компилятор от изменения приведения (во время разыменования), если используется другой тип, скажем, int (когда тип является коротким, приводящим к возможному опасному «перепроданному»). В компиляторе без изменчивости: int i = * ((unsigned short *) (PORT + 0x4)); возможно, компилятор (не говоря о нем): int i = * ((unsigned int *) (PORT + 0x4)). Для меня это самый важный момент в контексте вопроса. Я, возможно, неправильно понял. – cdcdcd

+0

Основное использование 'volatile' заключается в предотвращении кэширования значений в регистрах и попытке обеспечить, чтобы последовательность загрузок и хранилищ соответствовала явным намерениям программиста как можно ближе. Это может иметь значение, если, скажем, указатель указывает на местоположение ввода-вывода с отображением памяти, которое может измениться в любое время. Я не знаю, как он удаляет любую свободу действий для компилятора, чтобы переписать такой бросок - лифтинг, который я не считаю, что компилятор имеет в любом случае. –

Смежные вопросы