您的代码完全没问题
你完全正确,你的老师错了。完全没有理由增加额外的复杂性,因为它根本不会影响结果。它甚至引入了一个bug。(见下文)
首先,单独检查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的绝对值,这反过来意味着您的代码完全没问题。
模是简单的数学,但可能违反直觉
However, your teacher does have a point that you should be careful, because the fact that you're squaring the result is actually crucial here. Calculating a%b according to above definition is easy math, but it might go against your intuition. For multiplication and division, the result is positive if the operands have equal sign. But when it comes to modulo, the result has the same sign as the first operand. The second operand does not affect the sign at all. For instance, 7%3==1 but (-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
讽刺的是,你的老师用错误来证明他的观点。
你老师的代码有缺陷
Yes, it actually is. If the input is INT_MIN AND the architecture is two's complement AND the bit pattern where the sign bit is 1 and all value bits are 0 is NOT a trap value (using two's complement without trap values is very common) then your teacher's code will yield undefined behavior on the line n = n * (-1). Your code is - if ever so slightly - better than his. And considering introducing a small bug by making the code unnecessary complex and gaining absolutely zero value, I'd say that your code is MUCH better.
In other words, in compilations where INT_MIN = -32768 (even though the resulting function cannot receive an input that is < -32768 or > 32767), the valid input of -32768 causes undefined behavior, because the result of -(-32768i16) cannot be expressed as a 16-bit integer. (Actually, -32768 probably would not cause an incorrect result, because -(-32768i16) usually evaluates to -32768i16, and your program handles negative numbers correctly.) (SHRT_MIN could be -32768 or -32767, depending on the compiler.)
But your teacher explicitly stated that n can be in the range [-10^7; 10^7]. A 16-bit integer is too small; you would have to use [at least] a 32-bit integer. Using int might seem to make his code safe, except that int is not necessarily a 32-bit integer. If you compile for a 16-bit architecture, both of your code snippets are flawed. But your code is still much better because this scenario reintroduces the bug with INT_MIN mentioned above with his version. To avoid this, you can write long instead of int, which is a 32-bit integer on either architecture. A long is guaranteed to be able to hold any value in the range [-2147483647; 2147483647]. C11 Standard 5.2.4.2.1 LONG_MIN is often -2147483648 but the maximum (yes, maximum, it's a negative number) allowed value for LONG_MIN is -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;
}
实际上,这可能会有一点误导,因为-如上所述-可变数字可以得到负值,但数字本身永远不会是正数或负数。有一些方法可以解决这个问题,但这真的是吹毛求疵,我不关心这些小细节。特别是最后一位的单独函数太过分了。具有讽刺意味的是,这是你的教师代码实际上解决的问题之一。
Change sum += (digit * digit) to sum += ((n%10)*(n%10)) and skip the variable digit completely.
Change the sign of digit if negative. But I would strongly advice against making the code more complex just to make a variable name make sense. That's a VERY strong code smell.
Create a separate function that extracts the last digit. int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; } This is useful if you want to use that function somewhere else.
Just name it c as you originally do. That variable name does not give any useful information, but on the other hand, it's not misleading either.
但说实话,在这一点上你应该转向更重要的工作。:)