Hypercell ein ] Hypercell aus ] Zeige Navigation ] Verstecke Navigation ]
c++.de  
   
Forentreff 2012     
Bücher-Shop mit Amazon (Buchkategorien)C++ : Referenzen zu C++ : C++ Builder : Visual C++ : C# : Java : Spieleprogrammierung : Systemprogrammierung Linux : Software-Entwicklung : .NET : Compilertechnik : Algorithmen & Datenstrukturen : Objektorientierung : Entwurfsmuster : UML : eXtreme Programming : Scrum : Projektmanagement : Software-Testing : Datenbanken : Tom DeMarco : Dilbert : User Friendly
C/C++ Forum :: Projekt: OS-Development  ::  C vs C++ bei OSDEV  
Gehen Sie zu Seite 1, 2, 3, 4, 5  Weiter
  Zeige alle Beiträge auf einer Seite
Auf Beitrag antworten
Autor Nachricht
abc.w
Mitglied

Benutzerprofil
Anmeldungsdatum: 25.04.2008
Beiträge: 1364
Beitrag abc.w Mitglied 20:36:07 30.01.2010   Titel:              Zitieren

Erhard Henkes schrieb:
Es gibt leider auch viele Punkte, die man nur instinktiv richtig machen kann, z.B. C anstelle C++ verwenden

Vorsicht, Vorsicht... ;)

Habe neulich meinen älteren Code rausgekrammt und siehe da, so schlecht ist C++ nicht...

C/C++ Code:
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
    CScreen stdout;
    CCpu CPU(0u);

    stdout << "Started.\n";

    ...

    CPU.DisableInterrupts();
    CPU.Halt();
C/C++ Code:
1
2
3
4
5
6
7
8
9
CScreen stdout;
CCpu CPU(0u);

stdout << "Started.\n";

...

CPU.DisableInterrupts();
CPU.Halt();
C/C++ Code:
1
2
3
4
5
6
7
8
9
    CScreen stdout;
    CCpu CPU(0u);

    stdout << "Started.\n";

    ...

    CPU.DisableInterrupts();
    CPU.Halt();
Erhard Henkes
Mitglied

Benutzerprofil
Anmeldungsdatum: 25.04.2000
Beiträge: 11924
Beitrag Erhard Henkes Mitglied 22:57:10 30.01.2010   Titel:              Zitieren

Die Idee der Klasse mit ihren "domestizierten" Funktionen ist hervorragend. Da hat C nur die Struktur mit "freien" Funktionen zu bieten. Das bezahlt man in C++ mit viel Klassenbeziehungs-Wirrwarr und im kernel nutzlosen Overhead.

_________________
OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Tobiking2
Mitglied

Benutzerprofil
Anmeldungsdatum: 12.04.2009
Beiträge: 705
Beitrag Tobiking2 Mitglied 00:04:34 31.01.2010   Titel:              Zitieren

Erhard Henkes schrieb:
Die Idee der Klasse mit ihren "domestizierten" Funktionen ist hervorragend. Da hat C nur die Struktur mit "freien" Funktionen zu bieten. Das bezahlt man in C++ mit viel Klassenbeziehungs-Wirrwarr und im kernel nutzlosen Overhead.

Nur weil man C++ benutzt wird man aber nicht gezwungen komplizierte Klassenbeziehungen zu bauen oder Techniken zu nutzen die Overhead erzeugen. Und nutzlos ist es sicherlich nicht, selbst wenn es nur didaktischen Zwecken dient. :D
Erhard Henkes
Mitglied

Benutzerprofil
Anmeldungsdatum: 25.04.2000
Beiträge: 11924
Beitrag Erhard Henkes Mitglied 00:21:34 31.01.2010   Titel:              Zitieren

Zeige mir eine konkrete Stelle in PrettyOS im Kernel, bei der uns C++ einen greifbaren Vorteil bringen würde.

_________________
OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
erik.vikinger
Mitglied

Benutzerprofil
Anmeldungsdatum: 04.09.2009
Beiträge: 175
Beitrag erik.vikinger Mitglied 00:23:45 31.01.2010   Titel:              Zitieren

Hallo,

Erhard Henkes schrieb:
Es gibt leider auch viele Punkte, die man nur instinktiv richtig machen kann
Nach meiner Erfahrung sind Computer streng deterministische Gebilde, instinktive Entscheidungen sind daher nicht angebracht. ;)

Erhard Henkes schrieb:
Das bezahlt man in C++ mit viel Klassenbeziehungs-Wirrwarr und im kernel nutzlosen Overhead.
Overhead gegenüber C gibt es in C++ erst wenn man virtuelle Methoden benutzt oder die Exceptions verwendet. Insofern spricht IMHO nichts gegen die Verwendung von C++ anstelle von C, in jedem Anwendungsfall auch in einem OS-Kernel, solange man nicht zu tief in die Trickkiste von C++ greift. Auch der erzeugte Programm-Code ist dann nicht größer/langsamer als der von C.


Grüße
Erik
Erhard Henkes
Mitglied

Benutzerprofil
Anmeldungsdatum: 25.04.2000
Beiträge: 11924
Beitrag Erhard Henkes Mitglied 00:27:46 31.01.2010   Titel:              Zitieren

Das sieht dann z.B. so aus:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
// Hilfskonstruktionen
//
// pure virtual wird dennoch ausgeführt:

extern "C"{
void __cxa_pure_virtual() {} }
//
// Ersatz für new, new[], delete und delete[] der fehlenden C++-Standard-Bibliothek

void* operator new      (size_t size) { return malloc(size); }
void* operator new[]    (size_t size) { return malloc(size); }
void  operator delete   (void* ptr)   { free(ptr); }
void  operator delete[] (void* ptr)   { free(ptr); }
//
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
// Hilfskonstruktionen
//
// pure virtual wird dennoch ausgeführt:

extern "C"{
void __cxa_pure_virtual() {} }
//
// Ersatz für new, new[], delete und delete[] der fehlenden C++-Standard-Bibliothek

void* operator new (size_t size) { return malloc(size); }
void* operator new[] (size_t size) { return malloc(size); }
void operator delete (void* ptr) { free(ptr); }
void operator delete[] (void* ptr) { free(ptr); }
//
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
// Hilfskonstruktionen
//
// pure virtual wird dennoch ausgeführt:

extern "C"{
void __cxa_pure_virtual() {} }
//
// Ersatz für new, new[], delete und delete[] der fehlenden C++-Standard-Bibliothek

void* operator new      (size_t size) { return malloc(size); }
void* operator new[]    (size_t size) { return malloc(size); }
void  operator delete   (void* ptr)   { free(ptr); }
void  operator delete[] (void* ptr)   { free(ptr); }
//

_________________
OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
Tobiking2
Mitglied

Benutzerprofil
Anmeldungsdatum: 12.04.2009
Beiträge: 705
Beitrag Tobiking2 Mitglied 01:51:40 31.01.2010   Titel:              Zitieren

Erhard Henkes schrieb:
Zeige mir eine konkrete Stelle in PrettyOS im Kernel, bei der uns C++ einen greifbaren Vorteil bringen würde.

Ein technischer Grund fällt mir zurzeit nicht ein, manchmal ist es aber einfach eine andere Denkweise die hilft. Bei der Floppy gibt es z.B. vom Design her Probleme, die meiner Meinung nach objektorientiert eher auffallen und leicht zu lösen sind. Erfahrene C Entwickler werden diese Meinung sicherlich nicht teilen, da sie auch so die Probleme und Lösungen direkt sehen.

Ein Problem ist das eine mögliche Erweiterung auf mehrere Floppys unbequem ist. Man müsste um eine andere Floppy anzusprechen den Wert von _CurrentDrive umsetzen. Das sorgt aber dafür das sämtliche Befehle auf der anderen Floppy ausgeführt werden. Letztendlich würde man sich also an vielen Stellen den alten Wert merken, den Wert ändern, Befehle ausführen und den Wert wieder zurücksetzen. Ist jetzt kein Beinbruch, aber unschön wenn man das mal irgendwo vergisst. In C++ wäre man wahrscheinlich als erstes auf die Idee gekommen das ganze als Klasse zu modellieren. Dann steht jedes Objekt für eine Floppy und besitzt ein separates _CurrentDrive.

Das andere Problem ist die schon öfter erwähnte Treiberschnittstelle. In C++ würde man z.B. eine Klasse BlockDevice als Schnittstelle mit Funktionen wie read und write erstellen. Klasse Floppy, Festplatte etc. erben von BlockDevice und implementieren diese Funktionen. In einer Datenstruktur wie List<BlockDevice> kann man dann alle Geräte sammeln. In C wird dies nicht ganz so ausdrucksstark dargestellt. Statt der Klasse BlockDevice hat man eine Struktur mit Funktionspointern. Die bisherige Liste in PrettyOS arbeitet bisher auch nur mit void*. Es wird also auch noch öfters gecastet werden müssen. Es ist im Prinzip die gleiche Vorgehensweise wie in C++, allerdings finde ich die C++ Variante etwas anschaulicher.
abc.w
Mitglied

Benutzerprofil
Anmeldungsdatum: 25.04.2008
Beiträge: 1364
Beitrag abc.w Mitglied 03:23:30 31.01.2010   Titel:              Zitieren

Erhard Henkes schrieb:
Das sieht dann z.B. so aus:
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
// Hilfskonstruktionen
//
// pure virtual wird dennoch ausgeführt:

extern "C"{
void __cxa_pure_virtual() {} }
//
// Ersatz für new, new[], delete und delete[] der fehlenden C++-Standard-Bibliothek

void* operator new      (size_t size) { return malloc(size); }
void* operator new[]    (size_t size) { return malloc(size); }
void  operator delete   (void* ptr)   { free(ptr); }
void  operator delete[] (void* ptr)   { free(ptr); }
//
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
// Hilfskonstruktionen
//
// pure virtual wird dennoch ausgeführt:

extern "C"{
void __cxa_pure_virtual() {} }
//
// Ersatz für new, new[], delete und delete[] der fehlenden C++-Standard-Bibliothek

void* operator new (size_t size) { return malloc(size); }
void* operator new[] (size_t size) { return malloc(size); }
void operator delete (void* ptr) { free(ptr); }
void operator delete[] (void* ptr) { free(ptr); }
//
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
// Hilfskonstruktionen
//
// pure virtual wird dennoch ausgeführt:

extern "C"{
void __cxa_pure_virtual() {} }
//
// Ersatz für new, new[], delete und delete[] der fehlenden C++-Standard-Bibliothek

void* operator new      (size_t size) { return malloc(size); }
void* operator new[]    (size_t size) { return malloc(size); }
void  operator delete   (void* ptr)   { free(ptr); }
void  operator delete[] (void* ptr)   { free(ptr); }
//


Oder so (das ist jetzt natürlich kein Musterbeispiel, falls jemand trotzdem Kritik hat, gerne her damit):

C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "datatypes.h"

#ifdef
MAX_MEMORY_BLOCKS
#error
Already defined.
#else

/// Specifies number of memory blocks used for dynamic memory
#define
MAX_MEMORY_BLOCKS   (32u)
#endif

#ifdef
MAX_BLOCK_SIZE
#error
Already defined.
#else

/// Specifies number of uint32_t words contained in one memory block
#define
MAX_BLOCK_SIZE      (1023u)
#endif


/// Structure for memory block
static struct tMemoryBlock
{
    uint32_t used;                      ///< Flag, indicating if memory block is used or not
    uint32_t data[MAX_BLOCK_SIZE];      ///< Data words of the memory block
}
MemoryBlock[MAX_MEMORY_BLOCKS];

/// Check size of one MemoryBlock structure. It is expected to be a fixed number of bytes.
typedef char CheckSize_tMemoryBlock[sizeof(MemoryBlock[0]) == 4096 ? 1: -1];

void* operator new(uint32_t size)
{
    uint32_t i = 0u;
    uint32_t found_i = 0u;

    size = size;

    // This loop goes through all memory blocks and looks for free one
    for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
    {
        if (0u == MemoryBlock[i].used)
        {
            // Found one unused memory block
            // Set found index and break the loop

            found_i = i;
            i = (MAX_MEMORY_BLOCKS - 1u);
        }
    }

    // Set used flag
    ++(MemoryBlock[found_i].used);

    return &(MemoryBlock[found_i].data[0]);
}

void operator delete(void* pData)
{
    uint32_t i = 0u;

    // This loop goes through all memory blocks and checks, if
    // the pointer points to it. The corresponding memory block
    // gets unused then.

    for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
    {
        const void* pBegin = &(MemoryBlock[i].data[0]);
        const void* pEnd = &(MemoryBlock[i].data[MAX_BLOCK_SIZE - 1u]);

        if ((pData >= pBegin) && (pData <= pEnd))
        {
            // Found one used block, no sense to continue the loop
            // Set it as unused and break the loop

            MemoryBlock[i].used = 0;
            i = (MAX_MEMORY_BLOCKS - 1u);
        }
    }
}

void* operator new(uint32_t size, void* pBuffer)
{
    size = size;
    // Because this function is "placement new", just return the
    // pointer to the buffer

    return pBuffer;
}

void* operator new[](uint32_t size)
{
    return operator new(size);
}

void operator delete[](void* pData)
{
    operator delete(pData);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "datatypes.h"

#ifdef
MAX_MEMORY_BLOCKS
#error
Already defined.
#else

/// Specifies number of memory blocks used for dynamic memory
#define
MAX_MEMORY_BLOCKS (32u)
#endif

#ifdef
MAX_BLOCK_SIZE
#error
Already defined.
#else

/// Specifies number of uint32_t words contained in one memory block
#define
MAX_BLOCK_SIZE (1023u)
#endif


/// Structure for memory block
static struct tMemoryBlock
{
uint32_t used; ///< Flag, indicating if memory block is used or not
uint32_t data[MAX_BLOCK_SIZE]; ///< Data words of the memory block
}
MemoryBlock[MAX_MEMORY_BLOCKS];

/// Check size of one MemoryBlock structure. It is expected to be a fixed number of bytes.
typedef char CheckSize_tMemoryBlock[sizeof(MemoryBlock[0]) == 4096 ? 1: -1];

void* operator new(uint32_t size)
{
uint32_t i = 0u;
uint32_t found_i = 0u;

size = size;

// This loop goes through all memory blocks and looks for free one
for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
{
if (0u == MemoryBlock[i].used)
{
// Found one unused memory block
// Set found index and break the loop

found_i = i;
i = (MAX_MEMORY_BLOCKS - 1u);
}
}

// Set used flag
++(MemoryBlock[found_i].used);

return &(MemoryBlock[found_i].data[0]);
}

void operator delete(void* pData)
{
uint32_t i = 0u;

// This loop goes through all memory blocks and checks, if
// the pointer points to it. The corresponding memory block
// gets unused then.

for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
{
const void* pBegin = &(MemoryBlock[i].data[0]);
const void* pEnd = &(MemoryBlock[i].data[MAX_BLOCK_SIZE - 1u]);

if ((pData >= pBegin) && (pData <= pEnd))
{
// Found one used block, no sense to continue the loop
// Set it as unused and break the loop

MemoryBlock[i].used = 0;
i = (MAX_MEMORY_BLOCKS - 1u);
}
}
}

void* operator new(uint32_t size, void* pBuffer)
{
size = size;
// Because this function is "placement new", just return the
// pointer to the buffer

return pBuffer;
}

void* operator new[](uint32_t size)
{
return operator new(size);
}

void operator delete[](void* pData)
{
operator delete(pData);
}
C/C++ Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "datatypes.h"

#ifdef
MAX_MEMORY_BLOCKS
#error
Already defined.
#else

/// Specifies number of memory blocks used for dynamic memory
#define
MAX_MEMORY_BLOCKS   (32u)
#endif

#ifdef
MAX_BLOCK_SIZE
#error
Already defined.
#else

/// Specifies number of uint32_t words contained in one memory block
#define
MAX_BLOCK_SIZE      (1023u)
#endif


/// Structure for memory block
static struct tMemoryBlock
{
    uint32_t used;                      ///< Flag, indicating if memory block is used or not
    uint32_t data[MAX_BLOCK_SIZE];      ///< Data words of the memory block
}
MemoryBlock[MAX_MEMORY_BLOCKS];

/// Check size of one MemoryBlock structure. It is expected to be a fixed number of bytes.
typedef char CheckSize_tMemoryBlock[sizeof(MemoryBlock[0]) == 4096 ? 1: -1];

void* operator new(uint32_t size)
{
    uint32_t i = 0u;
    uint32_t found_i = 0u;

    size = size;

    // This loop goes through all memory blocks and looks for free one
    for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
    {
        if (0u == MemoryBlock[i].used)
        {
            // Found one unused memory block
            // Set found index and break the loop

            found_i = i;
            i = (MAX_MEMORY_BLOCKS - 1u);
        }
    }

    // Set used flag
    ++(MemoryBlock[found_i].used);

    return &(MemoryBlock[found_i].data[0]);
}

void operator delete(void* pData)
{
    uint32_t i = 0u;

    // This loop goes through all memory blocks and checks, if
    // the pointer points to it. The corresponding memory block
    // gets unused then.

    for (i = 0u; i < MAX_MEMORY_BLOCKS; ++i)
    {
        const void* pBegin = &(MemoryBlock[i].data[0]);
        const void* pEnd = &(MemoryBlock[i].data[MAX_BLOCK_SIZE - 1u]);

        if ((pData >= pBegin) && (pData <= pEnd))
        {
            // Found one used block, no sense to continue the loop
            // Set it as unused and break the loop

            MemoryBlock[i].used = 0;
            i = (MAX_MEMORY_BLOCKS - 1u);
        }
    }
}

void* operator new(uint32_t size, void* pBuffer)
{
    size = size;
    // Because this function is "placement new", just return the
    // pointer to the buffer

    return pBuffer;
}

void* operator new[](uint32_t size)
{
    return operator new(size);
}

void operator delete[](void* pData)
{
    operator delete(pData);
}
abc.w
Mitglied

Benutzerprofil
Anmeldungsdatum: 25.04.2008
Beiträge: 1364
Beitrag abc.w Mitglied 00:26:47 07.02.2010   Titel:              Zitieren

Tobiking2 schrieb:
In C++ würde man z.B. eine Klasse BlockDevice als Schnittstelle mit Funktionen wie read und write erstellen. Klasse Floppy, Festplatte etc. erben von BlockDevice und implementieren diese Funktionen. In einer Datenstruktur wie List<BlockDevice> kann man dann alle Geräte sammeln. In C wird dies nicht ganz so ausdrucksstark dargestellt. Statt der Klasse BlockDevice hat man eine Struktur mit Funktionspointern. Die bisherige Liste in PrettyOS arbeitet bisher auch nur mit void*. Es wird also auch noch öfters gecastet werden müssen. Es ist im Prinzip die gleiche Vorgehensweise wie in C++, allerdings finde ich die C++ Variante etwas anschaulicher.

Lese gerade zufällig in c't (Heft 19, 31.8.2009):
Zitat:
Kritische Lücke im Linux-Kernel
Eine Sicherheitslücke im Linux-Kernel betrifft alle Versionen ... seit Mai 2001. ... Üblicherweise deklariert eine Pointer-Struktur, welche Operationen ein Socket untersützt, etwa accept, bind und so weiter. Ist aber die Operation accept nicht implementiert, so sollte sie auf eine vordefinierte Komponente wie sock_no_accept zeigen. Dies ist offensichtlich nicht bei allen implementierten Protokollen der Fall...

So ist es, man hat ja keine Überwachung durch den Compiler, also muss der Programmierer selbst darauf achten, dass er alle Funktions-Pointer setzt bzw. auf deren Gültigkeit prüft...
Erhard Henkes
Mitglied

Benutzerprofil
Anmeldungsdatum: 25.04.2000
Beiträge: 11924
Beitrag Erhard Henkes Mitglied 01:15:39 07.02.2010   Titel:              Zitieren

Die Stärken moderner Programmiersprachen bestehen in der Fehlervermeidung und -überwachung. Allerdings hat man dafür bei C einen einfacheren Umgang mit der Materie. C++ fordert dem Entwickler hier deutlich mehr ab.

Warum nicht gleich Java für OSDEV?

_________________
OS-Development-, C++, Win32-API-, MFC-, Chemie-, Robotik- und Flugsimulator-Tutorials
http://www.henkessoft.de/index.htm
C/C++ Forum :: Projekt: OS-Development  ::  C vs C++ bei OSDEV  
Gehen Sie zu Seite 1, 2, 3, 4, 5  Weiter
Auf Beitrag antworten

Zeige alle Beiträge auf einer Seite




Nächstes Thema anzeigen
Vorheriges Thema anzeigen
Sie können keine Beiträge in dieses Forum schreiben.
Sie können auf Beiträge in diesem Forum nicht antworten.
Sie können Ihre Beiträge in diesem Forum nicht bearbeiten.
Sie können Ihre Beiträge in diesem Forum nicht löschen.
Sie können an Umfragen in diesem Forum nicht mitmachen.

Powered by phpBB © 2001, 2002 phpBB Group :: FI Theme

c++.de ist Teilnehmer des Partnerprogramms von Amazon Europe S.à.r.l. und Partner des Werbeprogramms, das zur Bereitstellung eines Mediums für Websites konzipiert wurde, mittels dessen durch die Platzierung von Werbeanzeigen und Links zu amazon.de Werbekostenerstattung verdient werden kann.

Die Vervielfältigung der auf den Seiten www.c-plusplus.de, www.c-plusplus.info, www.c-sar.de, www.c-plusplus.net und www.baeckmann.de enthaltenen Informationen ohne eine schriftliche Genehmigung des Seitenbetreibers ist untersagt (vgl. §4 Urheberrechtsgesetz). Die Nutzung und Änderung der vorgestellten Strukturen und Verfahren in privaten und kommerziellen Softwareanwendungen ist ausdrücklich erlaubt, soweit keine Rechte Dritter verletzt werden. Der Seitenbetreiber übernimmt keine Gewähr für die Funktion einzelner Beiträge oder Programmfragmente, insbesondere übernimmt er keine Haftung für eventuelle aus dem Gebrauch entstehenden Folgeschäden.