Ваш код в порядке
Вы абсолютно правы, а ваш учитель не прав. Нет абсолютно никакой причины добавлять эту дополнительную сложность, поскольку это никак не влияет на результат. Это даже вносит ошибку. (См. Ниже)
Во-первых, отдельная проверка, если n
равно нулю, очевидно, совершенно не нужна, и это очень легко реализовать. Если честно, я на самом деле подвергаю сомнению вашу компетентность учителя, если у него есть возражения по этому поводу. Но я думаю, что каждый может время от времени пукнуть. Тем не менее, я ДЕЙСТВИТЕЛЬНО считаю, что while(n)
следует изменить на while(n != 0)
, потому что это добавляет немного большей ясности, даже не стоя дополнительной строки. Это незначительная вещь.
Второй немного более понятен, но он все еще не прав.
Это то, что C11 стандарт 6.5.5.p6 говорит:
Если частное a / b представимо, выражение (a / b) * b + a% b должно быть равно a;в противном случае поведение a / b и a% b не определено.
Сноска гласит:
Это часто называют «усечением до нуля».
Усечение до нуля означает, что абсолютное значение для a/b
равно абсолютному значению для (-a)/b
для всех a
и b
, что, в свою очередь, означает, что ваш код идеальнохорошо. Тем не менее, ваш учитель имеет смысл, что вы должны быть осторожны, потому что факт, что вы возводите в квадрат результат, на самом деле имеет решающее значение. Вычисление a%b
в соответствии с приведенным выше определением - простая математика, но это может пойти вразрез с вашей интуицией. Для умножения и деления результат является положительным, если операнды имеют знак равенства. Но когда дело доходит до модуля, результат имеет тот же знак, что и операнд first . Например, 7%3==1
, но (-7)%(-3)==(-1)
. Вот фрагмент, демонстрирующий это:
$ cat > main.c
#include <stdio.h>
void f(int a, int b)
{
printf("a: %2d b: %2d a/b: %2d a\%b: %2d (a%b)^2: %2d (a/b)*b+a%b==a: %5s\n",
a, b ,a/b, a%b, (a%b)*(a%b), (a/b)*b+a%b == a ? "true" : "false");
}
int main(void)
{
int a=7, b=3;
f(a,b);
f(-a,b);
f(a,-b);
f(-a,-b);
}
$ gcc main.c -Wall -Wextra -pedantic -std=c99
$ ./a.out
a: 7 b: 3 a/b: 2 a%b: 1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: -7 b: 3 a/b: -2 a%b: -1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: 7 b: -3 a/b: -2 a%b: 1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: -7 b: -3 a/b: 2 a%b: -1 (a%b)^2: 1 (a/b)*b+a%b==a: true
Итак, по иронии судьбы, ваш учитель доказал свою точку зрения, ошибаясь.
Код вашего учителя ошибочен
Да, это на самом делеявляется. Если входное значение равно INT_MIN
И архитектура является дополнением до двух И шаблон битов, где бит знака равен 1, а все биты значения равны 0, НЕ является значением ловушки (использование дополнения до двух без значений прерываний очень распространено), тогда ваш Код учителя выдаст неопределенное поведение в строке n = n * (-1)
. Ваш код - если даже немного - лучше , чем его. И если учесть небольшую ошибку, сделав код ненужным сложным и получив абсолютно нулевое значение, я бы сказал, что ваш код НАМНОГО лучше.
Другими словами, в компиляциях, где INT_MIN = -32768 (даже еслирезультирующая функция не может получить входные данные <-32768 или> 32767), ввод valid -32768 вызывает неопределенное поведение, поскольку результат - (- 32768i16) не может быть выражен как 16-разрядное целое число,(На самом деле, -32768, вероятно, не приведет к неправильному результату, потому что - (- - 32768i16) обычно оценивается как -32768i16, и ваша программа корректно обрабатывает отрицательные числа.) (SHRT_MIN может быть -32768 или -32767, в зависимости от компилятора.)
Но ваш учитель прямо заявил, что n
может быть в диапазоне [-10 ^ 7;10 ^ 7]. 16-битное целое число слишком мало;вам придется использовать [как минимум] 32-разрядное целое число. Использование int
может показаться безопасным для его кода, за исключением того, что int
не обязательно является 32-разрядным целым числом. Если вы компилируете для 16-битной архитектуры, оба ваших фрагмента кода имеют недостатки. Но ваш код все еще намного лучше, потому что этот сценарий повторно вводит ошибку с INT_MIN
, упомянутую выше с его версией. Чтобы избежать этого, вы можете написать long
вместо int
, что является 32-разрядным целым числом в любой архитектуре. A long
гарантированно может содержать любое значение в диапазоне [-2147483647;2147483647]. C11 Стандарт 5.2.4.2.1 LONG_MIN
часто -2147483648
, но максимально (да, максимум, это отрицательное число) допустимое значение для LONG_MIN
равно 2147483647
.
Какие изменения я бы сделал в вашем коде?
Ваш код в порядке, так что на самом деле это не жалобы. Это больше похоже на то, что если мне действительно, действительно нужно что-то сказать о вашем коде, есть некоторые мелочи, которые могут сделать его чуть-чуть понятнее.
- Имена переменных могут быть немногонемного лучше, но это короткая функция, которую легко понять, поэтому это не имеет большого значения.
- Вы можете изменить условие с
n
на n!=0
. Семантически это на 100% эквивалентно, но делает его немного понятнее. - Переместите объявление
c
(которое я переименовал в digit
) внутри цикла while, поскольку оно используется только там. - Измените тип аргумента на
long
, чтобы он мог обрабатывать весь входной набор.
int sum_of_digits_squared(long n)
{
long sum = 0;
while (n != 0) {
int digit = n % 10;
sum += (digit * digit);
n /= 10;
}
return sum;
}
На самом деле, это может немного вводить в заблуждение, поскольку - как упоминалось выше - переменнаяdigit
может получить отрицательное значение, но сама по себе цифра никогда не бывает ни положительной, ни отрицательной. Есть несколько способов обойти это, но это ДЕЙСТВИТЕЛЬНО придирчиво, и мне было бы наплевать на такие мелкие детали. Особенно отдельная функция для последней цифры заходит слишком далеко. По иронии судьбы, это одна из вещей, которую на самом деле решает код вашего учителя.
- Измените
sum += (digit * digit)
на sum += ((n%10)*(n%10))
и полностью пропустите переменную digit
. - Измените знак
digit
если отрицательный. - Создать отдельную функцию, которая извлекает последнюю цифру.
int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; }
- Просто назовите его
c
, как вы это делаете изначально. Это имя переменной не дает никакой полезной информации, но, с другой стороны, оно также не вводит в заблуждение.
Но, если честно, в этот момент вам следует перейти к более важной работе. :)