귀하의 코드는 완벽합니다
당신은 절대적으로 정확하고 선생님이 잘못되었습니다. 결과에 전혀 영향을 미치지 않기 때문에 추가 복잡성을 더할 이유가 전혀 없습니다. 버그도 소개합니다. (아래 참조)
첫째, n
0 이면 별도의 검사 는 분명히 완전히 불필요하며 이는 매우 쉽게 실현됩니다. 솔직히 말해서, 나는 선생님이 이것에 대해 이의가 있다면 실제로 선생님의 능력에 의문을 제기합니다. 그러나 나는 모든 사람들이 때때로 뇌 방귀를 가질 수 있다고 생각합니다. 그러나 추가 라인 비용을 들이지 않고도 약간의 선명도를 추가 while(n)
하기 while(n != 0)
때문에 변경해야 한다고 생각합니다 . 그래도 사소한 일입니다.
두 번째는 좀 더 이해하기 쉽지만 여전히 잘못되었습니다.
이것이 C11 표준 6.5.5.p6의 내용입니다.
몫 a / b가 표현 가능한 경우, 식 (a / b) * b + a % b는 a와 같아야합니다. 그렇지 않으면 a / b 및 a % b의 동작이 정의되지 않습니다.
각주는 이렇게 말합니다.
이것을 종종 "0으로 자르기"라고합니다.
0으로 잘림은의 절대 값 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
AND 이고 아키텍처가 2의 보수이고 부호 비트가 1이고 모든 값 비트가 0 인 비트 패턴이 트랩 값이 아닌 경우 (트랩 값없이 2의 보수를 사용하는 것이 매우 일반적 임) 교사의 코드는 정의되지 않은 동작을 생성합니다. 줄에 n = n * (-1)
. 당신의 코드는 그의 코드 보다 약간 더 좋습니다 . 코드를 불필요하게 만들고 작은 제로 값을 얻음으로써 작은 버그를 도입하는 것을 고려할 때 코드가 훨씬 우수하다고 말합니다.
즉, INT_MIN = -32768 인 컴파일에서 (결과 함수가 <-32768 또는> 32767 인 입력을 수신 할 수 없지만) -32768 의 유효한 입력은-(-32768i16)의 결과로 인해 정의되지 않은 동작을 유발합니다. 16 비트 정수로 표현할 수 없습니다. (-32768i16)은 일반적으로 -32768i16으로 평가되고 프로그램은 음수를 올바르게 처리하기 때문에 실제로 -32768은 잘못된 결과를 발생시키지 않습니다. (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 % 동일하지만 조금 더 명확합니다.
- while 루프 내부에서만 선언 된
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
가 음수 값을 얻을 수 있지만 숫자 자체는 양수 또는 음수 가 아니기 때문에 약간 오해의 소지가 있습니다. 이 방법에는 몇 가지 방법이 있지만 이것은 정말 nitpicking이므로 작은 세부 사항은 신경 쓰지 않습니다. 특히 마지막 자리에 대한 별도의 기능이 너무 멀리 걸립니다. 아이러니하게도, 이것은 교사 코드가 실제로 해결하는 것 중 하나입니다.
- 변수를 변경
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
; 학계 만이 생각할 것입니다. 중복 괄호를 추가하지 마십시오.