Alle meine Programme sind auf Robustheit ausgelegt. Ein Programm ist robust, wenn es ohne zu murren und ohne Installiererei einfach läuft (und läuft und läuft). Auf möglichst jeder Zielplattform. (Das gilt sinngemäß auch für Webseiten!) Wichtig ist dabei die Unabhängigkeit von nicht-immer-vorhandenen Zusatzbibliotheken. Etwa den rechts gelisteten, manchen geplagten Anwender sattsam bekannten, mit Updates nervenden Softwarebomben.
Es gibt nur noch recht wenige Ausführungen anderswo, mit Win32 zu programmieren. Der typische Anfänger beginnt mit .NET und C#. Oder, noch schlimmer, mit Python.
Was die Fachliteratur als „Einfachstes Windows-Programm“ so anbietet ist schlichtweg abschreckend! Hier sind die wirklich einfachen Programme.
#include <stdio.h> int main() { printf("Hallo Welt!\n"); return 0; }
Wer kennt das nicht? (Quelle: Kerninghan+Ritchie: C-Programmierung)
Mit den richtigen Projekteinstellungen entsteht ein Konsolenprogramm, welches „Hallo Welt“ ausspuckt und mit ca. 32 Kilobyte ziemlich fett ist. Hier den Speck zu entfernen ist für Anfänger etwas knifflig, daher werde ich im folgenden nicht darauf eingehen. Außerdem sieht es nicht wie ein Windows-Programm aus. Sondern wie ein Fremdkörper.
#include <windows.h> int WinMain(HINSTANCE, HINSTANCE, LPTSTR, int) { return MessageBox(0,"Hallo Welt!","Nr. 2",0); }
Das ist ein einfaches Windows-Programm!
Beinahe selbsterklärend erscheint ein (kleines) Fenster in der Mitte des Bildschirms mit dem Text „Hallo Welt!“ und einem „OK“-Knopf zum Beenden des Programms.
Mit etwa 20 Kilobyte ist es aber immer noch Bloatware. Diese lassen sich ganz einfach entfernen, siehe nächstes Beispiel.
#include <windows.h> EXTERN_C int WinMainCRTStartup(void) { return MessageBox(0,"Hallo Welt!","Nr. 3",MB_OK); }
Dieses Programm macht genau dasselbe wie Nr. 2, die EXE-Datei ist mit den richtigen Projekteinstellungen nur noch rund 2 Kilobyte groß. Kleiner geht's nicht, die für Windows erforderlichen Verwaltungsinformationen im EXE-Header kosten ihren Fixpreis. Der eigentliche Kode und die Stringkonstanten bedürfen nur weniger als 100 Bytes und würde in den kleinsten Mikrocontroller passen: An der Intel-Architektur liegt's jedenfalls nicht.
Vergleicht man die drei Beispiele mit dem Dependency Walker, wird man feststellen, dass dieses genau eine externe Abhängigkeit aufweist, nämlich nach USER32:MessageBoxA. Die Bloatware vorher ruft noch Dutzende anderer Funktionen auf, die überhaupt nicht benötigt werden.
Unter Windows 10 und neuer bleibt das Programm als Leiche im Task-Manager liegen,
nachdem man in der Messagebox „OK“ anklickt und das Fenster verschwindet.
Merkt man daran, dass eine erneute Kompilierung fehlschlägt,
weil der Linker keinen Schreibzugriff auf die Echse erhält.
Ursache ist ein „Remote-Thread“ der noch läuft.
Dieser entsteht in der Regel durch SetWindowsHookEx(WH_CBT,dllHookProc,dllInstance,0)
einer injizierten DLL.
Das Programm muss mit ExitProcess()
beendet werden.
return
beendet nur den Thread
— und den Prozess nur wenn's der letzte Thread ist.
#include <windows.h> EXTERN_C __declspec(noreturn) void WinMainCRTStartup(void) { ExitProcess(MessageBox(0,"Hallo Welt!","Nr. 3.1",MB_OK)); }
Leider ist diese Echse etwas größer wegen Import von KERNEL32:ExitProcess.
__declspec(noreturn)
sollte den
ret
-Assemblerbefehl wegoptimieren.
ExitProcess()
sollte gefälligst selbst mit
__declspec(noreturn)
deklariert sein.
Im Normalfall bindet der C/C++-Compiler zwischen …
… Kode ein der in etwa folgendes macht:
Win32-GUI | Win32-Konsole | avr-gcc | PIC xc8 |
---|---|---|---|
– | Stapelzeiger initialisieren | – | |
– | R1 nullsetzen: clr r1 | – | |
Kommandozeile beschaffen: GetCommandLine() | Initialisierte statische Daten (.data) vom Flash in RAM kopieren | ||
– | Kommandozeile zerlegen: _getmainargs(); oder CommandLineToArgvW() | Uninitialisierte statische Daten (.bss) nullsetzen | |
Den Heap vorbereiten | |||
Nur C++: Statische Konstruktoren aufrufen | |||
WinMain() aufrufen | main() aufrufen
| ||
Prozess beenden mit ExitProcess() (return geht nicht!) | „Prozessende“ mit Endlosschleife simulieren und so auf Reset warten |
Und was zur Hölle braucht das 20 Kilobyte?? Auch bei avr-gcc ist die Stapelzeiger-Initialisierung bei modernen ATtiny und ATmega überflüssig (also Code Bloat) und führt zu ungeahnten Problemen, wenn man bspw. das Flash-Image eines ATtiny25 auf einem ATtiny85 ausführt.
Schließlich der Ausgangspunkt für 95 % aller praktischen Programmieraufgaben.
Programme die wie Dialoge aussehen sind das Butterbrot des Programmierers.
Der Erfolg von VisualBasic und Delphi bestätigt dies.
Denn oftmals sind es die kleinen Probleme,
die ein kleines Programm benötigen.
Sogar „große“ Programme mit größenveränderlichem Hauptfenster lassen sich
ganz ohne CreateWindow()
und RegisterClass()
schreiben,
nur ist's dann nicht mehr so vorteilhaft.
#include <windows.h> static INT_PTR WINAPI MainDlgProc(HWND Wnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { }return TRUE; case WM_COMMAND: switch (wParam) { case MAKELONG(IDCANCEL,BN_CLICKED): EndDialog(Wnd,wParam); break; }break; } return FALSE; } EXTERN_C void WinMainCRTStartup() { ExitProcess(DialogBox(0,MAKEINTRESOURCE(100),0,MainDlgProc)); }
Wie man am Quelltext „sieht“, fehlt jegliche Ausgabe, etwa Zeichenketten. Diese befinden sich nunmehr in einer Ressource: (Maus darüberhalten für Erklärungen!)
1 24 "manifest" 100 DIALOG 0,0,220,100 STYLE DS_3DLOOK|DS_NOFAILCREATE|DS_CENTER|WS_MINIMIZEBOX|WS_VISIBLE|WS_CAPTION|WS_SYSMENU CAPTION "Nr. 4" FONT 8, "Helv" { CTEXT "Hallo Welt!",-1,10,10,200,8 DEFPUSHBUTTON "Ende",2,60,50,100,14 }
So eine Ressourcendatei (*.rc) sieht aus wie C, ist aber kein C, und wird vom Resource Compiler (rc.exe) entweder in eine Binärressource (*.res) oder gleich in die EXE-Datei eingebunden. Ressourcen sind (u.a. für Dialoge) standardisiert und mehrsprachig, sie können auch nachträglich mit geeigneten Editoren korrigiert, übersetzt und hinzugefügt werden. So hat der Kunde die Möglichkeit der Anpassung und Übersetzung ohne Quelltext! Das ist die Idee hinter den Ressourcen! Keine Idee von Microsoft sondern von Apple. Mit .NET oder Qt geht das nicht.
Die EXE-Datei ist nun größer geworden (4 Kilobyte), weil eine weitere „Section“ (die Ressource nämlich) mit hineingewandert ist. Dafür ist dieses Programm nun beliebig ausbaufähig.
Wie man sieht, kommt man für lange Zeit
um RegisterClass()
und CreateWindow()
herum!
Wer so etwas als „Erstes Windows-Programm“ anbietet,
will wohl nur davon abschrecken.
(Schade, Herr Petzold.)
Ein unterschätztes Problem der (nicht nur Windows-)Programmierung ist das Merken der Fensterposition sowie weiterer „kleiner“ Konfigurationseinstellungen wie:
GetOpenFileName()
Tatsächlich kümmern sich viele Windows-Programme in der freien Wildbahn nicht die Bohne um das Speichern — und sind dadurch elend unkomfortabel!
Am praktischsten hat es sich erwiesen, immer zu speichern und gar nicht erst den Benutzer zu fragen! Also kein Menüpunkt „Konfiguration speichern“ oder so anzulegen.
Zum Glück gibt es unter Windows den perfekten Speicherort dafür: Die Systemregistrierung (Programmierer-Jargon: Registry) unter HKEY_CURRENT_USER\Software\Firma_oder_Programmierersignet\Programmname. Das elend lange HKEY_CURRENT_USER wird mit HKCU abgekürzt. Und hier kann man problemlos Binärdaten speichern!
Hier habe ich folgende Lösung für die beste herausgefunden:
struct
gesteckt:
• Fensterposition, Größe, Erscheinung — siehe GetWindowPlacement()
, sowie • Sonstige Einstellungen.
struct
-Elemente („Member“) halte ich minimal klein
(short
für Fensterposition und -Größe,
char
und BYTE
für nahezu alles andere,
um die Registry nicht mit Nullen zu überfrachten.
struct
„in einem Rutsch“ beim Starten geladen
und beim Beenden gespeichert.
So ist's schön aufgeräumt, bei hervorragender
User Experience.
Gibt man schon seit Windows 3.1 mit OutputDebugString()
aus.
Diese erscheinen im Output-Fenster der Entwicklungsumgebung.
Oder mittels Mark Russinovichs
DebugView.
Neu: Da das Vorhandensein von DebugView nicht immer sichergestellt
werden kann ist die alternative Ausgabe auf eine Konsole wünschenswert:
Die Funktion AttachConsole(-1)
verwendet das Konsolenfenster des Aufrufers.
Mit anderen Worten: Es verwendet die Konsole beim Aufruf aus einem Konsolenfenster
(d.h. aus cmd.exe oder command.com) wie ein Konsolenprogramm
aber öffnet keine Konsole beim Aufruf vom Startmenü, Explorer
oder Total Commander
im Gegensatz zu einem Konsolenprogramm.
Ergo: Als Windows-Programm compilieren (SUBSYSTEM:WINDOWS).
Gelingt AttachConsole(-1)
dann mit WriteConsole()
ausgeben
sonst wie gehabt mit OutputDebugString()
.
Zudem lassen sich so „bimodale“ Programme generieren: Konsole und Windows.
Gut für alles was man auch mal von einem
Makefile starten möchte.
Das gute alte MSVC6 aus dem Jahr 1998 (1 CD) ist bei mir immer noch die beste Grundlage, um derartig kleine Programme zu schreiben. Dazu passt am besten die Online-Hilfe „MSDN April 2001“. Bei mir auf 3 CDs. Leider macht die Installation Probleme ab Windows XP:
Was man mit MSVC6 (und allen C++-Dialekten vor C++11) nicht tun sollte
ist die Verwendung von std::vector
mit richtigen Objekten!
Dadurch, dass der Raubkopierkonstruktor und std::vector::emplace()
fehlt,
wird das Einbetten von Handles und deren automatischer Destruktoraufruf
zu einer elend windigen Angelegenheit.
Man tut besser daran, keine Objekte sondern nur einfache Datentypen hineinzutun.
Bei Strukturen, die Handles enthalten, muss man diese zu Fuß freigeben.
Oder man tut Zeiger auf Objekte in den Vektor.
(Bei virtuellen Objekten geht das ohnehin nur so.)
Nun, das macht das Iterieren umständlicher.
Weil die Iteratoren nun Zeiger auf Zeiger sind.
Normalerweise gibt es keinen Grund, den Gnu-C-Compiler mingw zu benutzen. Außer man folgt einen gewissen Fetisch. Denn der Compiler compiliert noch schlechter als der von Microsoft, den Microsoft-Compiler gibt's in einer freien Version ohne Einschränkungen, und die Microsoft-Lizenz macht keine Einschränkungen zur Verwendung des entstehenden Kompilats.
Die folgenden Kommandozeilenoptionen sind nötig, um die Laufzeitbibliothek loszuwerden und die Echse kleinzukriegen:
try..except
bzw. C __try..__except
)
__chkstk
, lokale Variablen muss man pro Funktion auf 4096 Byte begrenzen
push ebp; mov ebp,esp; sub esp,local_variables_size
zu etwas kürzerem
printf()
, strlen()
,
memcpy()
und new
muss man konsequent verzichten.
Für alles außer Gleitkomma gibt es Win32-API-Funktionen.
Auf den fehlenden Ressourcen-Editor kann man verzichten, wenn man von einer vorhandenen Ressource ausgeht und die .RC-Datei als Text editiert. Nur das Gestalten der Dialoge ist damit sehr mühevoll. Die Aufrufzeile geht (typisch Gnu-Gläubige) mal wieder nicht ohne Pflichtoption: windres -o ziel.res -O coff quelle.rc
Ich habe mal mingw64 auf die wirklich benötigten Dateien eingedampft. Dabei kam diese Dateiliste heraus. Immer noch reichlich 40 Megabyte. Unsäglich wirr verstreut. Das war zu Zeiten von Borland C++ die ganze Festplatte!
Fast alle meiner Win32-Programme sind ohne Laufzeitbibliothek geschrieben. Die nachfolgende Übersicht gibt einige Hinweise auf die Unterschiede der Programmierung.
Prinzipiell gibt es da zwei Wege:
Ganz ohne Laufzeitbibliothek | Verwendung der msvcrt.dll | |
Vorteile: |
|
|
Nachteile: |
|
|
Vorgehen: |
|
|
Dem Leser sollte Win32-Programmierung bekannt sein, wenn nicht, dann bitte den „Petzold“ lesen!
Ich bin mit Windows-Programmierung in Pascal groß geworden, und habe Objektbibliotheken zu hassen gelernt, weil sie eben nur so tun, als ob sie Komplexität verbergen könnten. Deswegen wird der Leser bei mir kein einziges MFC-Programm finden. Auch wenn es nach außen so (modern) aussehen möge, schon die Dateigröße verrät, dass es kein MFC sein kann.
Generelle Unterschiede:
mit Laufzeitbibliothek | ohne Laufzeitbibliothek | |
---|---|---|
Minimale Kodegröße | 20 KB | 2,5 KB |
Kopfdateien | #include <stdafx.h> oder ähnliches | #define WIN32_LEAN_AND_MEAN #include <windows.h> // Windows eben #include <windowsx.h> // Nützliche Makros #include <shlwapi.h> // String-Funktionen #include <comdlg32.h> // Datei-öffnen-Dialog #include <comctl32.h> // erweiterte Dialogelemente ... |
Eintrittspunkt | WinMain
| WinMainCRTStartup
|
Kommandozeilen-Argumente | Argument von WinMain , Programm-Name entfernt
Unicode-Problem! GetCommandLine wird empfohlen
| GetCommandLine benutzen, mit PathGetArgs
zerstückeln
|
Programmende | return retval
| ExitProcess(retval)
Nur return killt nicht die durch GetOpenFileName erzeugten Threads!
|
Compiler-Schalter | Projekt-Einstellungen funktionieren, die vorcompilierten Header machen aber meistens nur Ärger |
|
Linker-Schalter | Projekt-Einstellungen, auf die OLE-Importbibliotheken kann man getrost
verzichten, einige andere Importbibliotheken werden des öfteren benötigt:
olepro32.lib
(typischerweise für
OleLoadPicture()
ist richtig für Windows 95, aber falsch für Win7/64.
Für 64-Bit-Plattform zu oleaut32.lib wechseln!
oleaut32.dll gibt's sicher ab Windows 98 SE.
|
#pragma -Anweisungen ist nicht portabel
hin zum neueren MSVC9! Deshalb besser im Projekt setzen.
|
Diese Tabelle spiegelt nur den Unterscheid bei Microsoft Visual C++ (ab Version 6 geprüft) wider. Bei Borland C++ gelten andere Regeln!
Statt mit msvcrt-light.lib herumzuhampeln kann man den Compiler des Windows-DDKs benutzen. Dieser linkt automatisch konservativ zur alten MSVCRT.DLL mit folgender Kommandozeile, aus einer Build-Umgebung heraus:
Man erspart sich Probleme mit stdout
, __try
, __chkstk
und sicherlich auch der STL.
Ohne /entry= wird der Code größer, aber statische Konstruktoren
und Kommandozeilenargumente werden kompatibel behandelt.
Konstanten im Kodesegment (/GF) ersparen initialisierte statische Variablen.
(Alle nicht initialisierten statischen Variablen werden vom EXE-Loader
stets mit Null initialisiert, muss man wissen.
Es ist ohnehin unschöner Programmierstil, mit Nicht-Null initialisierte
statische Variablen nicht const
zu setzen.
Bei der Verwendung von
unicows.lib
bzw.
libunicows.lib
kommt man um solche Variablen nicht herum.)
Den Erfolg der Maßnahme kann man mittels dumpbin /headers
prüfen: das Datensegment (.data
) muss in der EXE-Datei leer sein.
SECTION HEADER #3
.data name
6F4 virtual size
8000 virtual address (00408000 to 004086F3)
0 size of raw data
0 file pointer to raw data
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
C0000040 flags
Initialized Data
Read Write
Ohne Standardbibliothek kann man nicht „portabel“ im Sinne des ANSI-Standards
programmieren. Aber kein Windows-Programm ist jemals ANSI-konform.
Es gibt kein main
, kein printf
. (Wohin denn?)
Muss ich weiter argumentieren? (Ich rede hier nicht von Konsolen-Programmen.)
Also, Portabibiltät ist etwas für den Elfenbeinturm (Informatiker, Lehre)
oder für Unix (Linux - da kommt man nicht herum!).
Für Windows: da sage ich einfach Tschüss und winke-winke!
Jetzt muss man nur noch wissen, was man statt der gewohnten Funktionen
verwenden muss. Ganz nebenbei wird das Programm unicode-fähig, ohne jedesmal
den TCHAR-Präfix (bspw. _tprintf
statt printf
)
verwenden zu müssen (was ja auch nicht ganz portabel ist).
Hier eine kurze Übersicht
mit Laufzeitbibliothek | ohne Laufzeitbibliothek | |||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Kommandozeilen-Argumente | via strchr zerstückelt | PathGetArgs einsetzen!
| ||||||||||||||||||||||||||||||||||
String-Manipulation | Vorwort: Es gibt einen kleinen aber feinen Unterschied
bei der Behandlung von Puffergrößen bei String-Funktionen der Laufzeitbibliothek
und von Windows!
elemof(array) (siehe unten) leistet hier gute Dienste.
| |||||||||||||||||||||||||||||||||||
strlen | lstrlen (ist Unicode-fähig auch unter Win9x)
| |||||||||||||||||||||||||||||||||||
strcpy , strncpy | lstrcpy , besser lstrcpyn ; für Win9x-Unicode StrCpy , besser StrCpyN
Beachte: strncpy und StrCpyN arbeiten unterschiedlich bei Pufferknappheit!
StrCpyN garantiert eine terminierende Null, kopiert also maximal N-1 Zeichen; strncpy füllt den Puffer bis zum Rand.
| |||||||||||||||||||||||||||||||||||
strcat | lstrcat, StrCat , besser StrCatBuff
| |||||||||||||||||||||||||||||||||||
strchr bzw. strrchr | StrChr bzw. StrRChr
| |||||||||||||||||||||||||||||||||||
strstr bzw. strrstr | StrStr bzw. StrRStr
| |||||||||||||||||||||||||||||||||||
strpbrk | StrPBrk
| |||||||||||||||||||||||||||||||||||
strspn | StrSpn
| |||||||||||||||||||||||||||||||||||
strdup | StrDup
|
Hier könnte man noch beliebig fortsetzen.
Bei Einsatz von Gleitkomma bei Ein- und Ausgabe ist es besser,
ein funktionierendes sprintf()
und sscanf()
zu haben, diese beschafft man sich wie folgt:
EXTERN_C int (_cdecl*_stprintf)(PTSTR,int,PTSTR,...);
EXTERN_C int (_cdecl*_stscanf)(PTSTR,PTSTR,...);
#ifdef UNICODE
# define __IMP__STPRINTF "_snwprintf"
# define __IMP__STSCANF "swscanf"
#else
# define __IMP__STPRINTF "_snprintf"
# define __IMP__STSCANF "sscanf"
#endif
// irgendwo in der Programminitialisierung:
HINSTANCE hLibMSVCRT=LoadLibraryA("MSVCRT.DLL");
(FARPROC)_stprintf=GetProcAddress(hLibMSVCRT,__IMP__STPRINTF);
(FARPROC)_stscanf=GetProcAddress(hLibMSVCRT,__IMP__STSCANF);
(das funktioniert auch mit mingw) oder, durch eine spezielle Importbibliothek. Denn die MSVCRT.DLL ist seit Windows 95 standardmäßig installiert, die braucht man weder mitzuliefern noch deren Funktionen statisch einzubinden.
Oftmals will der Linker eine Funktion __ftol oder __ftol2_sse. Siehe hier:
Bei Linker-Fehlern mit sse ist die Architektur beim Compilieren „falsch“ eingestellt;
das Programm würde auf älteren Prozessoren nicht laufen.
Schalter: /arch:IA32 (Microsoft C++).
Die Funktion _ftol ließe sich mit der Kommandozeilenoption
/QIfist inlinen.
Das generiert Kode wie diese Ersatzfunktion:
long _declspec(naked) _cdecl _ftol(void){ _asm{
push eax
fistp dword ptr [esp]
pop eax
ret
}}
Das Problem: Diese Funktion rundet standardmäßig.
(Die echte __ftol
-Funktion rundet zur Null.)
Meistens will man sowieso runden, und deshalb ist es besser, sämtliche
automatischen Typecasts zu entfernen und durch eine eigene Funktion
zu ersetzen, etwa:
__forceinline __int64 llrint(double f) {
__int64 i;
_asm fld f
_asm fistp i
return i;
}
statt dem langweiligen, aber üblichen i=(int)floor(x+0.5)
.
Das Rundungsergebnis ist dabei nicht ganz gleich,
bei x=0.5
rundet diese Zeile zu 1
(Kaufmännisches Runden),
während llrint(0.5)
zur geraden Zahl 0
rundet (Banker's Rounding).
X86-Assembler | AMD64-Assembler | Visual C (#include <intrin.h> ) | Gnu C | Funktion | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Bytetausch (Endian-Konvertierung) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
xchg ah,al_byteswap_ushort | __builtin_bswap16 | htons() auf Little-Endian
| bswap eax | bswap rax | _byteswap_ulong | _byteswap_uint64 | __builtin_bswap32 | __builtin_bswap64 | htonl() auf Little-Endian
| Bitsuche, Bitzählen, Bitrotieren | bsf eax,ecx | bsf rax,rcx | _BitScanForward | _BitScanForward64 | __builtin_ctz | __builtin_ctzll | nicht identisch!
| bsr eax,ecx | bsr rax,rcx | _BitScanReverse | _BitScanReverse64 | __builtin_clz | __builtin_clzll | =(int)lb(x) (log2)
| bt [ecx],edx | setnz al bt [rcx],rdx | setnz al _bittest | _bittest64 | - | (a&1<<b) | Überspannt Speicherbereiche
| btr [ecx],edx | setnz al btr [rcx],rdx | setnz al _bittestandreset | _bittestandreset64 | __sync_fetch_and_nand | (m=1<<b,r=a&m,a&=~m,r)
| bts [ecx],edx | setnz al bts [rcx],rdx | setnz al _bittestandset | _bittestandset64 | __sync_fetch_and_or | (m=1<<b,r=a&m,a|=m,r)
| btc [ecx],edx | setnz al btc [rcx],rdx | setnz al _bittestandcomplement | _bittestandcomplement64 | __sync_fetch_and_xor | (m=1<<b,r=a&m,a^=m,r)
| popcnt eax,ecx | popcnt rax,rcx | __popcnt16, __popcnt, __popcnt64 | __builtin_popcount | __builtin_popcountll | Zählt 1-Bits
| or ecx,ecx | setp al or rcx,rcx | setp al - | __builtin_parity | __builtin_parityll | Parität, Anzahl 1-Bits modulo 2
| rol eax,cl | rol rax,cl | _rotl | _rotl64 | (a<<b|a>>32-b) | (a<<b|a>>64-b) | Linksrotieren mit umlaufenden Bits
| ror eax,cl | ror rax,cl | _rotr | _rotr64 | (a<<32-b|a>>b) | (a<<64-b|a>>b) | Rechtsrotieren mit umlaufenden Bits
| Interlocked | cmpxchg [ecx],edx | cmpxchg [rcx],rdx | _InterlockedCompareExchange | __sync_val_compare_and_swap | SMP-sichere Austauschoperation
| lock inc dword[ecx] | lock inc qword[rcx] | _InterlockedIncrement | ? | SMP-sicheres Inkrementieren einer Speicherstelle
| lock dec dword[ecx] | lock dec qword[rcx] | _InterlockedIncrement | ? | SMP-sicheres Dekrementieren einer Speicherstelle
| Sonstiges | int 3 | __debugbreak | ? | Debugger-Aufruf
| |