Was ist der Unterschied zwischen _tmain () und main () in C ++?

224

Wenn ich meine C ++ - Anwendung mit der folgenden main () -Methode ausführe, ist alles in Ordnung:

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 bekomme was ich erwarte und meine Argumente werden ausgedruckt.

Wenn ich jedoch _tmain verwende:

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 wird nur das erste Zeichen jedes Arguments angezeigt.

Was ist der Unterschied, der dies verursacht?

Joshcomley
quelle

Antworten:

357

_tmainexistiert nicht in C ++. maintut.

_tmain ist eine Microsoft-Erweiterung.

mainist nach dem C ++ - Standard der Einstiegspunkt des Programms. Es hat eine dieser beiden Signaturen:

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

Microsoft hat ein wmain hinzugefügt, das die zweite Signatur durch Folgendes ersetzt:

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

Um den Wechsel zwischen Unicode (UTF-16) und ihrem Multibyte-Zeichensatz zu vereinfachen, haben sie definiert, _tmainwelcher, wenn Unicode aktiviert ist, als wmainund ansonsten als kompiliert wird main.

Was den zweiten Teil Ihrer Frage betrifft, so ist der erste Teil des Puzzles, dass Ihre Hauptfunktion falsch ist. wmainsollte ein wchar_tArgument nehmen, nicht char. Da der Compiler dies für die mainFunktion nicht erzwingt , erhalten Sie ein Programm, in dem ein Array von wchar_tZeichenfolgen an die mainFunktion übergeben wird, die sie als charZeichenfolgen interpretiert .

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

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

Und wie wird die Zeichenfolge in einer Zeichenfolge normalerweise beendet? Ja, um ein Null-Byte. Ihr Programm sieht also eine Reihe von Zeichenfolgen, die jeweils ein Byte lang sind.

Im Allgemeinen haben Sie bei der Windows-Programmierung drei Möglichkeiten:

  • Verwenden Sie explizit Unicode (rufen Sie wmain auf und rufen Sie für jede Windows-API-Funktion, die char-bezogene Argumente -Wakzeptiert, die Version der Funktion auf. Rufen Sie anstelle von CreateWindow CreateWindowW auf). Und anstatt Verwendung zu charverwenden wchar_t, und so weiter
  • Deaktivieren Sie Unicode explizit. Rufen Sie main und CreateWindowA auf und verwenden Sie sie charfür Zeichenfolgen.
  • Erlaube beides. (Rufen Sie _tmain und CreateWindow auf, die in main / _tmain und CreateWindowA / CreateWindowW aufgelöst werden.) Verwenden Sie TCHAR anstelle von char / wchar_t.

Gleiches gilt für die von windows.h definierten Zeichenfolgentypen: LPCTSTR wird entweder in LPCSTR oder LPCWSTR aufgelöst, und für jeden anderen Typ, der char oder wchar_t enthält, ist immer eine -T-Version vorhanden, die stattdessen verwendet werden kann.

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

jalf
quelle
6
Ich frage mich, ob sie auch einen Tcout anbieten. so dass man einfach tcout << argv [n] machen könnte; und es wird aufgelöst, in Ansi und wcout im Unicode-Modus zu couten? Ich vermute, dass dies in dieser Situation für ihn nützlich sein könnte. und +1 natürlich, nette Antwort :)
Johannes Schaub - litb
1
Welchen Nachteil würde das Deaktivieren von UNICODE haben?
Joshcomley
2
-1 Keine der drei aufgeführten Optionen ist praktisch. Die praktische Möglichkeit, Windows zu programmieren, besteht in der Definition UNICODE. Und einige andere Anpassungen für C ++ usw., bevor sie aufgenommen werden <windows.h>. Verwenden Sie dann die Unicode-Funktionen wie CreateWindow(im Allgemeinen ohne Wam Ende benötigt).
Prost und hth. - Alf
11
Warum genau halten Sie das für praktischer?
Jalf
1
"..._ tmain werden auch nur von Microsoft definiert" Ihr letzter Absatz ist absolut ungenau , _tmain ist in C ++ Builder von RAD Studio genauso implementiert. Unter der standardmäßigen _TCHAR-Zuordnung von C ++ Builder schlägt die einfache Verwendung von main fehl.
b1nary.atr0phy
35

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

Die richtige Erklärung ist

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

Wenn das Makro UNICODE definiert ist, wird das auf erweitert

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

Ansonsten erweitert es sich auf

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

Ihre Definition gilt für jeweils ein bisschen und wird (wenn Sie UNICODE definiert haben) auf erweitert

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

Das ist einfach falsch.

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

versuche so etwas

#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önnen einfach im Voraus entscheiden, ob Sie breite oder schmale Zeichen verwenden möchten. :-)

Aktualisiert am 12. November 2013:

Das traditionelle "TCHAR" wurde in "_TCHAR" geändert, was die neueste Mode zu sein scheint. Beide funktionieren gut.

Update beenden

Michael J.
quelle
1
"Es ist eine Microsoft-Erweiterung und funktioniert auf keinem anderen Compiler." Nicht so weit wie RAD Studio.
b1nary.atr0phy
@ b1naryatr0phy - Um Haare zu teilen, verwendet das Tool, mit dem Sie verknüpfen, "_TCHAR" anstelle von "TCHAR", sodass es nicht kompatibel ist (obwohl es meine Aussage verfälscht). Ich hätte jedoch sagen sollen: "Es handelt sich um eine Microsoft-Erweiterung, die auf keinem anderen Compiler funktioniert." Ich werde das Original ändern.
Michael J
@MichaelJ Ich bezog mich hauptsächlich auf den Abschnitt "Code Changes ...", in dem erklärt wird, warum RAD Studio jetzt _tmain anstelle von main verwendet und es nun die Standardvorgabe für Embarcaderos C ++ Builder ist.
b1nary.atr0phy
1
Dies ist das zweite Mal in letzter Zeit, dass diese vier Jahre alte Antwort abgelehnt wurde. Es wäre schön, wenn Downvoter einen Kommentar abgeben würden, in dem erklärt wird, welche Probleme sie wahrnehmen und (wenn möglich) wie die Antwort verbessert werden kann. b1naryatr0phy hat einen schlecht geschriebenen Satz gefunden, den ich aber im März behoben habe. Jeder Hinweis wäre dankbar.
Michael J
2
Das Leben ist dafür zu kurz.
Michael J
10

Die _T-Konvention gibt an, dass das Programm den für die Anwendung definierten Zeichensatz verwenden soll (Unicode, ASCII, MBCS usw.). Sie können Ihre Zeichenfolgen mit _T () umgeben, damit sie im richtigen Format gespeichert werden.

 cout << _T( "There are " ) << argc << _T( " arguments:" ) << endl;
Paul Alexander
quelle
Tatsächlich empfiehlt MS diesen Ansatz, afaik. Sie machen Ihre Anwendung Unicode-fähig und nennen sie ... auch mit der _t-Version aller Funktionen zur Manipulation von Zeichenfolgen.
Deep-B
1
@ Tief-B: Und auf Windows, das ist , wie Sie Ihre Anwendung Unicode-ready machen (ich bevorzuge den Begriff von Unicode-ready -Aware), wenn sie auf der Grundlage wurde chars vor. Wenn Ihre Anwendung direkt verwendet wchar_tdann Ihre Anwendung ist Unicode.
Paercebal
5
Wenn Sie versuchen, auf UNICODE zu kompilieren, wird Ihr Code übrigens nicht als Ausgabe von wchar_t in einem char-basierten cout kompiliert, wo es wcout hätte sein sollen. Siehe Michael Js Antwort für ein Beispiel für die Definition eines "tcout" ...
paercebal
1
Keine, wenn dies von Microsoft empfohlen wird, hauptsächlich, weil es einfach falsch ist. Beim Kompilieren für Unicode schreibt der Code Zeigerwerte in den Standardausgabestream. -1.
Unsichtbarer
5

Ok, die Frage scheint ziemlich gut beantwortet worden zu sein. Die UNICODE-Überladung sollte ein breites Zeichenarray als zweiten Parameter verwenden. Wenn also der Befehlszeilenparameter lautet "Hello", würde dies wahrscheinlich als enden "H\0e\0l\0l\0o\0\0\0"und Ihr Programm würde nur das drucken, 'H'bevor es sieht, was es für einen Nullterminator hält.

Jetzt fragen Sie sich vielleicht, warum es überhaupt kompiliert und verlinkt.

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

Das Verknüpfen ist ein etwas komplexeres Thema. In C gibt es keine dekorierten Symbolinformationen, daher wird nur eine Funktion namens main gefunden. Argc und argv sind wahrscheinlich immer als Call-Stack-Parameter vorhanden, nur für den Fall, dass Ihre Funktion mit dieser Signatur definiert ist, auch wenn Ihre Funktion sie ignoriert.

Obwohl C ++ verzierte Symbole hat, verwendet es mit ziemlicher Sicherheit die C-Verknüpfung für main und nicht einen cleveren Linker, der nacheinander nach jedem sucht. Also hat es Ihren wmain gefunden und die Parameter auf den Call-Stack gelegt, falls es sich um die int wmain(int, wchar_t*[])Version handelt.

Goldesel
quelle
Ok, ich habe seit Jahren Probleme, meinen Code auf Windows Widechar zu portieren, und DAS ist das erste Mal, dass ich verstehe, warum dies passiert. Hier, nimm meinen ganzen Ruf! haha
Leonel
-1

Mit ein wenig Aufwand, dies zu templatisieren, würde es mit jeder Liste von Objekten funktionieren.

#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';
}
Fehlentwicklung
quelle