2009-05-21 6 views
209

Wenn ich meine C++ Anwendung mit der folgenden Methode main() alles laufen ist OK:Was ist der Unterschied zwischen _tmain() und main() in C++?

int main(int argc, char *argv[]) 
{ 
    cout << "There are " << argc << " arguments:" << endl; 

    // Loop through each argument and print its number and value 
    for (int i=0; i<argc; i++) 
     cout << i << " " << argv[i] << endl; 

    return 0; 
} 

ich bekommen, was ich erwarte, und meine Argumente ausgedruckt.

Allerdings, wenn ich verwenden _tmain:

int _tmain(int argc, char *argv[]) 
{ 
    cout << "There are " << argc << " arguments:" << endl; 

    // Loop through each argument and print its number and value 
    for (int i=0; i<argc; i++) 
     cout << i << " " << argv[i] << endl; 

    return 0; 
} 

Es zeigt nur das erste Zeichen jedes Argument.

Was verursacht den Unterschied?

Antwort

322

_tmain existiert nicht in C++. main tut.

_tmain ist eine Microsoft-Erweiterung.

main ist nach dem C++ - Standard der Einstiegspunkt des Programms. Es ist einer dieser beiden Unterschriften hat:

int main(); 
int main(int argc, char* argv[]); 

Microsoft eine wmain hinzugefügt, die die zweite Signatur mit dieser ersetzt:

int wmain(int argc, wchar_t* argv[]); 

Und dann, um es einfacher zwischen Unicode zu wechseln (UTF- 16) und ihre Multibyte-Zeichensatz, haben sie _tmain definiert, die, wenn Unicode aktiviert ist, als wmain kompiliert wird und ansonsten als main.

Wie im zweiten Teil Ihrer Frage, der erste Teil des Puzzles ist, dass Ihre Hauptfunktion falsch ist. wmain sollte ein wchar_t Argument nehmen, nicht char. Da der Compiler dies für die main-Funktion nicht erzwingt, erhalten Sie ein Programm, in dem ein Array von wchar_t-Strings an die main-Funktion übergeben wird, die sie als char Zeichenfolgen interpretiert.

In UTF-16, dem Zeichensatz, der von Windows verwendet wird, wenn Unicode aktiviert ist, werden alle ASCII-Zeichen als das Bytepaar \0 dargestellt, gefolgt vom ASCII-Wert.

Und da die x86-CPU Little-Endian ist, wird die Reihenfolge dieser Bytes vertauscht, so dass der ASCII-Wert zuerst kommt, gefolgt von einem Null-Byte.

Und in einer Zeichenkette, wie ist die Zeichenfolge in der Regel beendet? Ja, durch ein Nullbyte. Ihr Programm sieht also eine Reihe von Strings, die jeweils ein Byte lang sind.

In der Regel haben Sie drei Möglichkeiten, wenn die Windows-Programmierung zu tun:

  • Explizit Unicode (Call- wmain, und für jede Windows-API-Funktion, die char bezogene Argumente, rufen Sie die -W Version der Funktion übernimmt.Rufen Sie CreateWindowW (statt CreateWindow) auf. Und statt char verwenden Sie wchar_t, und so weiter
  • Explizit deaktivieren Sie Unicode. Rufen Sie main und CreateWindowA auf und verwenden Sie char für Strings.
  • Erlauben beide. (Rufen Sie _tmain und CreateWindow auf, die zu main/_tmain und CreateWindowA/CreateWindowW aufgelöst werden), und verwenden Sie TCHAR anstelle von char/wchar_t.

Das gleiche gilt für die Abkürzungen von windows.h definiert: LPCTSTR löst entweder LPCSTR oder LPCWSTR, und für jeden anderen Typ, der char oder wchar_t enthält, eine -T- Version existiert immer, welche verwendet werden können, stattdessen.

Beachten Sie, dass dies alles Microsoft-spezifisch ist. TCHAR ist kein Standard-C++ - Typ, es ist ein Makro, das in windows.h definiert ist. wmain und _tmain werden ebenfalls nur von Microsoft definiert.

+6

ich frage mich, ob sie auch ein tcout bieten? damit man einfach << argv [n] tun könnte; und es löst in Ansi und WCout im Unicode-Modus zu cout? Ich vermute, dass das für ihn in dieser Situation nützlich sein könnte. und +1 natürlich, nette Antwort :) –

+1

Welchen Nachteil würde UNICODE deaktivieren? – joshcomley

+0

@Johannes Schaub - littb: AFAIK, sie bieten kein tcout (auch wenn sie cout und wcout anbieten). In Visual C++ 2003 musste ich einen definieren und alle anderen STL-bezogenen Symbole definieren und/oder schreiben, die ich mit TCHAR verwenden wollte. Ich weiß jedoch nicht auf Visual C++ 2008 oder 2010. – paercebal

10

Die Konvention _T wird verwendet, um anzugeben, dass das Programm den für die Anwendung definierten Zeichensatz verwenden soll (Unicode, ASCII, MBCS usw.). Sie können Ihre Zeichenfolgen mit _T() umgeben, um sie im richtigen Format zu speichern.

cout << _T("There are ") << argc << _T(" arguments:") << endl; 
+0

In der Tat empfiehlt MS diesen Ansatz, afaik. Wenn Sie Ihre Anwendung Unicode-fähig machen, nennen sie das ... auch die _t-Version aller String-Manipulationsfunktionen. –

+1

@ Deep-B: Und unter Windows ist ** das **, wie Sie Ihre Anwendung Unicode-Ready machen (ich bevorzuge den Begriff Unicode-Ready to -ware), wenn es vorher auf 'char's basiert. Wenn Ihre Anwendung direkt 'wchar_t' verwendet, ist Ihre Anwendung ** ** Unicode. – paercebal

+5

Übrigens, wenn Sie versuchen, auf UNICODE zu kompilieren, wird Ihr Code nicht als Ihre Ausgabe wchar_t in einem char-basierten Cout kompilieren, wo es hätte wcout sein sollen. Siehe Michael Js Antwort für ein Beispiel für die Definition eines "tcout" ... – paercebal

34

_tmain ist ein Makro, das je nachdem, ob Sie mit Unicode oder ASCII kompilieren, neu definiert wird. Es handelt sich um eine Microsoft-Erweiterung, und es wird nicht garantiert, dass sie auf anderen Compilern funktioniert.

Die richtige Erklärung ist

int _tmain(int argc, _TCHAR *argv[]) 

Wenn das Makro UNICODE definiert ist, die

int wmain(int argc, wchar_t *argv[]) 

zu

erweitert Ansonsten dehnt es sich auf

int main(int argc, char *argv[]) 

Ihre Definition geht für ein bisschen Jeder und (wenn Sie UNICODE definiert haben) wird auf

erweitert
int wmain(int argc, char *argv[]) 

was einfach falsch ist.

Std :: Cout arbeitet mit ASCII-Zeichen. Sie benötigen std :: wcout, wenn Sie breite Zeichen verwenden.

versuchen so etwas wie diese

#include <iostream> 
#include <tchar.h> 

#if defined(UNICODE) 
    #define _tcout std::wcout 
#else 
    #define _tcout std::cout 
#endif 

int _tmain(int argc, _TCHAR *argv[]) 
{ 
    _tcout << _T("There are ") << argc << _T(" arguments:") << std::endl; 

    // Loop through each argument and print its number and value 
    for (int i=0; i<argc; i++) 
     _tcout << i << _T(" ") << argv[i] << std::endl; 

    return 0; 
} 

Oder Sie könnten nur im Voraus entscheiden, ob breite oder schmale Zeichen zu verwenden. :-)

12 Nov 2013 Aktualisiert:

das traditionelle "TCHAR" auf "_TCHAR", die die neueste Mode zu sein scheint. Beide funktionieren gut.

End-Update

+1

+1 '_tmain' verwendet' TCHAR' nicht 'char'. – user7116

+1

* "Es ist eine Microsoft-Erweiterung und funktioniert nicht mit anderen Compilern." * [Nicht so weit wie RAD Studio.] (Http://docwiki.embarcadero.com/RADStudio/XE3/en/TCHAR_Mapping) –

+0

@ b1naryatr0phy - Um Haare zu teilen, verwendet das Werkzeug, mit dem Sie verlinken, "_TCHAR" anstatt "TCHAR", also ist es nicht kompatibel (obwohl es meine Aussage verfälscht). Allerdings hätte ich sagen sollen: "Es handelt sich um eine Microsoft-Erweiterung, und es ist nicht garantiert, dass sie auf anderen Compilern funktioniert." Ich werde das Original ändern. –

5

Ok, scheint die Frage war recht gut beantwortet zu haben, sollte die UNICODE Überlastung einen breiten Zeichen-Array als zweiten Parameter übernehmen. Wenn also der Befehlszeilenparameter "Hello" ist, würde das wahrscheinlich als "H\0e\0l\0l\0o\0\0\0" enden und Ihr Programm würde nur die 'H' drucken, bevor es sieht, was es denkt, ist ein Null-Terminator.

So können Sie sich jetzt fragen, warum es sogar kompiliert und verbindet.

Nun, es kompiliert, weil Sie eine Überladung zu einer Funktion definieren dürfen.

Die Verknüpfung ist ein etwas komplexeres Problem. In C gibt es keine verzierten Symbolinformationen, so dass es nur eine Funktion namens main findet. Argc und argv sind wahrscheinlich immer dann als Call-Stack-Parameter da, auch wenn Ihre Funktion mit dieser Signatur definiert ist, auch wenn Ihre Funktion sie ignoriert.

Auch wenn C++ Symbole verziert hat, verwendet es fast sicher C-Verknüpfung für Haupt, anstatt ein cleverer Linker, der nach jedem der Reihe nach sucht. Also hat es deinen wmain gefunden und die Parameter auf den Call-Stack gesetzt, falls es die int wmain(int, wchar_t*[]) Version ist.

+0

Ok, also habe ich seit Jahren Probleme, meinen Code in Windows-Widechar zu portieren, und DAS ist das erste Mal, dass ich verstehe, warum das passiert. Hier, nimm meinen ganzen Ruf! Haha – Leonel

0

Mit ein wenig Aufwand der Templatisierung, es funktioniert mit jeder Liste von Objekten.

#include <iostream> 
#include <string> 
#include <vector> 

char non_repeating_char(std::string str){ 
    while(str.size() >= 2){ 
     std::vector<size_t> rmlist; 
     for(size_t i = 1; i < str.size(); i++){   
      if(str[0] == str[i]) { 
       rmlist.push_back(i); 
      }  
     }   

     if(rmlist.size()){    
      size_t s = 0; // Need for terator position adjustment 
      str.erase(str.begin() + 0); 
      ++s; 
      for (size_t j : rmlist){ 
       str.erase(str.begin() + (j-s));     
       ++s; 
      } 
     continue; 
     } 
     return str[0]; 
    } 
    if(str.size() == 1) return str[0]; 
    else return -1; 
} 

int main(int argc, char ** args) 
{ 
    std::string test = "FabaccdbefafFG"; 
    test = args[1]; 
    char non_repeating = non_repeating_char(test); 
    Std::cout << non_repeating << '\n'; 
}