2009-05-21 3 views
209

Jeśli uruchomię mojej aplikacji C++ z następujących głównych metoda() wszystko jest OK:Jaka jest różnica między _tmain() i main() w 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; 
} 

mam czego oczekuję i moje argumenty są drukowane.

Jednakże, jeśli mogę użyć _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; 
} 

To po prostu wyświetla pierwszą literę każdego argumentu.

Jaka jest różnica powodująca to?

Odpowiedz

322

_tmain nie istnieje w C++. main ma.

_tmain to rozszerzenie Microsoft.

main jest, zgodnie ze standardem C++, punktem wejścia programu. Ma jeden z tych dwóch podpisów:

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

Microsoft dodał wmain który zastępuje drugi podpis z tym:

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

a potem, aby ułatwić przełączanie się między Unicode (UTF- 16) i ich wielobajtowy zestaw znaków, zdefiniowali _tmain, który, jeśli jest włączony kod Unicode, jest skompilowany jako wmain, a poza tym jako main.

Jeśli chodzi o drugą część pytania, pierwsza część układanki jest taka, że ​​główna funkcja jest błędna. wmain powinien przyjąć argument wchar_t, a nie char. Ponieważ kompilator nie wymusza tego dla funkcji main, otrzymujesz program, w którym tablica ciągów wchar_t jest przekazywana do funkcji main, co interpretuje je jako ciągi znaków.

Teraz, w UTF-16, zestaw znaków używany przez system Windows, gdy włączony jest kod Unicode, wszystkie znaki ASCII są reprezentowane jako para bajtów \0, po których następuje wartość ASCII.

A ponieważ procesor x86 jest mało-endianowy, kolejność tych bajtów jest zamieniana, tak że wartość ASCII pojawia się jako pierwsza, po której następuje bajt zerowy.

A w łańcuchu znaków, w jaki sposób łańcuch zwykle się kończy? Tak, przez bajt zerowy. Zatem twój program widzi kilka ciągów, każdy o długości jednego bajtu.

W ogóle, masz trzy opcje, gdy robi programowania Windows:

  • jawnie użyć Unicode (Call wmain, a dla każdej funkcji API systemu Windows, który bierze argumenty związane char, Call wersję funkcji -W.Zamiast CreateWindow, wywołaj CreateWindowW). I zamiast używać char używać wchar_t, i tak dalej
  • Jawnie wyłączyć Unicode. Połącz główne i CreateWindowA i użyj char dla ciągów.
  • Pozwól na oba. (wywołaj _tmain i CreateWindow, które rozdzielą na main/_tmain i CreateWindowA/CreateWindowW) i użyj TCHAR zamiast char/wchar_t.

To samo odnosi się do typów łańcuchowych zdefiniowanych przez windows.h: LPCTSTR postanawia obu LPCSTR lub LPCWSTR, i dla każdego innego rodzaju, który zawiera char lub wchar_t, A -T- zawsze istnieje wersja, która może być używana zamiast.

Należy pamiętać, że wszystko to dotyczy firmy Microsoft. TCHAR nie jest standardowym typem C++, jest to makro zdefiniowane w windows.h. wmain i _tmain są również zdefiniowane tylko przez Microsoft.

+6

Zastanawiam się, czy zapewniają one również tcout? aby można było wykonać tcout << argv [n]; i rozwiązuje się cout w Ansi i wcout w trybie Unicode? Podejrzewam, że może mu się przydać w tej sytuacji. i +1 oczywiście, fajna odpowiedź :) –

+1

Jaką wadą byłoby wyłączanie UNICODE? – joshcomley

+0

@Johannes Schaub - litb: AFAIK, nie zapewniają tcout (nawet jeśli zapewniają cout i wcout). W Visual C++ 2003 musiałem zdefiniować jeden, a także zdefiniować i/lub typedef wszystkie inne symbole związane z STL chciałem użyć TCHAR. Nie wiem jednak o Visual C++ 2008 lub 2010. – paercebal

10

Konwencja _T służy do wskazania, że ​​program powinien używać zestawu znaków zdefiniowanego dla aplikacji (Unicode, ASCII, MBCS itp.). Możesz otoczyć struny za pomocą _T(), aby zachować je w odpowiednim formacie.

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

W rzeczywistości MS zaleca takie podejście, Afaiku. Tworząc swoją aplikację w trybie unicode, nazywają ją ... przy użyciu wersji _t wszystkich funkcji manipulowania ciągami. –

+1

@ Deep-B: A w Windowsie to ** to, jak przygotujesz swoją aplikację w trybie unicode (wolę termin unicode-ready -aware), jeśli był oparty na 'char's before. Jeśli twoja aplikacja bezpośrednio używa 'wchar_t' wtedy twoja aplikacja ** to ** unicode. – paercebal

+5

Nawiasem mówiąc, jeśli spróbujesz skompilować na UNICODE, twój kod nie skompiluje się jako wyjściowy wchar_t wewnątrz couta opartego na znakach, gdzie powinien zostać wyciśnięty. Zobacz odpowiedź Michaela J na przykład definiowania "tcout" ... – paercebal

34

_tmain to makro, które zostaje przedefiniowane w zależności od tego, czy kompilujesz z Unikodem czy ASCII. Jest to rozszerzenie Microsoft i nie gwarantuje się pracy z żadnym innym kompilatorem.

Prawidłowe zgłoszenie jest

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

Jeśli makro UNICODE jest zdefiniowana, że ​​rozszerza się

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

przeciwnym razie rozszerza się

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

Twoja definicja odnosi się do kawałka każdy i (jeśli masz zdefiniowany standard UNICODE) zostanie rozwinięty do

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

co jest po prostu złe.

std :: cout działa ze znakami ASCII. Potrzebujesz std :: wytarcia, jeśli używasz szerokich znaków.

spróbować coś takiego

#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; 
} 

Albo może po prostu zdecydować z góry, czy wykorzystać szerokie lub wąskie znaki. :-)

Updated 12 lis 2013:

Zmieniono tradycyjnych "TCHAR" na "_TCHAR", który wydaje się być najnowsza moda. Oba działają dobrze.

Koniec Aktualizacja

+1

+1 '_tmain' używa' TCHAR' nie 'char'. – user7116

+1

* "Jest to rozszerzenie Microsoft i nie będzie działać na żadnym innym kompilatorze." * [Nie dotyczy to RAD Studio.] (Http://docwiki.embarcadero.com/RADStudio/XE3/en/TCHAR_Mapping) –

+0

@ b1naryatr0phy - Aby dzielić włosy, narzędzie, do którego odnośnik używa, używa "_TCHAR", a nie "TCHAR", więc nie jest kompatybilne (chociaż to fałszuje moje stwierdzenie). Jednak powinienem był powiedzieć "Jest to rozszerzenie Microsoftu i nie gwarantuje się pracy z żadnym innym kompilatorem.". Zmienię oryginał. –

5

Ok, pytanie wydaje się być dość dobrze odpowiedział, przeciążenie UNICODE powinny podjąć szeroką gamę znaków jako drugi parametr. Jeśli więc parametr wiersza poleceń to "Hello", który prawdopodobnie skończyłby się jako "H\0e\0l\0l\0o\0\0\0", a twój program wydrukowałby tylko 'H', zanim zobaczy, co uważa za terminator o wartości NULL.

Teraz możesz się zastanawiać, dlaczego to się kompiluje i łączy.

Kompiluje się, ponieważ można zdefiniować przeciążenie funkcji.

Łączenie jest nieco bardziej złożonym zagadnieniem. W C nie ma dekorowanych informacji o symbolu, więc znajduje się funkcja o nazwie main. Argumenty argc i argv są prawdopodobnie zawsze dostępne jako parametry stosu wywołań, na wszelki wypadek, nawet jeśli twoja funkcja jest zdefiniowana za pomocą tego podpisu, nawet jeśli twoja funkcja zignoruje je.

Mimo że C++ ma zdobione symbole, to prawie na pewno używa połączenia C dla głównego, a nie sprytnego linkera, który po kolei szuka. Tak więc odnalazł twoje urządzenie i umieścił parametry na stosie wywołań w przypadku, gdy jest to wersja int wmain(int, wchar_t*[]).

+0

Ok, więc mam problem z przeniesieniem mojego kodu do windows widechar od lat i TO po raz pierwszy zrozumiałem, dlaczego tak się dzieje. Tutaj, weź całą moją reputację! ha ha – Leonel

0

Przy niewielkim wysiłku templowania tego wszystkiego, będzie działać z dowolną listą obiektów.

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