¿Cuál es la diferencia entre _tmain () y main () en C ++?

224

Si ejecuto mi aplicación C ++ con el siguiente método main (), todo está bien:

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

Obtengo lo que espero y mis argumentos se imprimen.

Sin embargo, si 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;
}

Simplemente muestra el primer carácter de cada argumento.

¿Cuál es la diferencia que causa esto?

joshcomley
fuente

Respuestas:

357

_tmainno existe en C ++. mainhace.

_tmain es una extensión de Microsoft.

maines, según el estándar C ++, el punto de entrada del programa. Tiene una de estas dos firmas:

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

Microsoft ha agregado un wmain que reemplaza la segunda firma con esto:

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

Y luego, para facilitar el cambio entre Unicode (UTF-16) y su conjunto de caracteres multibyte, han definido _tmainqué, si Unicode está habilitado, se compila como wmain, y de lo contrario como main.

En cuanto a la segunda parte de su pregunta, la primera parte del rompecabezas es que su función principal es incorrecta. wmaindebería tomar una wchar_tdiscusión, no char. Como el compilador no aplica esto para la mainfunción, obtienes un programa en el que wchar_tse pasan una serie de cadenas a la mainfunción, que las interpreta como charcadenas.

Ahora, en UTF-16, el juego de caracteres utilizado por Windows cuando Unicode está habilitado, todos los caracteres ASCII se representan como el par de bytes \0seguido del valor ASCII.

Y dado que la CPU x86 es little-endian, el orden de estos bytes se intercambia, de modo que el valor ASCII viene primero, luego seguido de un byte nulo.

Y en una cadena de caracteres, ¿cómo se termina generalmente la cadena? Sí, por un byte nulo. Por lo tanto, su programa ve un montón de cadenas, cada una de ellas en bytes.

En general, tiene tres opciones al hacer la programación de Windows:

  • Utilice explícitamente Unicode (llame a wmain, y para cada función de API de Windows que tome argumentos relacionados con caracteres, llame a la -Wversión de la función. En lugar de CreateWindow, llame a CreateWindowW). Y en lugar de usar el charuso wchar_t, etc.
  • Desactiva explícitamente Unicode. Llame a main y CreateWindowA, y use charpara cadenas.
  • Permitir ambos. (llame a _tmain y CreateWindow, que resuelven a main / _tmain y CreateWindowA / CreateWindowW), y usan TCHAR en lugar de char / wchar_t.

Lo mismo se aplica a los tipos de cadena definidos por windows.h: LPCTSTR se resuelve en LPCSTR o LPCWSTR, y para cualquier otro tipo que incluya char o wchar_t, siempre existe una versión -T- que puede utilizarse en su lugar.

Tenga en cuenta que todo esto es específico de Microsoft. TCHAR no es un tipo estándar de C ++, es una macro definida en windows.h. wmain y _tmain también están definidos solo por Microsoft.

jalf
fuente
66
Me pregunto si también proporcionan un tcout. para que uno pueda hacer tcout << argv [n]; y resuelve cout en Ansi y wcout en modo Unicode? Sospecho que podría ser útil para él en esta situación. y +1 por supuesto, buena respuesta :)
Johannes Schaub - litb
1
¿Qué desventaja proporcionaría la desactivación de UNICODE?
joshcomley
2
-1 Ninguna de las tres opciones enumeradas son prácticas. La forma práctica de programar Windows es definir UNICODE. Y algunos otros ajustes para C ++, etc., antes de incluir <windows.h>. Luego use las funciones Unicode como CreateWindow(en general, sin Wnecesidad al final).
Saludos y hth. - Alf
11
¿Por qué exactamente consideras que eso es más práctico?
jalf
1
"..._ tmain también están definidos solo por Microsoft" Su último párrafo es absolutamente inexacto , _tmain está implementado exactamente igual en C ++ Builder de RAD Studio. De hecho, bajo el mapeo _TCHAR predeterminado de C ++ Builder , simplemente usar main fallará.
b1nary.atr0phy
35

_tmain es una macro que se redefine dependiendo de si compila o no con Unicode o ASCII. Es una extensión de Microsoft y no se garantiza que funcione en ningún otro compilador.

La declaración correcta es

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

Si se define la macro UNICODE, eso se expande a

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

De lo contrario se expande a

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

Su definición va para un poco de cada uno y (si tiene un UNICODE definido) se expandirá a

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

lo cual es simplemente incorrecto.

std :: cout funciona con caracteres ASCII. Necesita std :: wcout si está utilizando caracteres anchos.

prueba algo como esto

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

O simplemente puede decidir de antemano si usará caracteres anchos o estrechos. :-)

Actualizado 12 de noviembre de 2013:

Cambió el tradicional "TCHAR" a "_TCHAR", que parece ser la última moda. Ambos funcionan bien.

Fin de actualización

Michael J
fuente
1
"Es una extensión de Microsoft y no funcionará en ningún otro compilador". No en lo que respecta a RAD Studio.
b1nary.atr0phy
@ b1naryatr0phy: para dividir los pelos, la herramienta a la que enlaza utiliza "_TCHAR", en lugar de "TCHAR", por lo que no es compatible (aunque sí falsifica mi declaración). Sin embargo, debería haber dicho "Es una extensión de Microsoft y no se garantiza que funcione en ningún otro compilador". Enmendaré el original.
Michael J
@MichaelJ Me refería principalmente a la sección "Cambios de código ...", que explica por qué RAD Studio ahora usa _tmain en lugar de main, y en realidad ahora es el valor predeterminado estándar para C ++ Builder de Embarcadero.
b1nary.atr0phy
1
Esa es la segunda vez recientemente que esta respuesta de cuatro años ha sido rechazada. Sería bueno si los votantes negativos hicieran un comentario explicando qué problemas perciben y (si es posible) cómo mejorar la respuesta. b1naryatr0phy encontró una oración mal escrita, pero la arreglé en marzo. Cualquier guía sería apreciada.
Michael J
2
La vida es demasiado corta para esto.
Michael J
10

la convención _T se usa para indicar que el programa debe usar el juego de caracteres definido para la aplicación (Unicode, ASCII, MBCS, etc.). Puede rodear sus cadenas con _T () para que se almacenen en el formato correcto.

 cout << _T( "There are " ) << argc << _T( " arguments:" ) << endl;
Paul Alexander
fuente
De hecho, MS recomienda este enfoque, afaik. Haciendo que su aplicación reconozca unicode, la llaman ... usando la versión _t de todas las funciones de manipulación de cadenas, también.
Deep-B
1
@ Deep-B: Y en Windows, así es como hace que su aplicación esté lista para Unicode (prefiero el término de listo para Unicode a -ware), si se basaba en chars antes. Si su aplicación usa directamente, wchar_tentonces su aplicación es unicode.
paercebal
55
Por cierto, si intentas compilar en UNICODE, entonces tu código no se compilará como wchar_t de salida dentro de un cout basado en char, donde debería haber sido wcout. Vea la respuesta de Michael J para un ejemplo de definición de un "tcout" ...
paercebal
1
Ninguno si Microsoft lo recomienda, en gran medida, porque es simplemente incorrecto. Al compilar para Unicode, el código escribe valores de puntero en la secuencia de salida estándar. -1.
Inspectable el
5

Ok, la pregunta parece haber sido respondida bastante bien, la sobrecarga de UNICODE debería tomar una amplia matriz de caracteres como su segundo parámetro. Entonces, si el parámetro de la línea de comando es "Hello"que probablemente terminaría como "H\0e\0l\0l\0o\0\0\0"y su programa solo imprimiría el'H' antes de que vea lo que cree que es un terminador nulo.

Así que ahora te preguntarás por qué incluso compila y enlaza.

Bueno, se compila porque se le permite definir una sobrecarga en una función.

La vinculación es un problema un poco más complejo. En C, no hay información de símbolos decorados, por lo que solo encuentra una función llamada main. Probablemente, argc y argv siempre están ahí como parámetros de la pila de llamadas, en caso de que incluso si su función se define con esa firma, incluso si su función los ignora.

A pesar de que C ++ tiene símbolos decorados, casi con certeza utiliza el enlace C para main, en lugar de un enlazador inteligente que busca cada uno a su vez. Entonces encontró su wmain y puso los parámetros en la pila de llamadas en caso de que sea la int wmain(int, wchar_t*[])versión.

CashCow
fuente
Ok, tengo problemas para portar mi código a Windows Widechar desde hace años y ESA es la primera vez que entiendo por qué sucede esto. ¡Toma, toma toda mi reputación! jaja
Leonel
-1

Con un poco de esfuerzo para generar esto, funcionaría con cualquier 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';
}
Misgevolution
fuente