Ваш код ідеально чудовий
Ви абсолютно правильні, а ваш вчитель помиляється. Немає абсолютно ніяких причин додати цю додаткову складність, оскільки це зовсім не впливає на результат. Він навіть вводить помилку. (Дивись нижче)
По-перше, окрема перевірка, якщо n
дорівнює нулю, очевидно, зовсім непотрібна, і це дуже легко здійснити. Якщо чесно, я фактично ставлю під сумнів компетентність ваших викладачів, якщо він має заперечення проти цього. Але я здогадуюсь, що у кожного може час від часу боліти мозок. Однак я думаю, що це while(n)
слід змінити, while(n != 0)
оскільки це додає трохи додаткової ясності, навіть не витрачаючи зайву лінію. Хоча це незначна річ.
Другий - трохи зрозуміліший, але він все-таки помиляється.
Про це говорить стандарт C11 6.5.5.p6 :
Якщо показник a / b є представним, вираз (a / b) * b + a% b дорівнює a; в іншому випадку поведінка як a / b, так і% b не визначена.
Зноска говорить про це:
Це часто називають "усіченням до нуля".
Відсікання до нуля означає, що абсолютне значення for a/b
дорівнює абсолютному значенню (-a)/b
для всіх, a
а це b
, в свою чергу, означає, що ваш код ідеально нормальний.
Модуло математика нескладна, але може бути контрінтуїтивною
Однак у вашого вчителя є пункт, що ви повинні бути обережними, адже той факт, що ви збираєте результат, насправді має вирішальне значення. Розрахувати a%b
за вищевикладеним визначенням математика нескладно, але це може суперечити вашій інтуїції. Для множення та ділення результат позитивний, якщо операнди мають рівний знак. Але коли мова йде про модуль, результат має той самий знак, що і перший операнд. Другий операнд зовсім не впливає на знак. Наприклад, 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), дійсний вхід -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]. Стандарт С11 5.2.4.2.1 LONG_MIN
часто-2147483648
але максимальне (так, максимальне, це від’ємне число), допустиме значення для LONG_MIN
є 2147483647
.
Які зміни я б вніс у ваш код?
Ваш код добре, як є, тому це насправді не скарги. Це більше схоже на те, що якщо мені справді, дійсно потрібно сказати що-небудь про ваш код, є деякі дрібниці, які можуть зробити його трохи крихітнішим.
- Назви змінних можуть бути трохи кращими, але це коротка функція, яку легко зрозуміти, тому це не є великою справою.
- Ви можете змінити умову з
n
на n!=0
. Семантично це 100% еквівалент, але це робить трохи зрозумілішим.
- Перемістіть декларацію
c
(яку я перейменував digit
) у внутрішню петлю, оскільки вона використовується лише там.
- Змініть тип аргументу,
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
як ви робили. Ця назва змінної не дає корисної інформації, але, з іншого боку, вона також не вводить в оману.
Але якщо чесно, в цей момент ви повинні перейти до більш важливої роботи. :)
n = n * (-1)
- смішний спосіб писатиn = -n
; Тільки академік би про це навіть подумав. Не кажучи вже про те, щоб додати зайві дужки.