2013-07-16 2 views
11
int main() 
{ 
    char arr[5][7][6]; 
    char (*p)[5][7][6] = &arr; 
    printf("%d\n", (&arr + 1) - &arr); 
    printf("%d\n", (char *)(&arr + 1) - (char *)&arr); 
    printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr); 
    printf("%d\n", (unsigned)(p + 1) - (unsigned)p); 

    return 0; 
} 

Когда я запускаю приведенный выше код я получаю следующий вывод:Преобразование адреса массива в другие типы данных

1 
210 
42 
210 

Почему выход не 1 в каждом случае?

+4

Подсказка: '6 * 7 = 42',' 5 * 7 * 6 = 210' –

+0

@Armin Может объясните пожалуйста? – Alex

+1

Еще один намек - вы не конвертируете адрес в другой тип до тех пор, пока * после * вы уже сделали арифметику на указателях, которая выполняется в предположении исходного типа, а не окончательного типа ... – twalberg

ответ

5

Примечание &arr является полным 3-мерным массивом массива символов, тогда как arr указывает на первый элемент, который представляет собой двумерный массив символов.Нечто, как показано ниже на схеме:

0xbf8ce2c6 
+------------------+  ◄-- arr = 0xbf8ce2c6 
| 0xbf8ce2f0 | 
| +------------------+  ◄-- arr + 1 = 0xbf8ce2f0 
| | 0xbf8ce31a | | 
| | +------------------+  ◄-- arr + 2 = 0xbf8ce31a 
| | 0xbf8ce344 | | | 
| | | +------------------+  ◄-- arr + 3 = 0xbf8ce344 
| | 0xbf8ce36e | | | | 
| | | | +------------------+  ◄-- arr + 4 = 0xbf8ce36e 
| | | | | | | | | | 
+---|---|---|--|---+ | | | | Each are 7*6, 2-Dimensional 
    | | | |  | | | | Consists Of 42 bytes 
    +---|---|--|-------+ | | | 
     | | |   | | | 
     +---|--|-----------+ | | 
      | |    | | 
      +--|---------------+ | 
       |     | 
       +------------------+ 

The diagram show: 
1. How a 3-dimensional can be interpreted as series of 2-dimensional arrays 
2. Here (arr + i) points to a 2-D array 
3. Notice difference between: (arr + i + 1) - (arr + i) = 0x2a = 42, where i = [0, 4] 

Тип &arr является char(*)[5][7][6], что это адрес полукокса 3D-массив размерности [5][7][6]. Значительная разница между &arr и &arr + 1 составляет 5 * 7 * 6 * sizeof(char) = 210.
Поскольку размер char[5][7][6] составляет 5 * 7 * 6 * sizeof(char).
В вашем коде &arr указывает на 3-мерный массив и &arry + 1 следующий 3-D массив (который не существует в нашем коде).

Проверить этот рабочий код на codepade:

int main() 
{ 
    char arr[5][7][6]; 
    printf(" &arr : %p", &arr); 
    printf(" &arr+1: %p", &arr + 1); 

    return 0; 
} 

Выход:

&arr : 0xbf5dd7de 
&arr+1: 0xbf5dd8b0 

Разница между (&arr + 1) - (&arr) = 0xbf5dd8b0 - 0xbf5dd7de = 0xd2 = 210.

В вашем втором Printf:

printf("%d\n", (char *)(&arr + 1) - (char *)&arr); 

Вы преобразование типов адресов типа char(*)[5][7][6] в простой (char*), и потому, что SizeOf char[5][7][6] является 210 оба адрес 210 далек. (вспомните sizeof(char) == 1). Именно по этой причине выходы: 210

Теперь, как я сказал в первом утверждении, arr является адресом первого элемента, который представляет собой двумерный массив символов. Тип arr: char(*)[7][6]. Теперь один элемент (двумерный массив размером 6 * 7 * sizeof(char) = 42).
(Примечание: вы можете представить трехмерный массив в виде массива один-d, где каждый элемент представляет собой 2-мерный массив).

В вашем третьем Printf:

printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr); 

Вы до приведения типов значение без знака (но не в качестве адреса/тип указателя). Разница между arr + 1 и arr равна 42 * sizeof(char) = 42 (то есть равна char[7][6]). Поэтому вывод printf: 42.

Примечание: Вы должны прочитать sizeof (int) == sizeof (void*)?, поскольку вы указываете адрес для значения. и это преобразование не полностью определено. (мое объяснение касается вашего вывода и результата, который я дал).

Для дальнейшей проверки осветления ниже рабочего кода на codepade:

int main() 
{ 
    char arr[5][7][6]; 
    printf(" arr : %p\n", arr); 
    printf(" arr+1: %p", arr + 1); 

    return 0; 
} 

Выход есть:

arr : 0xbf48367e 
arr+1: 0xbf4836a8 

разница между Take (arr + 1) - (arr) = 0xbf4836a8 - 0xbf48367e = 0x2a = 42.

Последний Printf:

printf("%d\n", (unsigned)(p + 1) - (unsigned)p); 

Просто разница между &arr+1 и &arr = 210 (аналогично второму Printf), потому что p является указателем на 3-D массив символов (= &arr). И вы приписываете его типу значения (не указательному типу).

Кроме того, (Просто добавить для понимания цели, я предполагаю, что читатель найдет это полезным),

Давайте мы узнаем еще одно различие между arr и &arr с помощью оператора SizeOf, который поможет вашей понять концепцию более глубокое , Для первого чтения: sizeof Operator

При применении оператора sizeof идентификатор массива, результат является размером всего массива, а не размера указателя, представленного идентификатором массива.

Проверить этот рабочий код на codepade:

int main() 
{ 
    char arr[5][7][6]; 
    printf(" Sizeof(&arr) : %lu and value &arr: %p\n", sizeof(&arr), &arr); 
    printf(" Sizeof(arr) : %lu and value arr : %p\n", sizeof(arr), arr); 
    printf(" Sizeof(arr[0]): %lu and value a[0]: %p\n",sizeof(arr[0]), arr[0]); 
    return 0; 
} 

Его выход:

Sizeof(&arr) : 4 and value &arr: 0xbf4d9eda 
Sizeof(arr) : 210 and value arr : 0xbf4d9eda 
Sizeof(arr[0]): 42 and value a[0]: 0xbf4d9eda 
  • Здесь &arr это просто адрес, а в адрес системы имеет четыре байта и это адрес полного 3-мерного массива символов.

  • arr - это имя 3-мерного массива, а оператор sizeof дает общий размер массива 210 = 5 * 7 * 6 * sizeof(char).

Как я показал в моей диаграмме arr указует на первые элементы, который является 2-мерным массивом. Так, потому что arr = (arr + 0). Теперь с помощью * Оператор разыменования на (arr + 0) дает значение по адресу *(arr + 0) = arr[0].

  • Уведомление sizeof(arr[0]) дает 42 = 7 * 6 * sizeof(char). И это доказательство концептуально представляет собой трехмерный массив, но массив двумерного массива.

Потому что выше в моем ответе на много раз, когда я написал, как: «размер char[5][7][6] является 5 * 7 * 6 * sizeof(char) так я добавляю интересный код ниже @codepade:

int main(){ 
printf(" Char   : %lu \n", sizeof(char)); 
printf(" Char[5]  : %lu \n", sizeof(char[6])); 
printf(" Char[5][7] : %lu \n", sizeof(char[7][6])); 
printf(" Char[5][7][6]: %lu \n", sizeof(char[5][7][6])); 

return 1; 
} 

Выход:

Char   : 1 
Char[5]  : 6 
Char[5][7] : 42 
Char[5][7][6]: 210 
+0

Проверьте это [диаграмма] (http://alibad.files.wordpress.com/2010/05/array3d-jagged.jpg?w=369&h=590) –

+0

@Alex попробуйте код снова с каждой typecase на этот раз: ' (char (*) [5] [7] [6]) '. например: 'printf ("% d \ n ", ((char (*) [5] [7] [6]) (arr + 1) - ((char (*) [5] [7] [6]) arr); 'и указать причину вывода. –

6

Ну, если бы я хотел разделить волосы: во-первых, код вызывает неопределенное поведение повсюду, в операциях printf(). Разница двух указателей имеет тип ptrdiff_t, и для этого правильный спецификатор преобразования составляет %td, а не %d.

Остальное - только спекуляция. Предположим, что ваша система разумна и численно Значение указателя &arr всегда одно и то же, независимо от того, к чему он будет преобразован.

(&arr + 1) - &arr 1, конечно, согласно правилам арифметики указателя. (Фактическая разница между этими двумя указателями 210 * sizeof(int) байт, но это не школа математика, но арифметика указателей, поэтому результатом в единицах размера sizeof(T), где T является базовым типом указателя.)

Тогда (char *)(&arr + 1) - (char *)&arr направляет указатели на char *, а так как размер char равен 1, это будет печатать разницу в байтах; Вы эффективно обманываете/злоупотребляете арифметикой указателя здесь.

Кроме того: printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr) вычитается из двух указателей типа int (*)[7][6]. Вот что arr распадается. Конечно, 7 * 6 = 42, поэтому разница в размере между arr + 1 и arr составляет 42 элемента.

p, однако, не является указателем на первый элемент массива, но он является указателем на сам массив. Его тип правильно обозначен как int (*)[5][6][7]. Теперь, если вы печатаете разницу с помощью , что тип, но вы не позволяете компилятору сделать разделение по валять его в том, что указатели являются просто unsigned, то вы получите 5 * 6 * 7, который 210.

+1

Кто бы ни отклонил, оставьте комментарий. Я не вижу причины для нисходящего потока. –

+3

Ответ плохо написан и не объясняет задействованные типы. Затем ответ написан таким образом, чтобы делать истинные высказывания и демонстрировать, а не учить понятия тому, кто их не понимает. Кроме того, преобразование в 'char *' и вычитание не обманывает или злоупотребляет арифметикой указателя; он полностью определяется стандартом C. –

6

В (&arr + 1) - &arr :

&arr - это адрес массива из 5 массивов из 7 массивов 6 char. Добавив один, вы получите адрес, где следующий массив из 5 массивов из 7 массивов 6 char был бы, если бы у нас был массив этих объектов, а не только один. Вычитая исходный адрес, &arr, выдает разницу между двумя адресами. По стандарту C эта разница выражается как количество элементов между двумя адресами, где тип элемента - это тип объекта, на который указывают. Поскольку этот тип представляет собой массив из 5 массивов из 7 массивов 6 char, расстояние между двумя адресами является одним элементом. Другими словами, расстояние от &arr до (&arr + 1) составляет массив из 5 массивов из 7 массивов 6 char.

В (char *)(&arr + 1) - (char *)&arr:

&arr + 1 снова указатель, где следующий массив из 5 массивов 7 массивов 6 char будет. Когда он преобразуется в char *, результатом является указатель на то, что будет первым байтом этого следующего массива. Аналогично, (char *)&arr является указателем на первый байт первого массива. Тогда вычитание двух указателей дает разницу между ними в элементах. Так как эти указатели являются указателями на char, разница получается как число char. Таким образом, разница в количестве байтов в массиве из 5 массивов из 7 массивов 6 char, что составляет 5 • 7 • 6 char или 210 char.

В (unsigned)(arr + 1) - (unsigned)arr:

Поскольку arr не используется с & (или sizeof или других исключительных случаях), она автоматически преобразуется из массива 5 массивов 7 массивов 6 char на указатель на первый элемент , Таким образом, это указатель на массив из 7 массивов 6 char. Затем arr + 1 является указателем на следующий массив из 7 массивов 6 char. Когда этот адрес преобразуется в unsigned, результат в реализации C, который вы используете, фактически является адресом памяти объекта. (Это обычное явление, но не гарантируется стандартом C и, разумеется, прерывается, когда адреса имеют 64 бита, но unsigned - 32 бита.) Аналогичным образом, (unsigned)arr является адресом первого объекта. Когда адреса вычитаются, результатом является расстояние между ними в байтах. Таким образом, разница - это количество байтов в массиве из 7 массивов 6 char, что составляет 7 • 6 байтов или 42 байта. Обратите внимание на разницу клавиш в этом случае: &arr является указателем на массив из 5 массивов из 7 массивов 6 char, но arr является указателем на массив из 7 массивов 6 char.

В (unsigned)(p + 1) - (unsigned)p:

p представляет собой указатель на массив из 5 массивов 7 массивов 6 char. Тогда p+1 является указателем на то, где будет следующий массив. Преобразование в unsigned действует, как описано выше. Вычитание дает разницу в байтах, поэтому это размер массива из 5 массивов из 7 массивов 6 char, поэтому он снова составляет 210 байт.

Как и в сторону:

Тип (&arr + 1) - &arr является ptrdiff_t и должны быть напечатаны с %td.

Тип (char *)(&arr + 1) - (char *)&arr - ptrdiff_t и должен быть напечатан с %td.

Тип (unsigned)(arr + 1) - (unsigned)arr: unsigned int и должен быть напечатан с %u.

Тип (unsigned)(p + 1) - (unsigned)p - unsigned int и должен быть напечатан с %u.

+0

Как вы думаете, код вызывает неопределенное поведение во время выполнения? –

+0

@GrijeshChauhan: Определены дополнения, вычитания и преобразования в 'char *' в этом коде. Преобразования в 'unsigned' не полностью определены, но хорошо себя ведут в типичных реализациях C, если результирующие адреса находятся в пределах ограничений типа' unsigned'. Использование неверных спецификаторов формата не определено. –

+0

Спасибо! Я получил это сейчас, я снова прочитал эту часть вашего ответа (я проголосовал 2-й). но мой лимит голосования по голосованию пересек. Спасибо –

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