2013-06-26 3 views
2

Может кто-то прояснить неправильную интерпретацию с моей стороны? Я знаю, что мое понимание неверно из-за результата моего кода (см. Нижнюю часть вопроса). Спасибо заранее.Разница в значениях между доступом указателя и доступом к массиву

Чтобы уточнить, что делает каждый сегмент следующей строки означают ?:

*(u8 *)((u32)BufferAddress + (u32)i) 

И как же она отличается от следующей строки:

*(u32 *)((u32)BufferAddress + (u32)i) 

Моя интерпретация выше:

  1. segment1 = ((u32) BufferAddress + (u32) i) => определить адрес как целое.
  2. segment2 = (u32 *) (segment1) => указать адрес, который будет обрабатываться как указатель, где указатель имеет длину 32 бита.
  3. segment3 = * (segment2) => разыменовать указатель, чтобы получить значение для проживания по вычисленному адресу.

Что является неправильным в моей интерпретации? Я думаю, что мое отсутствие понимания находится в области сегмента2 ... В чем разница между кастингом (u32 *) и (u8 *)?

Вот код, который заставил меня понять, у меня есть знания запрещенную:

инициализации код:

main(...) { 
    ... 
    u8 *Buffer = malloc(256); 
    ... 
    Buffer[0] = 1; 
    Buffer[1] = 0; 
    Buffer[2] = 0; 
    Buffer[3] = 4; 
    Buffer[4] = 0; 
    Buffer[5] = 0; 
    qFunction(... , Buffer, 6, ...); 
    ... 
} 

qFunction(... , const u8 *BufferPointer, u32 BufferLength, ...) { 
    u32 BufferAddress; 
    ... 
    BufferAddress = (u32) BufferPointer; 
    ... 

    /* Method 1: */ 
    for (i=0; i < BufferLength; i++) 
      printf("%d, %p\n", BufferPointer[i], &BufferPointer[i]); 

    /* Method 2: */ 
    for (i=0; i < BufferLength; i++) 
      printf("%d, 0x%lx\n", *(u8 *)(BufferAddress+i), BufferAddress+i); 

    /* Method 3: */ 
    for (i=0; i < BufferLength; i++) 
      printf("%d, 0x%lx\n", *(u32 *)(BufferAddress+i), BufferAddress+i); 
    ... 
} 

Выходы Метод 1 и метод 2, как я ожидаю (оба то же самое):

1, 0x1000000 
0, 0x1000001 
0, 0x1000002 
4, 0x1000003 
0, 0x1000004 
0, 0x1000005 

Однако выход метода 3 кажется мне странным; только часть результата такая же, как и метод 1/2:

-1442840511, 0x1000000 
11141120, 0x1000001 
43520, 0x1000002 
4, 0x1000003 
0, 0x1000004 
0, 0x1000005 

Буду признателен за любые советы или ссылки на материалы для чтения. Спасибо.

+1

Ответы macduff верны, но один комментарий добавить; вы должны стараться избегать приведения указателей на целые числа, если это возможно, поскольку это не является действительно необходимым и вызовет проблемы, если вы когда-нибудь решите построить в 64-битных в будущем (указатели будут 64 бита, а не 32). –

ответ

3

Я мог бы поддразнивать и сказать: «Вы не дали нам достаточной информации». Технически это правда, но это просто требует принятия некоторых предположений.u8 и u32 не являются стандартными типами C, и вы могли бы их переназначить на что угодно, но предположительно они представляют 8-значное значение без знака (например, uchar) и 32-значное значение без знака (например, unsigned). Предполагая, что давайте посмотрим на те, которые вы понимаете, и объясните, где это выходит из третьего.

BufferPointer - это const u8 *, что означает, что он является постоянным указателем типа u8. Это означает, что массив, на который он указывает, имеет тип 8-битный без знака.

Теперь BufferAddress - это u32 - это типично для указателей, по крайней мере, на 32-битной системе. Поскольку они всегда имеют размер шины, на 64-битной системе указатели имеют 64 бита.

Итак, Method1 печатает элементы массива и адрес массива. Это прекрасно и круто.

Method2:

* (u8 *) (BufferAddress + я), BufferAddress + я

BufferAddress является целым числом без знака, и вы добавляете значения к нему, чтобы получить другие адреса. Это базовая точка массивов - память будет смежной, и вы получаете доступ к следующему элементу, увеличивая количество байтов каждого элемента. Так как это массив u8, вы просто продвигаетесь на 1. Вот, однако, поймать - если бы это был массив ints, вы бы захотели BufferAddress + (i * 4), а не BufferAddress + i, потому что размер каждого int равен 4 байт. Кстати, так как арифметика указателя работает в C. Если вы сделали `& (((u32 *) BufferAddress) + 1), вы получите 0x100004 вместо 0x100001, потому что вы выбрали BufferAddress для указателя 4 байта, и компилятор знает, что когда вы смотрите на следующий элемент, он должен быть 4 байта.

So (BufferAddress+i) - адрес i-го элемента массива u8. (u8 *) отличает BufferAddress от сверляющего целого числа до указателя на ячейки памяти типа u8, так что когда вы делаете *(u8 *), компилятор знает, как рассматривать его как u8. Вы могли бы сделать (u64 *), и компилятор сказал бы «О! Эта область памяти 64 бит» и попытаться интерпретировать значения таким образом.

Что может дать понять, что происходит в методе 3 сейчас. Вы получаете соответствующие адреса каждого элемента массива, но вы говорите компилятору «рассматривайте эту область памяти как 32-битные данные». Поэтому каждый раз, когда вы используете * (u32 *), вы читаете 4 байта массива и рассматриваете его как unsigned int. Кстати, раз i> = 3, вы сталкиваетесь с неопределенным поведением, потому что вы читаете вне массива.

Позвольте мне дать визуализацию того, что ваша память в этой области выглядит следующим образом:

0x1000000 = 1 
0x1000001 = 0 
0x1000002 = 0 
0x1000003 = 4 
0x1000004 = 0 
0x1000005 = 0 

Для method2, когда я = 2, вы смотрите на BufferAddress (= 0x1000000) + 3, т.е. 0x1000002, который имеет в нем число 0. Компилятор знает, что это только один байт, так что с этим.

Но для метода3, когда i = 3, вы говорите компилятору, чтобы рассматривать его как 32 бита. Таким образом, он не видит «0», он видит 0, 4, 0, 0 и использует эти цифры для получения целочисленного значения, которое определенно не будет 4.

3
*(u8 *)((u32)BufferAddress + (u32)i) 
*(u32 *)((u32)BufferAddress + (u32)i) 

В верхней строке отбрасывает указатель на беззнаковое 8 битное значение, прежде чем разыменовании, в то время как секунды отбрасывает его в беззнаковое 32 битное значение, прежде чем разыменовании. Верхняя строка отменяет один байт, а нижняя - разыгрывает целых 4 байта.

Для решения другого вопроса:

Что неправильно о моей интерпретации? Я думаю, что мое отсутствие понимания находится в области segment2 ... В чем разница между литьем (u32 *) и (u8 *)?

Интерпретация адреса, длина которого составляет 32 бита, истинна для верхней и нижней строк кода.