Kodunuz gayet iyi
Kesinlikle haklısınız ve öğretmeniniz yanlış. Sonucu hiç etkilemediğinden, bu ekstra karmaşıklığı eklemek için kesinlikle hiçbir neden yoktur. Hatta bir hata bile veriyor. (Aşağıya bakınız)
İlk olarak, eğer n
sıfır olup olmadığını kontrol etmek kesinlikle gereksizdir ve bunun gerçekleştirilmesi çok kolaydır. Dürüst olmak gerekirse, aslında öğretmenlerinizin bu konuda itirazları olup olmadığını sorgularım. Ama sanırım herkes zaman zaman bir beyin osuruğuna sahip olabilir. Ancak, bunun while(n)
değiştirilmesi gerektiğini düşünüyoruz, while(n != 0)
çünkü ekstra bir çizgiye bile mal olmadan biraz ekstra netlik katıyor. Yine de küçük bir şey.
İkincisi biraz daha anlaşılır, ama yine de yanlış.
Bu nedir C11 standart 6.5.5.p6 diyor ki:
A / b bölümü gösterilebiliyorsa, (a / b) * b + a% b ifadesi a; aksi takdirde, hem a / b hem de% b davranışları tanımlanmamıştır.
Dipnot şunu söylüyor:
Buna genellikle "sıfıra doğru kesme" denir.
Sıfır aracına doğru Kesme için mutlak değer olduğunu a/b
mutlak değerine eşit (-a)/b
herkes için a
ve b
dönüş araçlarında kodunuzu mükemmel ince olduğunu.
Modulo kolay matematiktir, ancak sezgisel olabilir
Ancak, öğretmeninizin dikkatli olmanız gerektiği bir noktası var, çünkü sonucu karelemeniz aslında burada çok önemlidir. a%b
Yukarıdaki tanımlamaya göre hesaplamak kolay matematiktir, ancak sezginize aykırı olabilir. Çarpma ve bölme için, işlenenler eşittir işaretine sahipse sonuç pozitiftir. Ancak modulo söz konusu olduğunda, sonuç ilk işlenenle aynı işarete sahiptir . İkinci işlenen işareti hiç etkilemez. Mesela, 7%3==1
ama (-7)%(-3)==(-1)
.
İşte bunu gösteren bir pasaj:
$ 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
Yani, ironik bir şekilde, öğretmeniniz yanlış olduğunu kanıtladı.
Öğretmeninizin kodu hatalı
Evet, aslında öyle. Eğer giriş INT_MIN
VE ise mimari ikinin tamamlayıcısı VE işaret bitinin 1 olduğu ve tüm değer bitlerinin 0 olduğu bit deseni ise bir tuzak DEĞİLDİR (ikisinin tuzak değerleri olmadan tamamlayıcısını kullanmak çok yaygındır), o zaman öğretmeninizin kodu tanımlanmamış davranış verecektir hatta n = n * (-1)
. Kodunuz - eğer çok azsa - ondan daha iyidir. Ve kodu gereksiz karmaşık hale getirerek ve kesinlikle sıfır değer kazanarak küçük bir hata getirmeyi düşününce, kodunuzun ÇOK daha iyi olduğunu söyleyebilirim.
Başka bir deyişle, INT_MIN = -32768 (sonuçta ortaya çıkan işlev <-32768 veya> 32767 olan bir girdi alamıyor olsa da) derlemelerde, geçerli -32768 girdisi tanımlanmamış davranışa neden olur, çünkü - (- 32768i16) 16 bit tam sayı olarak ifade edilemez. (Aslında, -32768 muhtemelen yanlış bir sonuca neden olmaz, çünkü - (- 32768i16) genellikle -32768i16 olarak değerlendirilir ve programınız negatif sayıları doğru şekilde işler.) (SHRT_MIN, derleyiciye bağlı olarak -32768 veya -32767 olabilir.)
Ancak öğretmeniniz açıkça n
[-10 ^ 7; 10 ^ 7]. 16 bitlik bir tam sayı çok küçük; en azından 32 bitlik bir tam sayı kullanmanız gerekir. Kullanmak int
, kodunu güvenli hale getirebilir, ancak int
32 bitlik bir tam sayı olması gerekmez. 16 bit mimari için derlerseniz, kod snippet'lerinizin her ikisi de kusurludur. Ancak kodunuz hala çok daha iyi çünkü bu senaryo, INT_MIN
yukarıda belirtilen sürümü ile birlikte hatayı yeniden ortaya koyuyor. Bunu önlemek için, yazabilir long
yerine int
ya mimarisine bir 32 bitlik tamsayı olan. A'nın long
, [-2147483647 aralığında herhangi bir değeri tutabileceği garanti edilmektedir; 2147483647]. C11 Standart 5.2.4.2.1 LONG_MIN
genellikle-2147483648
ancak maksimum izin değeri (evet, maksimum, negatif bir sayıdır)LONG_MIN
olduğunu 2147483647
.
Kodunuzda ne gibi değişiklikler yapardım?
Kodunuz olduğu gibi iyidir, bu yüzden bunlar gerçekten şikayet değildir. Kodunuz hakkında gerçekten bir şey söylemem gerekiyorsa, bunu biraz daha net hale getirebilecek bazı küçük şeyler var.
- Değişkenlerin isimleri biraz daha iyi olabilir, ancak anlaşılması kolay kısa bir işlevdir, bu yüzden önemli değildir.
- Koşulu olarak olarak
n
değiştirebilirsiniz n!=0
. Anlamsal olarak,% 100 eşdeğerdir, ancak biraz daha açık hale getirir.
c
(Yeniden adlandırdığım digit
) bildirimini while döngüsü içinde yalnızca orada kullanıldığından taşıyın .
long
Girdi kümesinin tamamını işleyebilmesini sağlamak için bağımsız değişken türünü değiştirin .
int sum_of_digits_squared(long n)
{
long sum = 0;
while (n != 0) {
int digit = n % 10;
sum += (digit * digit);
n /= 10;
}
return sum;
}
Aslında, bu biraz yanıltıcı olabilir, çünkü - yukarıda belirtildiği gibi - değişken digit
negatif bir değer alabilir, ancak bir rakam kendi başına asla pozitif veya negatif değildir. Bu konuda birkaç yol var, ama bu gerçekten çirkin, ve ben böyle küçük detaylar umurumda değil. Özellikle son basamağın ayrı işlevi çok ileri gidiyor. İronik olarak, bu öğretmenlerin kodlarının aslında çözdüğü şeylerden biridir.
- Değişim
sum += (digit * digit)
için sum += ((n%10)*(n%10))
ve değişken atlamak digit
tamamen.
digit
Negatif ise işaretini değiştirin . Ama sadece bir değişken mantıklı yapmak için kodu daha karmaşık hale karşı şiddetle tavsiye ediyorum. Bu ÇOK güçlü bir kod kokusu.
- Son basamağı ayıklayan ayrı bir işlev oluşturun.
int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; }
Bu işlevi başka bir yerde kullanmak istiyorsanız bu yararlıdır.
- Sadece
c
başlangıçta yaptığınız gibi adlandırın. Bu değişken adı yararlı bilgi vermez, ancak diğer yandan da yanıltıcı değildir.
Ama dürüst olmak gerekirse, bu noktada daha önemli çalışmalara geçmelisiniz. :)
n = n * (-1)
yazmak için gülünç bir yoln = -n
; Sadece bir akademisyen bunu bile düşünebilirdi. Gereksiz parantezleri ekleyelim.