Quelle est la différence entre _tmain () et main () en C ++?

224

Si j'exécute mon application C ++ avec la méthode main () suivante, tout est OK:

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

J'obtiens ce que j'attends et mes arguments sont imprimés.

Cependant, si j'utilise _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;
}

Il affiche simplement le premier caractère de chaque argument.

Quelle est la différence à l'origine de cela?

joshcomley
la source

Réponses:

357

_tmainn'existe pas en C ++. mainEst-ce que.

_tmain est une extension Microsoft.

mainest, selon la norme C ++, le point d'entrée du programme. Il possède l'une de ces deux signatures:

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

Microsoft a ajouté un wmain qui remplace la deuxième signature par ceci:

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

Et puis, pour faciliter le basculement entre Unicode (UTF-16) et leur jeu de caractères multi-octets, ils ont défini _tmainlequel, si Unicode est activé, est compilé en tant que wmain, et sinon en tant que main.

Quant à la deuxième partie de votre question, la première partie du puzzle est que votre fonction principale est incorrecte. wmaindevrait prendre un wchar_targument, non char. Comme le compilateur n'applique pas cela pour la mainfonction, vous obtenez un programme où un tableau de wchar_tchaînes est passé à la mainfonction, qui les interprète comme des charchaînes.

Maintenant, en UTF-16, le jeu de caractères utilisé par Windows lorsque Unicode est activé, tous les caractères ASCII sont représentés comme la paire d'octets \0suivie de la valeur ASCII.

Et comme le processeur x86 est peu endian, l'ordre de ces octets est inversé, de sorte que la valeur ASCII vient en premier, puis est suivie d'un octet nul.

Et dans une chaîne de caractères, comment la chaîne se termine-t-elle généralement? Oui, par un octet nul. Votre programme voit donc un tas de chaînes, chacune d'un octet de long.

En général, vous avez trois options lors de la programmation Windows:

  • Utilisez explicitement Unicode (appelez wmain, et pour chaque fonction API Windows qui prend des arguments liés aux caractères, appelez la -Wversion de la fonction. Au lieu de CreateWindow, appelez CreateWindowW). Et au lieu d'utiliser l' charutilisation wchar_t, etc.
  • Désactivez explicitement Unicode. Appelez main et CreateWindowA et utilisez charpour les chaînes.
  • Autorisez les deux. (appelez _tmain et CreateWindow, qui se résolvent en main / _tmain et CreateWindowA / CreateWindowW), et utilisez TCHAR au lieu de char / wchar_t.

La même chose s'applique aux types de chaîne définis par windows.h: LPCTSTR se résout en LPCSTR ou LPCWSTR, et pour tout autre type qui inclut char ou wchar_t, une version -T- existe toujours qui peut être utilisée à la place.

Notez que tout cela est spécifique à Microsoft. TCHAR n'est pas un type C ++ standard, c'est une macro définie dans windows.h. wmain et _tmain sont également définis par Microsoft uniquement.

jalf
la source
6
je me demande s'ils fournissent un tcout aussi? pour qu'on puisse simplement faire tcout << argv [n]; et il se résout à cout en Ansi et wcout en mode Unicode? Je soupçonne que cela pourrait lui être utile dans cette situation. et +1 bien sûr, belle réponse :)
Johannes Schaub - litb
1
Quel inconvénient offrirait la désactivation d'UNICODE?
joshcomley
2
-1 Aucune des trois options énumérées n'est pratique. La façon pratique de programmer Windows est de définir UNICODE. Et quelques autres ajustements pour C ++ etc., avant d'inclure <windows.h>. Utilisez ensuite les fonctions Unicode comme CreateWindow(en général sans Wbesoin à la fin).
Bravo et hth. - Alf
11
Pourquoi considérez-vous exactement cela comme plus pratique?
jalf
1
"..._ tmain sont également définis par Microsoft uniquement" Votre dernier paragraphe est absolument inexact , _tmain est implémenté exactement de la même façon dans C ++ Builder de RAD Studio. En fait, sous le mappage _TCHAR par défaut de C ++ Builder , le simple fait d'utiliser main échouera.
b1nary.atr0phy
35

_tmain est une macro qui est redéfinie selon que vous compilez avec Unicode ou ASCII. Il s'agit d'une extension Microsoft et il n'est pas garanti de fonctionner sur d'autres compilateurs.

La bonne déclaration est

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

Si la macro UNICODE est définie, elle se développe en

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

Sinon, il se développe pour

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

Votre définition va pour un peu de chacun, et (si vous avez défini UNICODE) s'étendra à

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

ce qui est tout simplement faux.

std :: cout fonctionne avec des caractères ASCII. Vous avez besoin de std :: wcout si vous utilisez des caractères larges.

essayez quelque chose comme ça

#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 vous pouvez simplement décider à l'avance d'utiliser des caractères larges ou étroits. :-)

Mis à jour le 12 nov.2013:

Changé le traditionnel "TCHAR" en "_TCHAR" qui semble être la dernière mode. Les deux fonctionnent bien.

Mettre à jour la fin

Michael J
la source
1
"Il s'agit d'une extension Microsoft et ne fonctionnera sur aucun autre compilateur." Pas en ce qui concerne RAD Studio.
b1nary.atr0phy
@ b1naryatr0phy - Pour diviser les cheveux, l'outil auquel vous vous connectez utilise "_TCHAR", plutôt que "TCHAR", il n'est donc pas compatible (bien qu'il fausse ma déclaration). Cependant, j'aurais dû dire "C'est une extension Microsoft et il n'est pas garanti de fonctionner sur d'autres compilateurs.". Je vais modifier l'original.
Michael J
@MichaelJ Je faisais principalement référence à la section "Modifications de code ...", qui explique pourquoi RAD Studio utilise désormais _tmain à la place de main, et en fait, c'est maintenant la valeur par défaut standard pour C ++ Builder d'Embarcadero.
b1nary.atr0phy
1
C'est la deuxième fois récemment que cette réponse vieille de quatre ans est rejetée. Ce serait bien si les downvoters faisaient un commentaire expliquant quels problèmes ils perçoivent et (si possible) comment améliorer la réponse. b1naryatr0phy a trouvé une phrase mal écrite, mais je l'ai corrigée en mars. Toute preuve serait appréciée.
Michael J
2
La vie est trop courte pour ça.
Michael J
10

la convention _T est utilisée pour indiquer que le programme doit utiliser le jeu de caractères défini pour l'application (Unicode, ASCII, MBCS, etc.). Vous pouvez entourer vos chaînes avec _T () pour les stocker dans le format correct.

 cout << _T( "There are " ) << argc << _T( " arguments:" ) << endl;
Paul Alexander
la source
En fait, MS recommande cette approche, afaik. Rendant votre application sensible à l'unicode, ils l'appellent ... en utilisant également la version _t de toutes les fonctions de manipulation de chaînes.
Deep-B
1
@ Deep-B: Et sous Windows, c'est ainsi que vous rendez votre application prête pour unicode (je préfère le terme de prêt pour unicode à la connaissance), si elle était basée sur chars auparavant. Si votre application utilise directement, wchar_tvotre application est unicode.
paercebal
5
Soit dit en passant, si vous essayez de compiler sur UNICODE, votre code ne sera pas compilé en tant que wchar_t en sortie dans un cout basé sur des caractères, où il aurait dû être wcout. Voir la réponse de Michael J pour un exemple de définition d'une "tcout" ...
paercebal
1
Aucune si cela est recommandé par Microsoft, en grande partie, car c'est tout simplement faux. Lors de la compilation pour Unicode, le code écrit des valeurs de pointeur dans le flux de sortie standard. -1.
IIspectable
5

Ok, la question semble avoir été assez bien répondu, la surcharge UNICODE devrait prendre un large tableau de caractères comme deuxième paramètre. Donc, si le paramètre de ligne de commande est, "Hello"cela finirait probablement par "H\0e\0l\0l\0o\0\0\0"et votre programme afficherait uniquement le 'H'avant de voir ce qu'il pense être un terminateur nul.

Alors maintenant, vous vous demandez peut-être pourquoi il compile et établit des liens.

Eh bien, il compile parce que vous êtes autorisé à définir une surcharge pour une fonction.

La liaison est un problème légèrement plus complexe. En C, il n'y a aucune information de symbole décoré, donc il trouve juste une fonction appelée main. L'argc et l'argv sont probablement toujours là en tant que paramètres de pile d'appels au cas où même si votre fonction est définie avec cette signature, même si votre fonction les ignore.

Même si C ++ possède des symboles décorés, il utilise presque certainement C-linkage pour main, plutôt qu'un éditeur de liens intelligent qui les recherche tour à tour. Il a donc trouvé votre wmain et mis les paramètres sur la pile d'appels au cas où ce serait la int wmain(int, wchar_t*[])version.

Vache à lait
la source
Ok, donc j'ai du mal à porter mon code sur Windows Widechar depuis des années maintenant et c'est la première fois que je comprends pourquoi cela se produit. Tiens, prends toute ma réputation! haha
Leonel
-1

Avec un petit effort de création de modèles, cela fonctionnera avec n'importe quelle liste d'objets.

#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';
}
Mégévolution
la source