您的代码非常好
你是绝对正确的,你的老师是错误的。绝对没有理由增加这种额外的复杂性,因为它根本不影响结果。它甚至引入了一个错误。(见下文)
首先,n
显然完全不需要单独检查if 是否为零,这很容易实现。老实说,如果您的老师对此有异议,我实际上会质疑他的能力。但是我想每个人都会时不时放屁。但是,我确实while(n)
应该将其更改为,while(n != 0)
因为它增加了一点额外的清晰度,甚至不需要花费额外的时间。不过这是小事。
第二个更容易理解,但他仍然错了。
这就是C11标准6.5.5.p6所说的:
如果商a / b是可表示的,则表达式(a / b)* b + a%b等于a;否则,a / b和a%b的行为均未定义。
脚注说:
这通常称为“向零截断”。
截断为零意味着for的绝对值a/b
等于(-a)/b
all 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位整数。(实际上,-32768可能不会导致错误的结果,因为-(-32768i16)通常计算结果为-32768i16,并且您的程序正确处理了负数。)(取决于编译器,SHRT_MIN可以是-32768或-32767。)
但是您的老师明确指出n
可以在[-10 ^ 7; 10 ^ 7]。16位整数太小;您将必须使用[至少] 32位整数。使用int
似乎可以使他的代码安全,除非那int
不一定是32位整数。如果针对16位体系结构进行编译,则您的两个代码段都存在缺陷。但是您的代码仍然要好得多,因为这种情况重新引入了上述错误INT_MIN
及其版本。为了避免这种情况,您可以编写,long
而不是int
,这是任一体系结构上的32位整数。long
保证A 可以保存[-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
像最初一样命名即可。该变量名不会提供任何有用的信息,但是另一方面,它也不会引起误解。
但老实说,在这一点上,您应该继续进行更重要的工作。:)
n = n * (-1)
是一种荒谬的写作方式n = -n
; 只有一个学者甚至会想到它。更不用说添加多余的括号了。