2013-03-20 2 views
6

Я начал читать несколько статей о указателях на C, и у меня есть один пример, который я не понимаю.О указателях в C

пример отсюда: http://en.wikibooks.org/wiki/C_Programming/Pointers_and_arrays

Здесь:

Давайте посмотрим на несколько иной проблемы. Мы хотим иметь двухмерный массив, но нам не нужно иметь все строки одинаковой длины. Мы делаем объявление массива указателей. Вторая строка ниже объявляет A как массив указателей. Каждый указатель указывает на поплавок. Вот некоторые применимый код:

float linearA[30]; 
float *A[6]; 

A[0] = linearA;    /* 5 - 0 = 5 elements in row */ 
A[1] = linearA + 5;   /* 11 - 5 = 6 elements in row */ 
A[2] = linearA + 11;   /* 15 - 11 = 4 elements in row */ 
A[3] = linearA + 15;   /* 21 - 15 = 6 elements  */ 
A[4] = linearA + 21;   /* 25 - 21 = 4 elements  */ 
A[5] = linearA + 25;   /* 30 - 25 = 5 elements  */ 

A[3][2] = 3.66;   /* assigns 3.66 to linearA[17];  */ 
A[3][-3] = 1.44;   /* refers to linearA[12];   
          negative indices are sometimes useful. 
          But avoid using them as much as possible. */ 

Мой вопрос, почему A[0] является указателем только пять элементов, а не ко всем linearA, так как имя массива является указателем на его первый элемент.

И A[1] = linearA + 5; - 6 элементов в ряду - по той же причине? Разве не A[1] должен быть указателем на 6-й член linearA?

Может кто-нибудь объяснить, где моя ошибка?

+0

Предполагается, что 'A [0]' будет использоваться как указатель на первый элемент массива из пяти 'float'. Вы также можете использовать его как указатель на первый элемент массива из 30 'float', поскольку это то, что предоставляет' linearA'. Все это просто интерпретация. –

+2

Указатели не имеют информации о том, что * количество элементов * они указывают на. Указатель на первый элемент массива размера 42 точно такой же, как для * количества элементов * того, на что они указывают, в качестве указателя на первый элемент массива размером 12000. В вашем примере 'A [0] '- указатель на первый элемент массива из 30 элементов ... но он не заботится о * числе элементов *. Программист интерпретирует его как * 5 элементов * перед выполнением других назначений. – pmg

+0

@pmg: * тип * указателя указывает размер (и другую информацию) того, на что он указывает. Эта информация обычно не хранится во время выполнения. –

ответ

3

За исключением нескольких исключений, в C имя массива преобразуется в указатель на первый элемент массива. linearA представляет собой массив 30 из float и в выражении:

A[0] = linearA; 

он преобразуется в указатель на float.

A - массив 6 указателя на float. Элемент A имеет указатель на тип float. Таким образом, A[0] является указателем на float, а не указателем на массив.

И A[i][j] в С эквивалентно *(A[i] + j) так A[i][j] является float (разыменование указателя на float дает float).

+0

Я согласен со всем, что вы сказали, но я не понимаю их комментариев: A [0] = linearA;/* 5 - 0 = 5 элементов в строке */ A [1] = linearA + 5;/* 11 - 5 = 6 элементов в строке */ – Farseer

+0

@Farseer потому что '(linearA + 5) - linearA == 5' и' (linearA + 11) - (linearA + 5) == 6' – ouah

1

A[0] является указателем на 1-й элемент linearA. Поскольку linearA является непрерывным массивом, этот указатель фактически позволяет получить доступ к любому из элементов linearA 30 путем добавления соответствующего смещения. Однако в этом фрагменте кода вы эмулируете 2D-массив, указывая на разные смещения в массиве linearA. В результате получается 2D-подобная адресация массива: A[n] приводит вас к местоположению (т. Е. Смещению в linearA) вашей n-й строки и A[n][m] приводит вас к m-му элементу внутри этой строки.

+0

Нет, 'A [0] 'имеет тип' float * 'и указывает на один объект' float', который является 0-м элементом 'linearA'. –

+0

@ KeithThompson Вот что я имел в виду, я буду перефразировать, чтобы быть ясным. – SomeWittyUsername

1

Это потому, что эта строка устанавливает массив из 6 указателей на float:

float *A[6]; 

И эта линия задает первую из этих указателей на первого элемента из 30.

A[0] = linearA; 

Следовательно, каждый элемент A указывает на подраздел исходного массива. Вы должны назначить их, хотя они сначала указывают на случайные адреса.

Первый - это начальный адрес (&linearA[0]), а следующие пять - следующие. Они доступны как A[0][0] до A[0][5]. Из-за того, как массивы соответствуют указателям, вы можете продолжать расти, пока вы не превысите 30-е.

Но вы можете назначить A[n] любой части массива, который вам нравится. Пока это часть исходного массива, он укажет на этот член и на следующие 5 (или сколько угодно).

Например, указав A[1] на номер &linearA[6], вы бы эффективно создали двухмерный массив (он бы напоминал один, но не вел себя как один).

1

Мой вопрос, почему A [0] является указателем только пять элементов, а не все из linearA, так как имя массива является указателем на свой первый член.

вы настроите A[0], чтобы указать на linearA, который является первым поплавок в массиве, A[0] является указателем и, таким образом, ничего о том, что он указывает на расстанемся с адреса не знаю. Таким образом, A[0] не является указателем на только пять элементов, он указывает на то, где начинается массив, и не имеет понятия о том, где заканчивается массив.

И A [1] = linearA + 5; 6 элементов в строке - по той же причине? Не является ли [1] указателем на 6-й член линейного А?

да A[1] указывает на шестой элемент, но, как сказано перед его начальным адресом.

1

В примере вы размещены показывает несколько эзотерическую технику, называемую Iliffe vector, который является одним из возможных способов осуществить неровные массивы в C. зазубренный массив представляет собой матрицу, в которой каждая строка имеет различную длину.

Поскольку массивы одномерны в C, вы создаете один массив linearA, содержащий все элементы, которые интерпретируются как последовательность строк, каждый из которых имеет разный размер. Массив указателя A содержит указатели на первый элемент каждой строки, который позволяет вам обращаться к элементам с использованием индексов строк и столбцов.

Код показывает несколько интересных особенностей указателей C и массивах:

linearA + 5 

Pointer арифметики: добавление целого числа к указателю (или массиву) дает указатель, указывающий п элементов после первоначального указателя.

A[3][2] = 3.66; 

Этот красивый синтаксис позволяет вам думать об этой структуре как о двумерной матрице.

Кроме того, и это, вероятно, является основной точкой примера, указатели и массивы взаимозаменяемы. Здесь A[3] является указателем на float, так как A был определен как массив указателей на floats; добавление [2] дает нам элемент 2 места после указателя, указанного указателем оригинала.Это похоже на арифметику указателя выше, только в этом случае указатель разыменовывается. Фактически, доступ к массиву определяется с точки зрения указателей, поэтому X[5] эквивалентен *(X+5).

A[3][-3] 

Это показывает, что вам ничего не мешает вам получить доступ к элементу за пределами данной строки. В этом случае вы получаете доступ к элементу 3 места до, указанному A[3]. Это то, что редко требуется, и оно работает только в этом случае, потому что вы построили матрицу, чтобы иметь смежные элементы. Обычно доступ к элементам за пределами выделенного диапазона массива приведет к сбою вашей программы.

Наконец, чтобы ответить на ваш вопрос:

И A[1] = linearA + 5; является 6 элементов в ряд - по той же причине? Разве не A[1] должен быть указателем на 6-й член linearA?

Как указатели и массивы являются взаимозаменяемыми, A[1] является как указатель на шестой элемент в linearAи массив, начиная с шестого элемента в linearA. В языке нет ничего, говорящего, что последний из 6 элементов длинный, вы должны реализовать эту логику в своем коде.

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