Jaka jest różnica między _tmain () i main () w C ++?

224

Jeśli uruchomię aplikację C ++ przy użyciu następującej metody main (), wszystko będzie w porządku:

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

Dostaję to, czego oczekuję, a moje argumenty są drukowane.

Jeśli jednak użyję _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;
}

Wyświetla tylko pierwszy znak każdego argumentu.

Jaka to różnica?

joshcomley
źródło

Odpowiedzi:

357

_tmainnie istnieje w C ++. mainrobi.

_tmain jest rozszerzeniem Microsoft.

mainjest, 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 tym:

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

Następnie, aby ułatwić przełączanie między Unicode (UTF-16) a ich wielobajtowym zestawem znaków, zdefiniowali, _tmainktóry, jeśli włączony jest Unicode, jest kompilowany jako wmain, a inaczej jako main.

Jeśli chodzi o drugą część twojego pytania, pierwszą częścią układanki jest to, że twoja główna funkcja jest zła. wmainpowinien wziąć wchar_targument, nie char. Ponieważ kompilator nie wymusza tego dla mainfunkcji, otrzymujesz program, w którym tablica wchar_tciągów znaków jest przekazywana do mainfunkcji, która interpretuje je jako charciągi znaków.

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

A ponieważ procesor x86 jest little-endian, kolejność tych bajtów jest zamieniana, tak że najpierw pojawia się wartość ASCII, a następnie bajt zerowy.

A w ciągu znaków char, jak zwykle jest zakończony? Tak, bajt zerowy. Więc twój program widzi kilka ciągów znaków, każdy o długości jednego bajtu.

Ogólnie rzecz biorąc, masz trzy opcje podczas programowania w systemie Windows:

  • Jawnie użyj Unicode (wywołaj wmain, a dla każdej funkcji Windows API, która pobiera argumenty związane z char, wywołaj -Wwersję funkcji. Zamiast CreateWindow, wywołaj CreateWindowW). I zamiast używać charuse wchar_t, i tak dalej
  • Jawnie wyłącz Unicode. Wywołaj main, CreateWindowA i użyj chardla łańcuchów.
  • Pozwól na oba. (wywołaj _tmain i CreateWindow, które rozstrzygają się na main / _tmain i CreateWindowA / CreateWindowW) i użyj TCHAR zamiast char / wchar_t.

To samo dotyczy typów ciągów zdefiniowanych przez windows.h: LPCTSTR rozwiązuje albo LPCSTR albo LPCWSTR, a dla każdego innego typu, który zawiera char lub wchar_t, zawsze istnieje wersja -T-, której można użyć zamiast tego.

Pamiętaj, że wszystko to jest specyficzne dla Microsoft. TCHAR nie jest standardowym typem C ++, jest makrem zdefiniowanym w windows.h. wmain i _tmain są również zdefiniowane tylko przez Microsoft.

jalf
źródło
6
zastanawiam się, czy oni też zapewniają tcout? aby można było zrobić tcout << argv [n]; i rozwiązuje cout w Ansi i wcout w trybie Unicode? Podejrzewam, że może się przydać w tej sytuacji. i +1 oczywiście fajna odpowiedź :)
Johannes Schaub - litb
1
Jaką wadę miałoby wyłączenie UNICODE?
joshcomley
2
-1 Żadna z trzech wymienionych opcji nie jest praktyczna. Praktycznym sposobem programowania systemu Windows jest zdefiniowanie UNICODE. I kilka innych poprawek dla C ++ itp., Przed włączeniem <windows.h>. Następnie użyj funkcji Unicode takich jak CreateWindow(na ogół bez Wpotrzeby na końcu).
Pozdrawiam i hth. - Alf
11
Dlaczego uważacie to za bardziej praktyczne?
jalf
1
„... tmain są również definiowane tylko przez Microsoft” Twój ostatni akapit jest absolutnie niedokładny , _tmain jest zaimplementowany dokładnie tak samo w Kreatorze C ++ RAD Studio. W rzeczywistości, zgodnie z domyślnym mapowaniem _TCHAR w C ++ Builder , zwykłe użycie main zakończy się niepowodzeniem.
b1nary.atr0phy
35

_tmain to makro, które zmienia się w zależności od tego, czy kompilujesz z Unicode czy ASCII. Jest to rozszerzenie Microsoft i nie ma gwarancji, że będzie działać na innych kompilatorach.

Prawidłowa deklaracja to

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

Jeśli zdefiniowane jest makro UNICODE, rozwija się ono do

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

W przeciwnym razie rozwija się do

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

Twoja definicja obejmuje trochę każdego z nich i (jeśli masz zdefiniowany UNICODE) rozwinie się do

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

co jest po prostu błędne.

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

spróbuj czegoś 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;
}

Możesz też z góry zdecydować, czy użyć szerokich, czy wąskich znaków. :-)

Zaktualizowano 12 listopada 2013:

Zmieniono tradycyjny „TCHAR” na „_TCHAR”, który wydaje się być najnowszą modą. Oba działają dobrze.

Zakończ aktualizację

Michael J.
źródło
1
„Jest to rozszerzenie Microsoft i nie działa na żadnym innym kompilatorze”. Nie, jeśli chodzi o RAD Studio.
b1nary.atr0phy
@ b1naryatr0phy - Aby podzielić włosy, narzędzie, do którego linkujesz, używa „_TCHAR” zamiast „TCHAR”, więc nie jest kompatybilne (chociaż fałszuje moje stwierdzenie). Jednak powinienem powiedzieć: „Jest to rozszerzenie Microsoft i nie ma gwarancji, że będzie działać na innych kompilatorach”. Zmienię oryginał.
Michael J
@MichaelJ Odniosłem się głównie do sekcji „Zmiany kodu ...”, która wyjaśnia, dlaczego RAD Studio używa teraz _tmain zamiast main, a tak naprawdę jest teraz standardową domyślną wersją Embarcadero C ++ Builder.
b1nary.atr0phy
1
To już drugi raz, kiedy ta czteroletnia odpowiedź została odrzucona. Byłoby miło, gdyby downvoters skomentowali, jakie problemy dostrzegają i (jeśli to możliwe), jak poprawić odpowiedź. b1naryatr0phy znalazł źle napisane zdanie, ale naprawiłem to w marcu. Wszelkie wskazówki będą mile widziane.
Michael J
2
Życie jest na to za krótkie.
Michael J
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ć swoje ciągi _T (), aby były przechowywane w odpowiednim formacie.

 cout << _T( "There are " ) << argc << _T( " arguments:" ) << endl;
Paul Alexander
źródło
W rzeczywistości państwo członkowskie zaleca takie podejście, afaik. Sprawiając, że twoja aplikacja rozpoznaje Unicode, nazywają ją ... używając również wersji _t wszystkich funkcji manipulacji ciągami.
Deep-B
1
@ Głębokie-B: A na Windows, to jest , w jaki sposób uczynić aplikacja unicode-ready (wolę określenie Unicode gotowe do -aware), jeśli został on oparty chars wcześniej. Jeśli aplikacja używa bezpośrednio, oznacza wchar_tto, że jest to kod Unicode.
paercebal
5
Nawiasem mówiąc, jeśli spróbujesz skompilować na UNICODE, twój kod nie będzie się kompilował, ponieważ Twój wynik będzie wchodził do couta opartego na znakach, gdzie powinien był być wcout. Zobacz odpowiedź Michaela J na przykład zdefiniowania „tcout” ...
paercebal
1
Brak, jeśli jest to zalecane przez Microsoft, w dużej mierze, ponieważ jest to po prostu złe. Podczas kompilacji dla Unicode kod zapisuje wartości wskaźnika do standardowego strumienia wyjściowego. -1.
Widoczny
5

Ok, wydaje się, że odpowiedź na to pytanie jest dość dobra, przeciążenie UNICODE powinno zająć szeroką tablicę znaków jako drugi parametr. Więc jeśli parametr wiersza poleceń jest "Hello"taki, że prawdopodobnie skończyłby się tak, "H\0e\0l\0l\0o\0\0\0"a twój program wydrukowałby tylko 'H'zanim zobaczy, co uważa za terminator zerowy.

Więc teraz możesz się zastanawiać, dlaczego nawet się kompiluje i łączy.

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

Łączenie jest nieco bardziej złożonym problemem. W C nie ma dekorowanych informacji o symbolach, więc znajduje funkcję o nazwie main. Argc i argv są zawsze dostępne jako parametry stosu wywołań na wszelki wypadek, nawet jeśli twoja funkcja jest zdefiniowana z tą sygnaturą, nawet jeśli twoja funkcja je zignoruje.

Mimo że C ++ ma udekorowane symbole, prawie na pewno używa C-linkage dla main, a nie sprytnego linkera, który szuka każdego z nich z kolei. Więc znalazł twojego wmaina i umieścił parametry na stosie wywołań na wypadek, gdyby była to int wmain(int, wchar_t*[])wersja.

Dojną krową
źródło
Ok, więc mam problem z przenoszeniem mojego kodu do Windows Widechar od lat i To jest pierwszy raz, kiedy zrozumiałem, dlaczego tak się dzieje. Proszę, weź całą moją reputację! haha
Leonel
-1

Przy odrobinie wysiłku, aby to zrobić, to zadziałało 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';
}
Misgevolution
źródło