web-dev-qa-db-fra.com

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

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

Je reçois 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 causant ceci?

221
joshcomley

_tmain n'existe pas en C++. main fait.

_tmain est une extension Microsoft.

main est, selon la norme C++, le point d'entrée du programme. Il a 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[]);

Ensuite, pour faciliter le basculement entre Unicode (UTF-16) et leur jeu de caractères multi-octets, ils ont défini _tmain qui, si Unicode est activé, est compilé sous le nom wmain et sinon sous la forme main.

En ce qui concerne la deuxième partie de votre question, la première partie du puzzle est que votre fonction principale est mauvaise. wmain devrait prendre un argument wchar_t, et non char. Comme le compilateur ne l'applique pas pour la fonction main, vous obtenez un programme dans lequel un tableau de chaînes wchar_t est transmis à la fonction main qui les interprète comme des chaînes char.

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

Et puisque le processeur x86 est un petit bout, l'ordre de ces octets est inversé, de sorte que la valeur ASCII vienne en premier, 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 octet nul. Ainsi, votre programme voit un tas de chaînes, chacune sur un octet.

En général, vous avez trois options pour programmer en Windows:

  • Utilisez explicitement Unicode (appelez wmain, et pour chaque fonction de l'API Windows prenant des arguments relatifs aux caractères, appelez la version -W de la fonction. Au lieu de CreateWindow, appelez CreateWindowW). Et au lieu d'utiliser char, utilisez wchar_t, etc.
  • Désactiver explicitement Unicode. Appelez main et CreateWindowA et utilisez char pour les chaînes.
  • Autoriser les deux. (appelez _tmain et CreateWindow, qui se résolvent en principal/_tmain et CreateWindowA/CreateWindowW) et utilisez TCHAR au lieu de char/wchar_t.

La même chose s'applique aux types de chaînes définis par windows.h: LPCTSTR est résolu en LPCSTR ou en LPCWSTR, et pour tout autre type incluant char ou wchar_t, il existe toujours une version -T 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.

348
jalf

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

La déclaration correcte est

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

Si la macro UNICODE est définie, cela 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 chaque, et (si vous avez défini UNICODE) sera étendu à

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

ce qui est tout simplement faux.

std :: cout fonctionne avec les 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 "TCHAR" traditionnel en "_TCHAR" qui semble être la dernière mode. Les deux fonctionnent bien.

Fin de mise à jour

35
Michael J

la convention _T indique 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 de caractères avec _T () pour les stocker au format correct.

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

Ok, la question semble avoir été assez bien répondue, la surcharge UNICODE devrait prendre un tableau de caractères larges comme second paramètre. Donc, si le paramètre de ligne de commande est "Hello", il s’agira probablement de "H\0e\0l\0l\0o\0\0\0" et votre programme n’imprimera que le 'H' avant qu’il ne voie ce qu’il considère comme un terminateur nul.

Alors maintenant, vous pouvez vous demander pourquoi il compile et lie même.

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’ya pas d’information de symbole décorée, il ne trouve donc que la fonction main. Les commandes argc et argv sont probablement toujours présentes en tant que paramètres de pile d'appels, même si votre fonction est définie avec cette signature, même si votre fonction les ignore.

Bien que le C++ ait des symboles décorés, il utilise presque certainement le couplage C comme principal, plutôt qu'un lieur intelligent qui les recherche à tour de rôle. Donc, il a trouvé votre wmain et mis les paramètres sur la pile d'appels au cas où il s'agisse de la version int wmain(int, wchar_t*[]).

5
CashCow