Dienstag, 7. Juni 2011
Fehlerbehandlung
RemoteExceptionsonst nichts. Keine weitere Information, um dem Problem auf die Schliche zu kommen.
public class Server {
public static void main(String[] args) {
try {
LocateRegistry.createRegistry(1099);
System.out.println("RMI-Registry erfolgreich gestartet");
} catch (RemoteException ex) {
System.out.println("Fehler beim Starten der Registry: " + ex);
}
try {
DB db = new Data("fahrten.db");
//System.out.println(((Data)db).records.size());
DB d = (DB) UnicastRemoteObject.exportObject(db, 1099);
System.out.println("1xx");
Naming.rebind("Data", d);
System.out.println("2xx");
Header he = ((Data)db).getHeader();
Header h = (Header) UnicastRemoteObject.exportObject(he, 1099);
Naming.rebind("Header", h);
System.out.println("Alles gebunden");
} catch (RemoteException ex) {
System.err.println("RemoteException");
} catch (MalformedURLException ex) {
System.err.println("MalformedURLException");
}
}
}
Ein einfaches System.err.println("RemoteException: " + ex.getMessage()); liefert schon etwas mehr hilfreiche Information;RemoteException: remote object implements illegal remote interface; nested exception is:
java.lang.IllegalArgumentException: illegal remote method encountered: public abstract int data.Header.length()
In der Klasse Data findet manpublic class Data implements DB, Serializable {
private final int MAGIC_COOKIE = 4223;
private Header header;
private String fileName="";
private ArrayList lockedRecNo;
private int DataOffset; //166
Wenn man weiter sucht, findet man für Header das Interfacepublic interface Header extends Remote {
public Field getField(int fID);
public int length();
public void addField(Field f);
}
Es fehlt schlicht und ergreifend die Implementierung zu Header. Das kann nicht funktionieren!Die Implementierung muss
UnicastRemoteObject erweitern, in Data sollte man statt private Header header; direkt die Implementierung verwenden: private Fields header;.Die Implementierung von Fields beginnt etwa so:
public class Fields extends UnicastRemoteObject implements Header, Serializable {
private ArrayList felder;
public Fields() throws RemoteException {
felder = new ArrayList(0);
}
...
Unser Server sollte dann etwa so aussehen (ob das dann funktioniert, sei dahingestellt, aber die Exceptions sind geklärt):public class Server {
public static void main(String[] args) {
DB db = null;
try {
db = new Data("fahrten.db");
} catch (RemoteException ex) {
System.err.println("RemoteException (new Data()): " + ex.getMessage());
}
try {
LocateRegistry.createRegistry(1099);
System.out.println("RMI-Registry erfolgreich gestartet");
} catch (RemoteException ex) {
System.out.println("Fehler beim Starten der Registry: " + ex);
}
try {
DB d = (DB) UnicastRemoteObject.exportObject(db, 1099);
Naming.rebind("Data", d);
} catch (RemoteException ex) {
System.err.println("RemoteException (rebind(Data)): " + ex.getMessage());
} catch (MalformedURLException ex) {
System.err.println("MalformedURLException: " + ex.getMessage());
}
try {
Fields he = (Fields) ((Data) db).getHeader();
Header h = (Header) UnicastRemoteObject.exportObject(he, 1099);
try {
Naming.rebind("Header", (Fields) h);
} catch (MalformedURLException ex) {
System.err.println("MalformedURLException (rebind(Header)): " + ex.getMessage());
}
} catch (RemoteException ex) {
System.err.println("RemoteException: " + ex.getMessage());
}
System.out.println("Alles gebunden");
}
}
Der langen Rede kurzer Sinn:Für den Programmierer muss möglichst viel Information bereitgestellt werden. Für den Anwender hilft diese Information nichts und man muss einfache Fehlermeldungen anbieten (das ist hier auf dieser Ebene sowieso nicht möglich, denn die Software ist noch weit davon entfernt, einem Endanwender seine Dienste anzubieten).
Sinnvoll wäre es, die Exceptions zu loggen.
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);Am schlimmsten sind jedoch solche Konstrukte:
try {
while(true) {
records.add(read(nr));
nr++;
}
} catch (RemoteException ex) {
} catch (RecordNotFoundException ex) {
}
Fehler sind praktisch unauffindbar. Auch nicht mit dem Debugger, weil man nicht einmal einen Breakpoint setzen kann, um die Variable ex zu inspizieren (die enthält ja Infos zur Exception).Nachsatz: das alles kostet mich Stunden, die ich sinnvoller verbringen könnte. Einfach den Kandidaten durchfallen lassen, denn die Unit-Tests sind zu 93% (einer von dreizehn hat funktioniert) schief gegangen.
Labels: allgemeines, Fehler, Java
Montag, 6. Juni 2011
Autoboxing in Java
Folgendes Beispiel zeigt ein Problem beim Autoboxing in Java. Der Autor des Code-Fragments will int-Werte in der LinkedList speichern. Das funktioniert beim Speichern in Zeile 16 auch super, weil der int-Wert recNo automatisch in ein Integer-Objekt gepackt wird.
LinkedList<Integer> lockedRecords = new LinkedList<Integer>();
public long lock(int recNo) throws RemoteException,
RecordNotFoundException {
long lockCookie = 0;
if(existsRec(recNo)){
synchronized(records.get(recNo)){
while(lockedRecords.contains(recNo)){
try {
records.get(recNo).wait();
} catch (InterruptedException ex) {
System.err.println("Lock Interrupted!");
}
}
if(existsRec(recNo)){
lockedRecords.add(recNo);
boolean lock_getted = true;
lockCookie = lockedRecords.size() -1;
return lockCookie;
}
}
}else{
throw new RecordNotFoundException();
}
return lockCookie;
}
public void unlock(int recNo, long lockCookie) throws RemoteException,
RecordNotFoundException, SecurityException {
if(recNo > 0 && recNo < records.size()){
if(lockCookie >= 0 && lockCookie < lockedRecords.size()
&& lockedRecords.contains((int)lockCookie)){
synchronized(records.get(recNo)){
lockedRecords.remove(recNo);
records.get(recNo).notifyAll();
}
}else{
throw new SecurityException();
}
}else{
throw new RecordNotFoundException();
}
}
unlock() stürzt (praktisch) immer in Zeile 34 ab! Und zwar mit einer IndexOutOfBoundsException.
Warum?
Die Klasse LinkedList (sowie viele andere Klassen des Java Collection Frameworks) besitzt zwei remove()-Methoden:
E remove(int index), welche das Element mit dem Index index entfernt und eineIndexOutOfBoundsExceptionwirft, wenn der Index nicht existiert.- boolean remove(Object o), welche das entsprechende Objekt entfernt, falls es existiert (und in diesem Fall
trueliefert).
int-Parameter wird nicht automatisch in ein Integer-Objekt verpackt, da zuerst die zu int passende Methode verwendet wird! E remove(int index) wirft aber eine IndexOutOfBoundsException, wenn der int recNo kein gültiger Index ist. Das wäre eher zufällig passend.
Autoboxing ist also im Allgemeinen sehr praktisch, kann aber zu unerwarteten Problemen führen.
Übrigens ist eine Lösung mit Verwendung von Integer nicht zielführend, da beim Autoboxing nur bei kleinen Werten tatsächlich dasselbe Objekt verwendet wird:
Integer i1 = 23; Integer i2 = 23; Integer i3 = 2132321423; Integer i4 = 2132321423; System.out.println(i1 == i2); System.out.println(i1.equals(i2)); System.out.println(i3 == i4); System.out.println(i3.equals(i4));liefert:
true true false true
Samstag, 4. Juni 2011
Programmierrichtlinien haben doch Sinn (II)
public int[] find(String[] criteria) throws RemoteException {
ArrayList erg = new ArrayList();
for(int i=0; i< records.size();i++){
boolean match=true;
if(criteria != null){
for(int j=0; j< criteria.length;i++){
if(criteria[j] != null){
if(!records.get(i).getField(j).contains(criteria[j])){
match=false;
break;
} } } }
if(records.get(i).isDeleted()){
match=false;
}
if(match){
erg.add(i);
}
}
int[] back = new int[erg.size()];
for(int i=0; i< erg.size();i++){
back[i]=(int)erg.get(i);
}
return back;
}
Wo ist der Fehler? (Abgesehen von den schlecht formatierten schließenden Klammern in Zeile 11)Labels: allgemeines, Fehler, Java
Donnerstag, 2. Juni 2011
Programmierrichtlinien haben doch Sinn
Eine Stunde Fehlersuche! Suche in einem fremden Code, der gewisse Anforderungen erfüllen muss. Der Unit-Test für eine Methode, die einen CSV-String liefern ist fehlgeschlagen. Das Feld an der Position 5 war immer leer. Der Test erwartete den String "42995";"47.01708,16.93225";"20.73708,29.96312";" 196";"201105132101";" 405";"9634090160";"O", die Methode lieferte jedoch 42995;"47.01708,16.93225";"20.73708,29.96312"; 196;201105132101;"";9634090160;"O"
OK, dieser "Fehler" war schnell gefunden. Die Methode hielt sich besser an das CSV-Format (nur Strings sind in Hochkomma eingeschlossen, Zahlen nicht) als mein Test. Das war schnell korrigiert und nun lieferte die Methode alles als String:
"42995";"47.01708,16.93225";"20.73708,29.96312";" 196";"201105132101";"";"9634090160";"O"
Fast richtig. Das ist ein wirklicher Fehler. Durchforsten und Debuggen hat ergeben, dass dieses Feld schon beim Lesen immer leer wird. Der Code dazu ist folgender (das Leerzeichen bei den Bedingungen "kleiner als" habe ich für den Blog hinzugefügt, damit da nicht ein ungültiges HTML-Tag ensteht, grundsätzlich erschwert das Fehlen solcher Leerzeichen das Lesen des Codes):
public Data(String fn) throws FileNotFoundException, IOException{
fileName=fn;
rf = new RandomAccessFile(fn, "rw");
int mC=rf.readInt();
if(mC==4223){
int dataOffset=0;
short count=0;
dataOffset=rf.readInt();
firstDataOffset=dataOffset;
count=rf.readShort();
information = new Header(count);
for(int i=0;i< count;i++){
short fNameLength = rf.readShort();
byte[] roughData = new byte[fNameLength];
rf.read(roughData/*, (int)rf.getFilePointer(), fNameLength*/);
String fieldName = new String(roughData,"ISO-8859-1");
short fDescLength = rf.readShort();
roughData = new byte[fDescLength];
rf.read(roughData/*, (int)rf.getFilePointer(), fDescLength*/);
String fieldDescription = new String(roughData,"ISO-8859-1");
char type = (char)rf.readByte();
short fDataLength = rf.readShort();
Field f = new Field(fNameLength, fieldName, fDescLength, fieldDescription, type, fDataLength);
information.setHeaderField(i, f);
}
records = new ArrayList();
while(dataOffset< rf.length()){
short flag = rf.readShort();
String[] data = new String[information.getLength()];
for(int i=0;i< data.length;i++){
int readLen = information.getField(i).length;
byte[] roughData = new byte[readLen];
rf.read(roughData/*, dataOffset, readLen*/);
switch(information.getField(i).type){
case'f':
case'F':
data[i] = new String(roughData,"ISO-8859-1");
break;
case'v':
case'V':
StringBuilder sb = new StringBuilder();
for(int j=0;i< readLen && (char)roughData[j]!='\0';j++){
sb.append((char)roughData[j]);
}
data[i] = sb.toString();
break;
case'c':
case'C':
byte[] d = new byte[1];
d[0]=roughData[0];
data[i] = new String(d);
break;
}
dataOffset+=(2+readLen);
}
if(flag==0){
records.add(new RecordData(data, true, information));
}else{
records.add(new RecordData(data, false, information));
}
}
rf.close();
information.recAnz = records.size();
}else{
throw new FileNotFoundException("Keine DB-Datei");
}
}
Soviel konnte ich aufgrund der Rahmenbedingungen auch gleich herausfinden: das Feld mit Index 5 ist ein "V"-Feld, daher ist der Code ab Zeile 48 relevant. Der schaut eigentlich richtig aus.
Erstaunlich ist jedoch, dass die "V"-Felder davor korrekt waren, das "F"-Feld auch. Aber das fehlerhafte "V"-Feld ist gleich nach dem ersten "F"-Feld. Daher war meine Hypothese: das "F"-Feld "erzeugt" den Fehler. Aber die Zeilen 44 und 45 sind korrekt.
Also nächste Hypothese: das Lesen der Daten ist fehlerhaft! Zeilen 38 bis 40. Aber die sind m.E. korrekt. Vielleicht wurde der Dateiheader falsch gelesen (dort sind die Feldlängen und Feldtypen spezifiziert).
Zeilen 5 bis 32. Aber auch dort ist nichts verdächtiges. Es ist schon zum Verzweifeln! Alles schaut richtig aus und trotzdem schlägt der Test fehl (zu Recht, denn das Ergebnis ist falsch).
Jetzt ist einmal Zurücklehnen angesagt. Das ganze mal von der Entfernung betrachten. Breakpoint auf Zeile 48 setzen und schauen, was sich tut.
Erst beim 5. Feld (0-basiert) tritt der Fehler auf. Die Schleife wird sofort verlassen, obwohl roughData tatsächlich mehr als 0 Bytes enthält, 5, um genau zu sein. readLen enthält sogar den richtigen Wert. Wie so zum Teufel ist dann die Bedingung j < readLen && roughData[j] != 0 nicht erfüllt (das Casten auf (char) hatte ich schon entfernt, weil '\0' tatsächlich den Wert 0 hat)?
Ich habe bei dem Ausdruck Leerzeichen eingebaut, die im Original nicht waren. Jetzt fällt es wie Schuppen aus den HaarenAugen! Im Original auf Zeile 49 steht i < roughData && roughData[j] != 0. i statt j - optisch fast nicht zu unterscheiden!
i zählt die Felder, j die einzelnen Bytes in einem Feld. Daher ist ab dem Feld 5, welches eine Länge von 5 hat, der erste Teil der Bedingung nicht mehr erfüllt und es wird ein leerer String erzeugt.
Ja, Programmieranfängern wird immer gepredigt: "sprechende" Namen verwenden!
Das hätte geholfen! Statt i könnte man z.B. fieldno verwenden und statt j wäre byteno angebracht.
Damit wäre der Fehler nie passiert! Die Schleife hätte gleich falsch ausgesehen:
for(int byteno=0;fieldno< readLen && roughData[byteno]!=0;byteno++){
sb.append((char)roughData[byteno]);
}
Wenn dann noch Leerzeichen dazwischen sind, springt der Fehler sofort ins Auge, denn warum wird in der Schleife einmal fieldno und einmal byteno verwendet?
Selbst wenn man in kurzen Schleifen i und j als Laufvariable erlaubt, dann müsste aber in der äußeren Schleife immer noch ein "sprechender" Name verwendet werden. Das stäche auch ins Auge (die Leerzeichen verbessern das auch noch!):
for (int i = 0; fieldno < readLen && roughData[i] != 0; i++){
sb.append((char)roughData[byteno]);
}
Wenn schon kurze Laufvariable, dann besser nie i und j gemeinsam verwenden sondern "unterschiedlichere" Buchstaben wie i und k oder i und m. Die kann man nicht so leicht verwechseln!
Die Variante mit "sprechenden" Namen für Schleifen, die länger als ein paar Zeilen sind, und kurze Laufvariable für kurze Schleifen (Dreizeiler) ist die beste Möglichkeit, dann das Beispiel mit i und fieldno schaut gleich irgendwie falsch aus. Zumindest schaut man sich so etwas gleich näher an.
Leerzeichen und sprechende Namen bringen's!
Siehe auch Programmierrichtlinien allgemein. Dort steht leider nichts über die Verwendung von Leerzeichen.
Java Guidelines findet man z.B. hier: http://www.oracle.com/technetwork/java/codeconv-138413.html
Google findet auch etwas: www.google.com
Labels: allgemeines, Fehler, Java
Donnerstag, 3. Juni 2010
IDEs und Projektverzeichnisse und wie Maturanten damit umgehen
- bei einem einen Java-Decompiler verwenden, damit ich überhaupt praktisch mit dem Programm arbeiten und mir Fehler genauer betrachten kann, denn dieser Kandidat hat nur die
*.class-Dateien ins Abgabe-Archiv gepackt (Eclipse-Projekt). - bei 6 (sechs) ich die
*.java-Dateien mühsam wieder in die richtige Paket-Struktur bringen, teilweise nur durch händisches Anlegen der Klassen und dann Kopieren der entsprechenden Textstellen aus der einen Textdatei, in der alle Klassen zusammengefasst waren (alles Netbeans-Projekte). - bei den restlichen 10 konnte ich das Projektverzeichnis unmittelbar verwenden (4 Eclipse-, 5 Netbeans-Projekte bzw. ganz ohne Meta-Information der IDE).
*.java-Dateien!*.java-Dateien waren im übergeordneten Verzeichnis zu finden. Die Verzeichnisse mit den Sourcen beginnen alle mit einem Leerzeichen und müssen erst wieder umbenannt werden:Das Verzeichnis, das mit "-brz" endet, enthält das Projekt, aber eben ohne die Sourcen.
Ich denke, man muss schon ziemlich Hand anlegen, um aus den Projektverzeichnissen diese kaputten Projekte zu erzeugen.
Ich frage mich, was so kompliziert ist, wenn es heißt: "Das gesamte Projekt-Verzeichnis (
Name-brz) in eine Jar-Datei (Name-brz.jar) packen und ins Abgabeverzeichnis kopieren (Name-brz.jar enthält also u.a. die „ausführbare“ Datei brz.jar)."Die Angabe für das Projekt war so:
"Verwenden Sie als Standard-Encoding UTF-8 (Unicode)!
Nennen Sie das Projekt Name-brz, wobei Name Ihr Familienname ist.
Erstellen Sie eine geeignete Paket-Struktur.
Erstellen Sie die Verzeichnisse
build und doc. Im Verzeichnis build muss das fertige Programm brz.jar abgelegt werden. In doc muss die generierte JavaDoc abgelegt werden. Legen Sie im Projektverzeichnis eine Datei readme.txt an, die Hinweise zum Erzeugen von brz.jar enthält.brz.jar soll alle nötigen Informationen zum Betrieb mit Ausnahme der Datenbankdateien enthalten, d.h. brz.jar soll in jedem beliebigen Verzeichnis aufgerufen werden können.Programmaufruf: über ein Argument der Kommandozeile soll festgelegt werden, ob das Programm als Server (Argument
server), Client (Argument client) oder AdminClient (Argument admin) läuft.Erstellen Sie ein Ant-Script, mit dem die Applikation und die Dokumentation (neu) erstellt werden kann."
Es durfte frei zwischen Eclipse und Netbeans gewählt werden.
Die Leute haben 5 oder mehr Jahre Programmieren hinter sich. Und immer wieder Tests in dieser Arbeitsumgebung (Java unter Linux). Ich verstehe das nicht!
Diese Umstände kosteten bis jetzt ein paar Stunden Arbeit (überhaupt, wenn man diesen Blog-Eintrag mitrechnet). Wirklich korrigiert habe ich noch keine der Arbeiten.
Labels: allgemeines, eclipse, Fehler, netbeans, PR5
Mittwoch, 26. Mai 2010
Softwarezuverlässigkeit - "Warum explodierten Mariner 1, Ariane 5, ..." oder: "Was kümmern mich die Probleme der Datenverarbeitung"
Labels: allgemeines, Fehler, Links
Freitag, 19. März 2010
Probleme mit python's random
random (ähnliche Module, Klassen oder Libraries gibt es im Prinzip für alle Programmiersprachen). Dahinter steckt keine echte Zufallszahlenerzeugung sondern vielmehr eine Funktion, welche Pseudozufallszahlen liefert, die sich statistisch wie Zufallszahlen verhalten.random.seed(), z.B. random.seed(23). Damit wird der Zufallsgenerator mit diesem Wert initialisiert. Bei selbem seed bekommt man immer die selbe Folge von Pseudozufallszahlen. Verwendet man diese Funktion nicht, so wird (zumindest bei Python) automatisch die Systemzeit als Anfangswert genommen. Damit kann man die Folge nicht vorhersagen (für einen bestimmten Zeitpunkt schon, dann nimmt man diesen Zeitstempel einfach als seed).random:>>> from random import seed, random
>>> seed("23")
>>> random()
0.41111702255082994
>>> from random import seed, random
>>> seed("23")
>>> random()
0.09256672412670508
long, int) oder jedes "hashable" Objekt verwenden kann. Und dort dürfte das Problem liegen:>>> "23".__hash__()
6400038450057767
>>> "23".__hash__()
308105767
>>> seed(23)
>>> random()
0.92486525162594524
>>> seed(23)
>>> random()
0.92486525162594524
Labels: allgemeines, Fehler, Python
Montag, 15. Februar 2010
eclipse und cvs
Ich habe herausgefunden, was falsch ist, aber ich weiß noch nicht, warum das passiert. Es gibt in einem Java-Projekt zwei Konfigurationsdateien
.project und .classpath. In beiden Dateien treten beim Auschecken (oder schon beim Einchecken) Fehler auf. Ich möchte die Unterschiede anhand eines Beispiels zeigen:.project nach dem Auschecken:<projectdescription> <name>start-demo</name> <comment></comment> <projects> </projects> <buildspec> </buildspec> <natures> </natures> </projectdescription>
.project wie es funktioniert:<projectdescription>
<name>start-demo</name>
<comment></comment>
<projects>
</projects>
<buildspec>
<buildcommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildcommand>
</buildspec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectdescription>Irgendwie wurde die buildSpec und die nature unterschlegen.Die zweite Datei
.classpath fehlt komplett:<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>Man kann die Dateien nach obigem Muster anlegen und muss dann ein "Refresh" des Projekts machen.
Donnerstag, 30. April 2009
cvs up & running
cvs.htlwrn.ac.at wurde wiederhergestellt. Stand der Daten: Montag 27.4.Labels: allgemeines, CVS, Fehler, System
Mittwoch, 29. April 2009
cvs zerstört
cvs.htlwrn.ac.at wurde leider zerstört. Wahrscheinlich sind einige Daten verlorengegangen. Möglichweise sogar alle!Ausständige Abgaben bitte per Mail als
ZIP oder JAR.Ihr Workspace ist wahrscheinlich die einzige Sicherung.
Labels: allgemeines, CVS, Fehler, System
Mittwoch, 4. März 2009
CVS - "cvs commit: nothing known about ..."
cvs add file). Der Schüler hatte die Datei schon gelöscht. Also ist auch kein cvs add nötig. Aber aus einem mir (noch) unbekannten Grund versucht Netbeans doch ein "commit" auf diese (nicht existierende) Datei zu machen.Folgenden Workaround habe ich gefunden:
- Alle einzelnen Dateien des Projekts händisch commiten.
- Das Projekt in einem neuen Verzeichnis auschecken.
- Prüfen, ob alles da ist.
- Das alte/originale Projekt löschen.
- Das neue verwenden.
cvs add kümmern. Da würde man verstehen, warum man eine nicht existierende Datei nicht "commiten" kann. Hier war aber die Datei offensichtlich nicht vorhanden und trotzdem versuchte Netbeans ein "commit".Grundsätzlich vereinfacht aber Netbeans (und auch eclipse) die Verwendung von CVS - schon alleine die umständlichen Schritte beim Anlegen (import) entfallen.
cvs add braucht man nicht machen.Scheinbar ein Bug im Netbeans.
Links:
Labels: CVS, eclipse, Fehler, netbeans
Sonntag, 4. Januar 2009
Schaltjahr-Bug in Zune
Ich präsentiere hier den relevanten Ausschnit des gesamten Quelltexts.
Zunächst die gesamte Funktion, in der der Fehler zu finden ist:
//------------------------------------------------------------------------------
//
// Function: ConvertDays
//
// Local helper function that split total days since Jan 1, ORIGINYEAR into
// year, month and day
//
// Parameters:
//
// Returns:
// Returns TRUE if successful, otherwise returns FALSE.
//
//------------------------------------------------------------------------------
BOOL ConvertDays(UINT32 days, SYSTEMTIME* lpTime)
{
int dayofweek, month, year;
UINT8 *month_tab;
//Calculate current day of the week
dayofweek = GetDayOfWeek(days);
year = ORIGINYEAR;
while (days > 365)
{
if (IsLeapYear(year))
{
if (days > 366)
{
days -= 366;
year += 1;
}
}
else
{
days -= 365;
year += 1;
}
}
// Determine whether it is a leap year
month_tab = (UINT8 *)((IsLeapYear(year))? monthtable_leap : monthtable);
for (month=0; month<12; month++)
{
if (days <= month_tab[month])
break;
days -= month_tab[month];
}
month += 1;
lpTime->wDay = days;
lpTime->wDayOfWeek = dayofweek;
lpTime->wMonth = month;
lpTime->wYear = year;
return TRUE;
}
Hier das relevante Code-Stück noch mal herausgenommen:
year = ORIGINYEAR; // == 1980
while (day > 365)
{
if (IsLeapYear(year))
{
numOfLeap++;
if (day > 366)
{
day -= 366;
year += 1;
}
}
else
{
day -= 365;
year += 1;
}
}
Das Jahr 2008 ist ein Schaltjahr (
IsLeapYear(year) liefert true), d.h. der 31.12.2008 ist der 366 Tag. Die Schleife while (day > 365) wird nie beendet, da in der Abfrage if (day > 366) der Tag mit der Nummer 366 nie berücksichtigt wird. Man hat in diesem Fall also eine Endlosschleife (day wird in diesem Fall nie verändert!).Abonnieren Posts [Atom]



