2011-12-25 2 views
5

У меня есть основное сомнение в 2D-массивах (язык C). Рассмотрим декларацию 2D-массива следующим образом2D-переменная переменной указателя путаницы

int array[3][5]; 

Теперь, когда я делаю следующее, вывод и й ниже Printf такая же:

printf("%u\n", array); 
printf("%u\n", *(array)); 

Теперь при попытке к следующему:

printf("%u\n", array+1); 
printf("%u\n", *(array)+1); 

Выходы разные. Я получаю, что второй printf ссылается на массив [0] [1], а первый - на массив [1] [0]. Как это работает? array - указатель на что?

Заранее спасибо

ответ

1

Массивы не указатели. Игнорируйте любой ответ, книгу или учебник, который пытается сказать вам об ином.

Выражение типа массива, в большинстве контекстов, составляет , преобразованное (во время компиляции) в указатель на первый элемент массива.Исключения:

  • Операнд sizeof (sizeof arr дает размер массива, а не размер указателя)
  • Операнда одноместный & (&arr дает адрес массива, а не его первый элемент - та же ячейка памяти, другой тип). Это особенно актуально для вашего примера.
  • Строка буквальной в инициализаторе используется для инициализации объекта массива (char s[6] = "hello"; не копирует адрес строки буквальным, он копирует его значение)

2-мерный массив ничего больше или меньше массив массивов. Существуют и другие структуры данных, которые могут использоваться с тем же самым x[y][z] синтаксисом, но они не являются истинными двумерными массивами. Твое.

Оператор индексирования [] определен в терминах арифметики указателя. x[y] средства *(x+y).

Поведение вашего кода следует из этих правил.

Прочтите раздел 6 comp.lang.c FAQ. Это лучшее объяснение этого материала, который я видел.

И не используйте "%u" для печати значений указателя; конвертировать в void* и использовать "%p".

printf("%p\n", (void*)array); 
printf("%p\n", (void*)*(array)); 
0

Вы можете понять, таким образом:

точек массива на 3 строки с 5 столбцов каждый

когда вы делаете массив + 1, изменения строки, так что вы идете в 1 ряд. Вы должны попробовать получить доступ с помощью * (массив + 1).

когда вы * (массив), вы указывают на 0-й строки и * (массив) +1 движется вперед в колонке, так элемент массива [0] [1]

+0

так в чем разница b/w array + 1 и * (array + 1)? Когда я распечатываю их оба, я получаю одинаковое расположение памяти –

2

2D массивы в C запутаны.
массив и * массив являются одинаковыми указателями, но не являются одинаковыми.
массив имеет тип int [3] [5] (который представляет собой массив размером 5, массивов int [3]).
* array - это первая строка массива, которая имеет тип int [3].
массив + 1 означает массив плюс один элемент. Элементом массива является int [3], поэтому он составляет 12 байт вперед.
* массив + 1 означает * массив плюс один элемент. Элемент * массива - int, поэтому он имеет 4 байта вперед.

0

Приращение устройства в указателях соответствует размеру типа данных.

5

Я попытаюсь дать вам технически правильное объяснение, чтобы вы знали, что происходит. Не очень сложный, но на самом деле противоречащий интуиции.

Введение:

В C есть «lvalues», которые в основном представляют собой «назначаемые» объекты, которые имеют место где-то в памяти, и «rvalues», которые представляют собой, хорошо, «концептуальные» значения (не требуется для размещения в любом месте).

Например, если вы определяете int a = 5;, то a является lvalue типа int и value 5. Также он может быть интерпретирован как (или, скорее, преобразован) в rvalue типа int. Такое значение r еще будет известно, что оно равно 5, но оно больше не будет содержать информацию о местоположении a в памяти.

Некоторые выражения требуют lvalues ​​(например, левая сторона оператора =, потому что вам нужно назначить объект), а некоторые нужны rvalues ​​(например, operator +, потому что вам нужны только интегральные значения, когда вы добавляете, или правую сторону оператора =). Если выражение требует rvalue, но вы передаете lvalue, тогда оно преобразуется в rvalue.

Кроме того, для функций в C передаются только rvalues ​​(что означает, что C строго по умолчанию, а не по вызову).

Некоторых примеры:

int a = 1; 
a; // a is an lvalue of type int and value 1 
a = a+3; // here the `a` is converted to an rvalue of type int and value 1, then after the addition there's an assignment, on the lhs there's an lvalue `a` and an rvalue `4` 

Преобразование из именующих к RValue, как правило, тривиальны и unnoticable (это как принимая число 5 с полков меченых a). Массивы в основном являются исключением.

Большая вещь: В C нет значений типа массива. Имеются указатели lvalues ​​и rvalues, целочисленные lvalues ​​и rvalues, lvalues ​​структуры и rvalues ​​и т. Д. Но только массивы lvalue. Когда вы пытаетесь преобразовать lvalue типа массива в rvalue, у вас больше нет массива, у вас есть указатель на первый элемент массива. Это корень путаницы в массивах в C (и C++).

Пояснение:

  • array
  • *(array)
  • array+1
  • *(array)+1

array представляет собой объект типа int[3][5] (массив из 3 целых 5 цепей). Когда вы пытаетесь передать его функции, он получает указатель типа int (*)[5] (указатель на массив из 5 целых чисел), потому что это то, что осталось после преобразования lvalue-to-rvalue.

*(array) - tricker. Сначала выполняется lvalue-to-rvalue, что приводит к значению типа int(*)[5], затем operator* принимает это значение rvalue и возвращает lvalue типа int[5], после чего вы пытаетесь перейти к функции. Следовательно, он снова преобразуется в значение r, что приводит к int*.

array+1 вызывает массив, который будет преобразован в RValue типа int(*)[5] и Rvalue получает приращение на единицу, так что (в соответствии с правилами указателей арифметика) перемещает указатель 1 * sizeof(int[5]) байт вперед.

*(array)+1: см 2 пункта выше, но окончательное Rvalue типа int* получает приращение, опять же по правилам указателей арифметика, по 1 * sizeof(int).

Никакой тайны здесь!

+0

Отличное объяснение, но у меня есть один запрос для второго случая * (массив), почему оператор '*' возвращает значение lvalue, разыменовывает ли операнд всегда значение lvalue? – stackuser

+0

И мы не передаем массив функции, здесь мы просто печатаем ее, так как же происходит преобразование типа lvalue в rvalue? – stackuser

+0

Как вы просто печатаете, не передавая его функции? – Kos

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