2015-04-05 3 views
1

Что происходит внизу?В чем разница между float arrayName [] [] и float (* arrayNamePointer) []

Ниже приводится выдержка из C Primer Plus:

const float rain[YEARS][MONTHS] = 
    { 
     { 4.3, 4.3, 4.3, 3.0, 2.0, 1.2, 0.2, 0.2, 0.4, 2.4, 3.5, 6.6 }, 
     { 8.5, 8.2, 1.2, 1.6, 2.4, 0.0, 5.2, 0.9, 0.3, 0.9, 1.4, 7.3 }, 
     { 9.1, 8.5, 6.7, 4.3, 2.1, 0.8, 0.2, 0.2, 1.1, 2.3, 6.1, 8.4 }, 
     { 7.2, 9.9, 8.4, 3.3, 1.2, 0.8, 0.4, 0.0, 0.6, 1.7, 4.3, 6.2 }, 
     { 7.6, 5.6, 3.8, 2.8, 3.8, 0.2, 0.0, 0.0, 0.0, 1.3, 2.6, 5.2 } 
    }; 

    int year, month; 
    float subtot, total; 

    printf(" YEAR RAINFALL (inches)\n"); 

    for (year = 0, total = 0; year < YEARS; year++) 
    { 
     // for each year, sum rainfall for each month 
     for (month = 0, subtot = 0; month < MONTHS; month++) 
     { 
      subtot += rain[year][month]; 
     } 

     printf("%5d %15.1f\n", 2010 + year, subtot); 
     total += subtot; // total for all years 
    } 

мне пришлось изменить это, чтобы использовать указатели вместо индексов массива.

Так что я пошел с:

[....] 

float (* rainPointer)[2]; 
rainPointer = rain; 

[....] 

subtot += *(*(rainPointer + year) + month); 

Это работает на 0. года приращение года правильно и месяц переустанавливает правильно. Тем не менее, год 1 не указывает, где бы я ожидал. Я прошел через это миллион раз, я запустил их бок о бок, rainPointer всегда (на моих глазах) кажется правильным, год и месяц всегда правильные.

Я нашел ответ через Google, я должен использовать:

subtot += *(*(rain + year) + month); 

что различные между дождем и rainPointer? Почему они не совпадают, если оба указателя на начало массива из двух ints?

Что-то происходит, что я, очевидно, не осведомлен или полностью отсутствует.

+3

Является ли это опечаткой, что 'rainPointer' объявляется с самым внутренним размером массива' [2] ', а не' [12] '(или' [MONTHS] ')? – Wintermute

+0

Не то, что я знаю? Я мог бы ошибаться, но насколько сказано в книге (и этот фрагмент является дословным), вы должны объявить указатель на массив, который должен указывать на начало массива с двумя ints? Я не так хорошо объясняю. –

+0

'int', похоже, не участвует; как 'rain', так и' rainPointer' относятся к массивам 'float'. Дело в том, что 'rain' представляет собой массив массивов из 12' float 'each, а' rainPointer' - указатель на массивы из 2 'float' each. Они не совсем совместимы, и вы делаете это только в том случае, если хотите получить доступ к данным, как если бы они были в другом, выстроенном 2D-массиве (и не заботились о строгих правилах псевдонимов). И если вы хотели это сделать, тогда это звучит так, как будто вы получили ожидаемое поведение. – Wintermute

ответ

1

дождь двумерный массив, объявленный как

const float rain[YEARS][MONTHS]; 

В выражениях имя массива преобразуется в указатель на первый элемент массива. Например, если у вас есть массив

T rain[YEARS]; 

то в выражении rain преобразуется в указатель типа T *

На самом деле двумерный массив одномерный массив элементов из которых, в свою очередь одномерным массивы.

Таким образом, Декларация

const float rain[YEARS][MONTHS]; 

может быть записана как

typedef float T[MONTHS]; 
const T rain[YEARS]; 

где Т имеет тип float [MONTHS}

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

Выражение * (дождь + год) дает этот массив, который является этой строкой.

So * (rain + year) - это некоторая строка, которая представляет собой одномерный массив. Снова массив преобразуется в указатель на его первый элемент. Тип элемента равен const float Таким образом, выражение *(rain + year) + month является указателем на элемент в этом одномерном массиве (строке), который соответствует данному месяцу.

Наконец, выражение *(*(rain + year) + month) дает этот элемент строки.

Таким образом, используя выражение rain + year, вы повторяете строки. Используя выражение *(rain + year) + month, вы выполняете итерации через элементы данной строки .

Если вы хотите переписать петлю, используя только указатели, то они могут выглядеть

const float (*rain_ptr)[MONTHS]; 
const float *month_ptr; 
float total = 0.0f; 

for (rain_ptr = rain; rain_ptr != rain + YEARS; ++rain_ptr) 
{ 
    // for each year, sum rainfall for each month 
    float subtot = 0.0f; 
    for (month_ptr = *rain_ptr; month_ptr != *rain_ptr + MONTHS; ++month_ptr) 
    { 
     subtot += *month_ptr; 
    } 

    printf("%5d %15.1f\n", 2010 + rain_ptr - rain, subtot); 
    total += subtot; // total for all years 
} 

Конечно, если компилятор не позволяет, то есть нет необходимости объявлять указатели перед петлями. Лучше объявить их в циклах.

+0

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

+0

@Scotty Smiles Не могли бы вы показать пример, который дает разные результаты? Кстати, вы можете задать мне вопрос на английском языке на моем личном форуме в разделе для начинающих (первый раздел) по адресу: www.cpp.forum24.ru –

+0

@Scotty Smiles Посмотрите мой ответ на свой вопрос лично мне. :) –

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