Qual é a diferença entre _tmain () e main () em C ++?


224

Se eu executar meu aplicativo C ++ com o seguinte método main (), tudo ficará bem:

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

Consigo o que espero e meus argumentos são impressos.

No entanto, se eu usar _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;
}

Apenas exibe o primeiro caractere de cada argumento.

Qual é a diferença que está causando isso?

Respostas:


357

_tmainnão existe em C ++. mainfaz.

_tmain é uma extensão da Microsoft.

mainé, de acordo com o padrão C ++, o ponto de entrada do programa. Ele possui uma dessas duas assinaturas:

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

A Microsoft adicionou um wmain que substitui a segunda assinatura por esta:

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

E, para facilitar a alternância entre o Unicode (UTF-16) e seu conjunto de caracteres multibyte, eles definiram _tmainqual, se o Unicode estiver ativado, será compilado como wmaine, caso contrário, como main.

Quanto à segunda parte da sua pergunta, a primeira parte do quebra-cabeça é que sua função principal está errada. wmaindeve levar uma wchar_tdiscussão, não char. Como o compilador não impõe isso para a mainfunção, você obtém um programa no qual uma matriz de wchar_tstrings é passada para a mainfunção, que as interpreta como charstrings.

Agora, no UTF-16, o conjunto de caracteres usado pelo Windows quando o Unicode está ativado, todos os caracteres ASCII são representados como o par de bytes \0seguido pelo valor ASCII.

E como a CPU x86 é little-endian, a ordem desses bytes é trocada, para que o valor ASCII seja o primeiro e, em seguida, seguido por um byte nulo.

E em uma string de caracteres, como a string geralmente é terminada? Sim, por um byte nulo. Portanto, seu programa vê várias seqüências de caracteres, cada uma com um byte de comprimento.

Em geral, você tem três opções ao executar a programação do Windows:

  • Use explicitamente Unicode (chamada wmain e, para cada função da API do Windows que aceite argumentos relacionados a char, chame a -Wversão da função. Em vez de CreateWindow, chame CreateWindowW). E em vez de usar charuse wchar_t, e assim por diante
  • Desabilite explicitamente o Unicode. Chame main e CreateWindowA e use charpara strings.
  • Permita ambos. (chame _tmain e CreateWindow, que resolve como main / _tmain e CreateWindowA / CreateWindowW) e use TCHAR em vez de char / wchar_t.

O mesmo se aplica aos tipos de sequência definidos por windows.h: LPCTSTR resolve LPCSTR ou LPCWSTR e para todos os outros tipos que incluem char ou wchar_t, sempre existe uma versão -T que pode ser usada.

Observe que tudo isso é específico da Microsoft. TCHAR não é um tipo C ++ padrão, é uma macro definida em windows.h. wmain e _tmain também são definidos apenas pela Microsoft.


6
Gostaria de saber se eles fornecem um tcout também? para que alguém pudesse fazer tcout << argv [n]; e resolve fazer cout no Ansi e wcout no modo Unicode? Eu suspeito que isso poderia ser útil para ele nessa situação. e +1 é claro, agradável resposta :)
Johannes Schaub - litb

1
Que desvantagem a UNICODE desabilitante forneceria?
joshcomley

2
-1 Nenhuma das três opções listadas é prática. A maneira prática de programar o Windows é definir UNICODE. E alguns outros ajustes para C ++ etc., antes de incluir <windows.h>. Em seguida, use as funções Unicode como CreateWindow(em geral, sem Wnecessidade no final).
Saúde e hth. #

11
Por que exatamente você considera isso mais prático?
jalf

1
"..._ tmain também são definidos somente pela Microsoft" Seu último parágrafo é absolutamente impreciso , _tmain é implementado exatamente da mesma forma no C ++ Builder do RAD Studio. De fato, no mapeamento _TCHAR padrão do C ++ Builder , o simples uso de main falhará.
b1nary.atr0phy

35

_tmain é uma macro redefinida, dependendo se você compila ou não com Unicode ou ASCII. É uma extensão da Microsoft e não é garantido que funcione em outros compiladores.

A declaração correta é

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

Se a macro UNICODE for definida, ela se expande para

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

Caso contrário, ele se expande para

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

Sua definição vale um pouco de cada e (se você tiver UNICODE definido) será expandida para

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

o que é simplesmente errado.

std :: cout trabalha com caracteres ASCII. Você precisa std :: wcout se estiver usando caracteres largos.

tente algo assim

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

Ou você pode decidir com antecedência se deve usar caracteres largos ou estreitos. :-)

Atualizado 12 de novembro de 2013:

Alterou o tradicional "TCHAR" para "_TCHAR", que parece ser a última moda. Ambos funcionam bem.

Finalizar atualização


1
"É uma extensão da Microsoft e não funcionará em outros compiladores". Não tanto quanto o RAD Studio.
b1nary.atr0phy

@ b1naryatr0phy - Para dividir os cabelos, a ferramenta que você vincula usa "_TCHAR", em vez de "TCHAR", para que não seja compatível (embora falsifique minha declaração). No entanto, eu deveria ter dito "É uma extensão da Microsoft e não é garantido que funcione em outros compiladores". Vou alterar o original.
Michael J

@ MichaelJ: Eu estava me referindo principalmente à seção "Alterações de código ...", que explica por que o RAD Studio agora usa _tmain no lugar de main, e atualmente é o padrão padrão do C ++ Builder da Embarcadero.
b1nary.atr0phy

1
Essa é a segunda vez recentemente que essa resposta de quatro anos foi rebaixada. Seria bom se os votantes negativos fizessem um comentário explicando quais problemas eles percebem e (se possível) como melhorar a resposta. b1naryatr0phy encontrou uma frase mal escrita, mas consertei isso em março. Qualquer orientação seria apreciada.
Michael J

2
A vida é muito curta para isso.
Michael J

10

a convenção _T é usada para indicar que o programa deve usar o conjunto de caracteres definido para o aplicativo (Unicode, ASCII, MBCS, etc.). Você pode colocar suas seqüências de caracteres com _T () para armazená-las no formato correto.

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

De fato, a MS recomenda essa abordagem, ainda mais. Tornando seu aplicativo compatível com unicode, eles o chamam ... usando a versão _t de todas as funções de manipulação de strings também.
Deep-B

1
@ Deep-B: E no Windows, é assim que você torna seu aplicativo pronto para Unicode (eu prefiro o termo pronto para Unicode), se foi baseado em chars antes. Se seu aplicativo usar diretamente, wchar_tele será unicode.
22410 paercebal

5
A propósito, se você tentar compilar no UNICODE, seu código não será compilado como seu wchar_t de saída dentro de um cout baseado em char, onde deveria ter sido. Veja a resposta de Michael J para um exemplo de definição de um "tcout" ...
paercebal

1
Nenhuma, se isso for recomendado pela Microsoft, em grande parte, porque está totalmente errado. Ao compilar para Unicode, o código grava valores do ponteiro no fluxo de saída padrão. -1.
11nspectable

5

Ok, a pergunta parece ter sido respondida razoavelmente bem, a sobrecarga do UNICODE deve ter uma ampla matriz de caracteres como seu segundo parâmetro. Portanto, se o parâmetro da linha de comando for "Hello"esse, provavelmente terminará como "H\0e\0l\0l\0o\0\0\0"e seu programa apenas imprimirá o arquivo 'H'antes de ver o que ele acha que é um terminador nulo.

Então agora você pode se perguntar por que ele ainda compila e vincula.

Bem, ele compila porque você pode definir uma sobrecarga para uma função.

A vinculação é uma questão um pouco mais complexa. Em C, não há informações decorativas sobre os símbolos, apenas encontra uma função chamada main. O argc e o argv provavelmente estão sempre lá como parâmetros da pilha de chamadas, mesmo que sua função seja definida com essa assinatura, mesmo que sua função os ignore.

Embora o C ++ tenha símbolos decorados, ele certamente usa o C-linkage para main, em vez de um vinculador inteligente que procura cada um por vez. Por isso, encontrou o seu domínio e colocou os parâmetros na pilha de chamadas, caso seja a int wmain(int, wchar_t*[])versão.


Ok, então eu tenho problemas para portar meu código no windows widechar há anos e essa é a primeira vez que eu entendo por que isso acontece. Aqui, pegue toda a minha reputação! haha #
Leonel

-1

Com um pouco de esforço para modelar isso, funcionaria com qualquer lista de objetos.

#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';
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.