Sida 1 av 1

Bra eller dålig programmering i C?

Postat: 23 apr 2012, 22:06
av Johnny Rosenberg
Håller på att traggla vidare i C här och stöter då och då på frågan om man verkligen bör göra det ena och det andra.

Exempelvis igår, när jag behövde göra en loop som skulle behandla värden i en matris. Problemet var av sådan natur att loopen ska bete sig lite olika beroende på om jag är precis i början av loopen, slutet eller i mitten. Givetvis kan man ha lite villkorssatser i loopen så att rätt sak görs, men jag tänkte att det nog är tidsödande om loopen går många varv (i mitt fall åtminstone några miljoner varv), så jag gjorde tre olika loopar istället som körs direkt efter varandra.

Det kan se ut ungefär så här:

Kod: Markera allt

int, MyInteger;
MyInteger=3491;
for(i=0; i<MyInteger; i++){
	DoSomething(i);
}
for(i=MyInteger; i<MyInteger*1000; i++){
	DoSomethingElse(i);
}
for(i=MyInteger*1000; i<MyInteger*1001; i++){
	DoSomethingStrange(i);
}
Men så kom jag på något, som kanske är självklart för de flesta, men som ”nybörjare på gamla dar” har man kanske ett handikapp, nämligen att varje ny for-sats sätter i till ett värde som i redan hade, så jag blev lite nyfiken och testade först:

Kod: Markera allt

int i=0, MyInteger=3491;
for(i=i; i<MyInteger; i++){
	DoSomething(i);
}
for(i=i; i<MyInteger*1000; i++){
	DoSomethingElse(i);
}
for(i=i; i<MyInteger*1001; i++){
	DoSomethingStrange(i);
}
Det visade sig fungera och raderna blev lite kortare, vilket jag uppskattar.
Men trots allt så utförs ändå ett onödigt jobb här, så jag testade att helt utelämna första parametern i for-satsen:

Kod: Markera allt

int i=0, MyInteger=3491;
for(; i<MyInteger; i++){
	DoSomething(i);
}
for(; i<MyInteger*1000; i++){
	DoSomethingElse(i);
}
for(; i<MyInteger*1001; i++){
	DoSomethingStrange(i);
}
Även detta visade sig funka.
Nu återstår då bara frågan om detta tillvägagångssätt är något man kan rekommendera. Det förutsätter ju att man inte petar in någon sats emellan två av for-satserna som ändrar värdet på i, men den lilla kollen får man ju ha, tycker jag nog.
En annan invändning kan väl vara att vissa kompilatorer kanske inte gillar att en parameter lämnats tom, men huruvida det finns sådana har jag ingen aning om.

Åsikter om detta mottages tacksamt. Är det risk att man blir (ännu mer) idiotförklarad av sådan typ av kod?

Kan i alla fall köpa att det kanske är olämpligt att utelämna ”i=0” i första loopen, särskilt om i inte deklareras omedelbart före loopen.

Re: Bra eller dålig programmering i C?

Postat: 24 apr 2012, 00:35
av mcNisse
Jag tycker den tredje varianten såg helt ok ut.

En evig loop i C kan skrivas som

Kod: Markera allt

for(;;) {
}

Re: Bra eller dålig programmering i C?

Postat: 24 apr 2012, 09:39
av Konservburk
Johnny Rosenberg skrev:men jag tänkte att det nog är tidsödande om loopen går många varv
Johnny Rosenberg skrev:Men trots allt så utförs ändå ett onödigt jobb här, så jag testade att helt utelämna första parametern i for-satsen:

Kod: Markera allt

int i=0, MyInteger=3491;
for(; i<MyInteger; i++){
	DoSomething(i);
}
for(; i<MyInteger*1000; i++){
	DoSomethingElse(i);
}
for(; i<MyInteger*1001; i++){
	DoSomethingStrange(i);
}
Om du nu oroar dig för "onödigt jobb" så vill du nog inte utföra samma multiplikation varje varv.

Kod: Markera allt

int i, x, MyInteger=3491;
for (i=0; i<MyInteger; i++) {
   DoSomething(i);
}
for (x=MyInteger*1000; i<x; i++) {
   DoSomethingElse(i);
}
for (x=MyInteger*1001; i<x; i++) {
   DoSomethingStrange(i);
}

Re: Bra eller dålig programmering i C?

Postat: 24 apr 2012, 17:59
av Johnny Rosenberg
Konservburk skrev:Om du nu oroar dig för "onödigt jobb" så vill du nog inte utföra samma multiplikation varje varv.

Kod: Markera allt

int i, x, MyInteger=3491;
for (i=0; i<MyInteger; i++) {
   DoSomething(i);
}
for (x=MyInteger*1000; i<x; i++) {
   DoSomethingElse(i);
}
for (x=MyInteger*1001; i<x; i++) {
   DoSomethingStrange(i);
}
Oj då, det tänkte jag inte på… Nej, du har rätt; det är ju rätt så onödigt. Det kanske inte gör så mycket, men alla bäckar små…
Det var ju rätt smart att räkna ut x inuti for-parantesen. Det går ju åt en variabel extra, så man får väl överväga om det är värt det eller inte…

Re: Bra eller dålig programmering i C?

Postat: 24 apr 2012, 18:00
av Johnny Rosenberg
mcNisse skrev:Jag tycker den tredje varianten såg helt ok ut.

En evig loop i C kan skrivas som

Kod: Markera allt

for(;;) {
}
Eller varför inte ta bort klamrarna också och ersätta dem med ett semikolon?

Kod: Markera allt

for(;;);
Eller är jag ute och cyklar?

Re: Bra eller dålig programmering i C?

Postat: 24 apr 2012, 18:18
av David Andersson
Givet dessa exempel:

Kod: Markera allt

int i, MyInteger=3491;
for(i=0; i<MyInteger; i++){
	DoSomething(i);
}
for(i=MyInteger; i<MyInteger*1000; i++){
	DoSomethingElse(i);
}
for(i=MyInteger*1000; i<MyInteger*1001; i++){
	DoSomethingStrange(i);
}

Kod: Markera allt

int i, MyInteger=3491;
for(i=0; i<MyInteger; i++){
	DoSomething(i);
}
for(; i<MyInteger*1000; i++){
	DoSomethingElse(i);
}
for(; i<MyInteger*1001; i++){
	DoSomethingStrange(i);
}
Jag tycker båda alternativen är bra på olika sätt.

Den första visar extra tydligt precis var varje del-loop börjar och slutar.

Den andra visar extra tydligt att varje del-loop fortsätter precis där föregående slutar.

Generellt, när olika programkod gör samma sak, vilken är bäst? Ibland kan man behöva välja det som kör fortast, men för det mesta ska man välja det som ger tydligast kod, i syfta att upptäcka/minimera antalet buggar. (Arbetar man i en organisation kan man behöva välja det chefen tycker är snyggast: kodningsregler.)

Så frågan är, vilken bugg är allvarligast? Att det slulle bli ett glapp eller överlapp (element hamnar mellan looparna eller ingår i två loopar) eller att gränserna mellan del-loopar eller längden av en del-loop inte är exakt som tänkt? Om programmet senare dividerar en summa med MyInteger så kanske det senare är viktigast, för att det ska bli matematiskt riktigt. Det är i alla fall viktigt om värdena är kylvattennivåer i ett kärnkraftverk. Men om det är ljud och looparna ändra nivån på varje sample så kommer glapp att höras mycket tydligare (som knäpp/knaster) än om dividerandet är en aning fel.

Jag är inte riktigt nöjd med ovanstående stycke. Man ska inte behöva väga buggar mot varandra. Om båda avikelserna är buggar bör man hitta ett sätt att skriva looparna så att både glapp och fel längd orsakar så allvarliga fel i utdata att det är oundvikligt att upptäcka dem under test-fasen:
Tom Hanks, comp.lang.python 15 aug 2003 skrev: Yeah, I hate "fail-silently" - it's much simpler to find errors if you
simply explode the rocket to draw attention to your typo. :)
Förslaget att flytta multiplikationen ut ur loopen hjälper tyvärr inte läsbarheten. Exempel:

Kod: Markera allt

int i, x, MyInteger=3491;
for (i=0; i<MyInteger; i++) {
   DoSomething(i);
}
for (x=MyInteger*1000; i<x; i++) {
   DoSomethingElse(i);
}
for (x=MyInteger*1001; i<x; i++) {
   DoSomethingStrange(i);
}
Om kompilatorn kan se att DoSomethingElse och DoSomethingStrange inte ändrar MyInteger så kan den själv optimera bort multiplikationen ur loopen.

Re: Bra eller dålig programmering i C?

Postat: 24 apr 2012, 18:35
av David Andersson
Johnny Rosenberg skrev: Det går ju åt en variabel extra, så man får väl överväga om det är värt det eller inte…
Skarpsinnig observation och ibland ett väldigt viktigt övervägande. Varje ny variabel i ett program kan innebära en mental belastning, om värdet representerar en tillfällig eller ologisk abstraktion. Men om variabelns namn är väl valt så kan en ny variabel innebära en mental avlastning som gör programmet mer lättbegripligt.

(Jag antar att du med "det går åt en variabel extra" inte menade att variabeln förbrukar 4 byte minne, när du redan har en annan variabel som använder flera miljoner byte.)

Re: Bra eller dålig programmering i C?

Postat: 24 apr 2012, 19:39
av Konservburk
David Andersson skrev:Om kompilatorn kan se att DoSomethingElse och DoSomethingStrange inte ändrar MyInteger så kan den själv optimera bort multiplikationen ur loopen.
Jag tycker också att det borde vara så, men när jag testade med void DoSomething(int i){} osv, så blev det i snitt 11% kortare körtid när multiplikationerna har lyfts ut jämfört med om de får vara kvar. (Kompilerat med gcc 4.4.5 och kört på en 500MHz ARM-processor).

Re: Bra eller dålig programmering i C?

Postat: 24 apr 2012, 20:47
av David Andersson
Konservburk skrev:
David Andersson skrev:Om kompilatorn kan se att DoSomethingElse och DoSomethingStrange inte ändrar MyInteger så kan den själv optimera bort multiplikationen ur loopen.
Jag tycker också att det borde vara så, men när jag testade med void DoSomething(int i){} osv, så blev det i snitt 11% kortare körtid när multiplikationerna har lyfts ut jämfört med om de får vara kvar.
Hmm. Har du rätt i. Men om jag kompilerar med -O (optimering, cc -O filnamn.c -o filnamn) så går looparna lika fort. (gcc 4.4.3)

Re: Bra eller dålig programmering i C?

Postat: 24 apr 2012, 22:32
av Johnny Rosenberg
Tackar för alla synpunkter och engagemang. Antar att man med mer erfarenhet framöver kanske får en bättre känsla för vad som är ”bäst” i olika situationer. Känner att jag fått lite mer ”kött på benen” när det gäller just denna fråga i alla fall.

Men det finns ju alltid något att fundera på, men det får bli nya trådar av det…
::)

Re: Bra eller dålig programmering i C?

Postat: 25 apr 2012, 14:54
av Konservburk
David Andersson skrev:Hmm. Har du rätt i. Men om jag kompilerar med -O (optimering, cc -O filnamn.c -o filnamn) så går looparna lika fort. (gcc 4.4.3)
Det tänkte jag inte ens på, men det stämmer ju förstås. Jag kompilerade helt enkelt med make filnamn, och då blev det utan optimering -O eftersom jag inte hade den flaggan i $CFLAGS.