Рассматривать:
int sum(const int numbers[], const int size){
if (size == 0)
return 0;
else
return numbers[0] + sum(numbers+1, size-1);
}
Это простая рекурсивная функция из MIT 6.096 для добавления произвольного числа целых чисел, и она работает.
То, что я не могу понять, находится в последней строке:
Как numbers+1
работа, учитывая numbers[]
является int
массив, и вы не должны быть в состоянии добавить целое число к int[]
постоянная?
как работает «число + 1», если число [] является массивом int, и вы не можете добавить целое число к константе int []?
Нет никаких int[]
постоянная. numbers
распадается на указатель и numbers+1
простая арифметика указателей, применяемая к параметру, передаваемому рекурсивному вызову.
В качестве дополнительного примечания к ответу @ πάντα ῥεῖ приведем несколько пояснений по терминологии:
Ниже приведен еще один способ отображения обозначения массива:
Фраза numbers[1]
также может быть выражено как *(numbers + 1)
Где *
оператор сказал разыменовать адрес указателя numbers + 1
,
разыменовать в этом случае можно думать как прочитайте значение, на которое указывает.
Итак, код в вашем примере использует арифметика указателей. Фраза numbers + 1
это обозначение указателя, указывающее на второе int расположение указателя numbers
, size - 1
это количество байтов из ячейки памяти, начиная с numbers
до конца массива.
Что касается значения гнилой:
Как правило, в контексте Аргументы массива C, распад передает идею, что аргумент массива испытывает потерю информации о типе и измерении. Ваш const int numbers[]
сказано (возможно) распад в int *
поэтому больше не может предоставить информацию о размере массива. (С использованием sizeof()
макрос, например, не предоставляет длину массива, но размер указателя.) Это также является причиной, по которой предоставляется второй аргумент для передачи информации о размере.
тем не мение, в контексте этого вопроса значение распад является академическим, как отмечает @Ben Voigt: Последовательность токенов const int numbers [], когда она появляется в списке формальных параметров, объявляет указатель, а не массив. (Он никогда не превращался в указатель, потому что это был указатель для начала.)
Как πάντα ῥεῖ говорит int[]
распадается на int*
,
Но это sum
функция — это решение для бедняка, вы должны предпочесть accumulate
:
cout << accumulate(numbers, next(numbers, size), decay_t<decltype(numbers[0])>{});
Если у вас есть C ++ 17 и статически распределенный массив, такой как int numbers[size]
, вы можете воспользоваться cbegin
а также cend
:
cout << accumulate(cbegin(numbers), cend(numbers), decay_t<decltype(numbers[0])>{});
Я попытался сравнить рекурсивный sum
против accumulate
, тем не мение sum
не хватает места в стеке, прежде чем я смогу достичь vector
размер со значительной разницей, делая accumulate
явный победитель.
Я ассоциирую тип accumulate
«s init
агмент с типом numbers
‘элементы: decay_t<decltype(numbers[0])>{}
, Причина этого в том, если кто-то должен был вернуться и изменить тип numbers
, а не менять тип accumulate
«s init
В качестве аргумента накопление будет присвоено неправильному типу.
Например, если мы используем строку накопления: cout << accumulate(cbegin(numbers), cend(numbers), 0)
это хорошо для int numbers[]
, Проблема возникнет, если мы перейдем к определению: double numbers[] = {1.3, 2.3, 3.3, 4.3};
но мы не смогли изменить init
аргумент, который мы суммируем double
в int
, Это приведет к 10, а не 11,2: http://ideone.com/A12xin
int sum(int *num,int size)
{
int total=0;
/* function to sum integer array */
if (size <= 0) return(ERROR);
while(size--) total+= *num++;
return total;
}
Быстрее, компактнее и устойчивее к ошибкам.
число указатель; на каждой итерации функция sum () перемещается по массиву (вот что номера + 1 делает), одновременно уменьшая размер на 1 (—размер будет работать так же хорошо).
Когда размер достигает 0, это условие выхода, и рекурсия заканчивается.