Qual è la differenza tra _tmain () e main () in C ++?


224

Se eseguo la mia applicazione C ++ con il seguente metodo main () è tutto a posto:

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

Ottengo quello che mi aspetto e i miei argomenti vengono stampati.

Tuttavia, se uso _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;
}

Visualizza solo il primo carattere di ogni argomento.

Qual è la differenza che causa questo?

Risposte:


357

_tmainnon esiste in C ++. mainlo fa.

_tmain è un'estensione di Microsoft.

mainè, secondo lo standard C ++, il punto di ingresso del programma. Ha una di queste due firme:

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

Microsoft ha aggiunto un wmain che sostituisce la seconda firma con questa:

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

E quindi, per rendere più semplice il passaggio tra Unicode (UTF-16) e il loro set di caratteri multibyte, hanno definito _tmainquali, se Unicode è abilitato, è compilato come wmain, e altrimenti comemain .

Per quanto riguarda la seconda parte della tua domanda, la prima parte del puzzle è che la tua funzione principale è sbagliata. wmaindovrebbe prendere una wchar_tdiscussione, no char. Poiché il compilatore non applica questo per la mainfunzione, si ottiene un programma in cui un array di wchar_tstringhe viene passato alla mainfunzione, che le interpreta come charstringhe.

Ora, in UTF-16, il set di caratteri utilizzato da Windows quando Unicode è abilitato, tutti i caratteri ASCII sono rappresentati come coppia di byte \0seguita dal valore ASCII.

E poiché la CPU x86 è little-endian, l'ordine di questi byte viene scambiato, in modo che il valore ASCII venga prima, quindi seguito da un byte nullo.

E in una stringa di caratteri, come viene normalmente terminata la stringa? Sì, con un byte nullo. Quindi il tuo programma vede un mucchio di stringhe, ognuna lunga un byte.

In generale, hai tre opzioni quando esegui la programmazione di Windows:

  • Usa esplicitamente Unicode (chiama wmain, e per ogni funzione API di Windows che accetta argomenti relativi al char, chiama la -Wversione della funzione. Invece di CreateWindow, chiama CreateWindowW). E invece di usare charusewchar_t , e così via
  • Disabilita esplicitamente Unicode. Chiamare main e CreateWindowA e utilizzarechar per le stringhe.
  • Consenti entrambi. (chiama _tmain e CreateWindow, che si risolvono in main / _tmain e CreateWindowA / CreateWindowW) e usa TCHAR invece di char / wchar_t.

Lo stesso vale per i tipi di stringa definiti da windows.h: LPCTSTR si risolve in LPCSTR o LPCWSTR e per ogni altro tipo che include char o wchar_t, esiste sempre una versione -T che può essere invece utilizzata.

Si noti che tutto ciò è specifico di Microsoft. TCHAR non è un tipo C ++ standard, è una macro definita in windows.h. Anche wmain e _tmain sono definiti solo da Microsoft.


6
mi chiedo se forniscono anche un tcout? così che si potrebbe semplicemente fare tcout << argv [n]; e risolve il cout in Ansi e wcout in modalità Unicode? Ho il sospetto che potrebbe essere utile per lui in questa situazione. e +1 ovviamente, bella risposta :)
Johannes Schaub - litb

1
Quale svantaggio fornirebbe la disabilitazione di UNICODE?
joshcomley,

2
-1 Nessuna delle tre opzioni elencate è pratica. Il modo pratico di programmare Windows è definire UNICODE. E alcune altre regolazioni per C ++ ecc., Prima di includere <windows.h>. Quindi utilizzare le funzioni Unicode come CreateWindow(in generale senza Wnecessità alla fine).
Saluti e hth. - Alf

11
Perché lo ritieni esattamente più pratico?
jalf

1
"..._tmain sono anche definiti solo da Microsoft" Il tuo ultimo paragrafo è assolutamente inaccurato , _tmain è implementato esattamente lo stesso in C ++ Builder di RAD Studio. In effetti, sotto la mappatura _TCHAR predefinita di C ++ Builder , il semplice utilizzo di main fallirà.
b1nary.atr0phy

35

_tmain è una macro che viene ridefinita in base alla compilazione con Unicode o ASCII. È un'estensione Microsoft e non è garantito il funzionamento con altri compilatori.

La dichiarazione corretta è

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

Se la macro UNICODE è definita, si espande in

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

Altrimenti si espande a

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

La tua definizione vale per un po 'di ciascuno e (se hai definito UNICODE) si espanderà a

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

che è semplicemente sbagliato.

std :: cout funziona con caratteri ASCII. È necessario std :: wcout se si utilizzano caratteri ampi.

prova qualcosa del genere

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

Oppure potresti decidere in anticipo se utilizzare caratteri ampi o stretti. :-)

12 nov 2013:

Modificato il tradizionale "TCHAR" in "_TCHAR" che sembra essere l'ultima moda. Entrambi funzionano bene.

Fine aggiornamento


1
"È un'estensione Microsoft e non funzionerà su nessun altro compilatore." Non per quanto riguarda RAD Studio.
b1nary.atr0phy

@ b1naryatr0phy - Per dividere i capelli, lo strumento a cui ti colleghi utilizza "_TCHAR", anziché "TCHAR", quindi non è compatibile (sebbene falsifichi la mia affermazione). Tuttavia, avrei dovuto dire "È un'estensione di Microsoft e non è garantito il funzionamento con altri compilatori". Modificherò l'originale.
Michael J,

@MichaelJ Mi riferivo principalmente alla sezione "Modifiche al codice ...", che spiega perché RAD Studio ora utilizza _tmain al posto di main, e in realtà ora è lo standard predefinito per il C ++ Builder di Embarcadero.
b1nary.atr0phy,

1
Questa è la seconda volta di recente che questa risposta di quattro anni è stata sottoposta a downgrade. Sarebbe bello se i downvoter facessero un commento spiegando quali problemi percepiscono e (se possibile) come migliorare la risposta. b1naryatr0phy ha trovato una frase scritta male, ma l'ho risolta a marzo. Ogni prova sarebbe apprezzata.
Michael J,

2
La vita è troppo breve per questo.
Michael J,

10

la convenzione _T viene utilizzata per indicare che il programma deve utilizzare il set di caratteri definito per l'applicazione (Unicode, ASCII, MBCS, ecc.). È possibile circondare le stringhe con _T () per memorizzarle nel formato corretto.

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

In effetti, la SM raccomanda questo approccio, dopo. Rendendo la tua applicazione unicode-compatibile, la chiamano ... usando anche la versione _t di tutte le funzioni di manipolazione delle stringhe.
Deep-B,

1
@ Deep-B: E su Windows, è così che rendi la tua applicazione unicode pronta (preferisco il termine di unicode pronto per -aware), se si basava su chars prima. Se l'applicazione utilizza direttamente, wchar_tl'applicazione è Unicode.
Paercebal,

5
A proposito, se si tenta di compilare su UNICODE, il codice non verrà compilato come output wchar_t all'interno di un cout basato su char, dove avrebbe dovuto essere wcout. Vedi la risposta di Michael J per un esempio di definizione di un "tcout" ...
paercebal,

1
Nessuno se questo è raccomandato da Microsoft, in gran parte, perché è chiaramente sbagliato. Durante la compilazione per Unicode, il codice scrive i valori del puntatore nel flusso di output standard. -1.
Indispensabile il

5

Ok, sembra che la domanda abbia avuto una risposta abbastanza buona, il sovraccarico UNICODE dovrebbe prendere una vasta gamma di caratteri come secondo parametro. Quindi, se il parametro della riga di comando è "Hello"che probabilmente finirebbe come "H\0e\0l\0l\0o\0\0\0"e il tuo programma stamperebbe solo il file'H' prima di vedere ciò che pensa sia un terminatore nullo.

Quindi ora potresti chiederti perché compili e collegamenti.

Bene si compila perché ti è permesso definire un sovraccarico per una funzione.

Il collegamento è un problema leggermente più complesso. In C non ci sono informazioni sui simboli decorati, quindi trova semplicemente una funzione chiamata main. Probabilmente argc e argv sono sempre presenti come parametri dello stack di chiamate nel caso in cui la funzione sia definita con quella firma, anche se la funzione li ignora.

Anche se C ++ ha simboli decorati, quasi certamente usa C-linkage per main, piuttosto che un linker intelligente che cerca ciascuno a sua volta. Quindi ha trovato il tuo wmain e ha messo i parametri nello stack di chiamate nel caso fosse la int wmain(int, wchar_t*[])versione.


Ok, quindi ho problemi a trasferire il mio codice su Windows Widechar ormai da anni e QUESTA è la prima volta che capisco perché questo accade. Ecco, prendi tutta la mia reputazione! haha
Leonel,

-1

Con un piccolo sforzo di templatizzazione di questo, ha funzionato con qualsiasi elenco di oggetti.

#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';
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.