C ++中的_tmain()和main()有什么区别?


224

如果我使用以下main()方法运行C ++应用程序,则一切正常:

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

我得到了我的期望,并且我的论据被打印出来。

但是,如果我使用_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;
}

它仅显示每个参数的第一个字符。

造成这种情况的区别是什么?

Answers:


357

_tmain在C ++中不存在。main做。

_tmain 是Microsoft的扩展程序。

main根据C ++标准,是程序的入口点。它具有以下两个签名之一:

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

Microsoft已添加了一个wmain,它将以下内容替换为第二个签名:

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

然后,为了更轻松地在Unicode(UTF-16)和它们的多字节字符集之间进行切换,他们定义_tmain了(如果启用Unicode的话)编译为wmain,否则编译为main

至于问题的第二部分,难题的第一部分是您的主要功能是错误的。wmain应该wchar_t争论,而不是char。由于编译器不会对该main函数强制执行此操作,因此您将获得一个程序,其中将wchar_t字符串数组传递给该main函数,该函数将它们解释为char字符串。

现在,在启用了Unicode的Windows使用的字符集UTF-16中,所有ASCII字符均表示为一对字节,\0后跟ASCII值。

而且由于x86 CPU是低位字节序的,因此这些字节的顺序被交换,因此ASCII值首先出现,然后是空字节。

在char字符串中,通常如何终止字符串?是的,由一个空字节组成。因此您的程序会看到一串字符串,每个字符串长一个字节。

通常,在进行Windows编程时,您有三个选择:

  • 明确使用Unicode(调用wmain,对于每个与char相关的参数的Windows API函数,请调用-W该函数的版本。而不是CreateWindow,请调用CreateWindowW)。而不是使用charuse wchar_t,依此类推
  • 明确禁用Unicode。调用main和CreateWindowA,然后将其char用于字符串。
  • 两者都允许。(分别调用_tmain和CreateWindow,分别解析为main / _tmain和CreateWindowA / CreateWindowW),并使用TCHAR代替char / wchar_t。

这对于windows.h定义的字符串类型同样适用:LPCTSTR解析为LPCSTR或LPCWSTR,对于包含char或wchar_t的所有其他类型,始终存在-T-版本,可以代替使用。

请注意,所有这些都是Microsoft特定的。TCHAR不是标准的C ++类型,它是Windows.h中定义的宏。wmain和_tmain也仅由Microsoft定义。


6
我想知道他们是否也提供tcout?这样一个人就可以做tcout << argv [n]; 并解析为Ansi中的cout和Unicode模式中的wcout?我怀疑在这种情况下这可能对他有用。和+1当然是个不错的答案:)
Johannes Schaub-litb

1
禁用UNICODE会带来什么不利条件?
2009年

2
-1列出的三个选项均不可行。Windows编程的实用方法是定义UNICODE。包括之前,还要对C ++等进行其他一些调整<windows.h>。然后使用Unicode函数,例如CreateWindow(通常W不需要结尾)。
干杯和健康。-阿尔夫

11
您为什么确切地认为这更实用?
2012年

1
“ ..._ tmain也仅由Microsoft定义” 您的最后一段绝对不准确,_tmain在RAD Studio的C ++ Builder中实现的方式完全相同。实际上,在C ++ Builder的默认_TCHAR映射下,仅使用main将会失败。
b1nary.atr0phy 2013年

35

_tmain是根据是否使用Unicode或ASCII进行编译而重新定义的宏。它是Microsoft的扩展程序,不能保证可以在任何其他编译器上使用。

正确的声明是

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

如果定义了宏UNICODE,则扩展为

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

否则扩展为

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

您的定义各不相同,并且(如果定义了UNICODE)将扩展为

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

这是完全错误的。

std :: cout使用ASCII字符。如果使用宽字符,则需要std :: wcout。

尝试这样的事情

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

或者,您可以预先决定是使用宽还是窄字符。:-)

2013年11月12日更新:

将传统的“ TCHAR”更改为“ _TCHAR”,这似乎是最新的时尚。两者都很好。

结束更新


1
“这是一个Microsoft扩展,在其他任何编译器上均不起作用。” 就RAD Studio而言。
b1nary.atr0phy

@ b1naryatr0phy-要拆分头发,链接到的工具使用“ _TCHAR”而不是“ TCHAR”,因此它不兼容(尽管它确实使我的陈述失真)。但是我应该说“这是Microsoft扩展,不能保证可以在任何其他编译器上工作。”。我将修改原件。
迈克尔·J

@MichaelJ我主要指的是“代码更改...”部分,它解释了为什么RAD Studio现在使用_tmain代替main,实际上,它现在是Embarcadero的C ++ Builder的标准默认值。
b1nary.atr0phy 2013年

1
这是这个已有四年历史的答案最近第二次被否决。如果下选民发表评论解释他们认为的问题以及(如果可能的话)如何改善答案,那将是很好的。b1naryatr0phy发现了一个写得不好的句子,但是我在三月份解决了这个问题。任何指导将不胜感激。
Michael J

2
生命太短暂了。
Michael J

10

_T约定用于指示程序应使用为应用程序定义的字符集(Unicode,ASCII,MBCS等)。您可以用_T()括起来,以正确的格式存储它们。

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

实际上,MS建议使用这种方法afaik。使您的应用程序具有Unicode意识,他们也使用所有字符串处理函数的_t版本来调用它。
Deep-B 2010年

1
@ Deep-B:在Windows上,如果以前基于s ,这就是使应用程序成为unicode就绪(我更喜欢unicode就绪的术语,而不是-aware)char的方式。如果您的应用程序直接使用,wchar_t则您的应用程序 unicode。
paercebal

5
顺便说一句,如果您尝试在UNICODE上进行编译,则您的代码将不会作为您在基于char的cout中应该是wcout的输出中的wchar_t的输出进行编译。请参阅Michael J的答案,以定义“ tcout”为例...
paercebal 2010年

1
如果Microsoft推荐这样做,则基本上没有,因为这是完全错误的。在为Unicode进行编译时,代码将指针值写入标准输出流。-1。
IInspectable

5

好的,这个问题似乎已经很好地回答了,UNICODE重载应将宽字符数组作为其第二个参数。因此,如果命令行参数"Hello"可能以as结尾,"H\0e\0l\0l\0o\0\0\0"并且您的程序只会'H'在看到它认为是空终止符之前打印出。

所以现在您可能想知道为什么它甚至可以编译和链接。

它之所以可以编译,是因为可以定义函数的重载。

链接是一个稍微复杂的问题。在C语言中,没有修饰的符号信息,因此只能找到一个称为main的函数。argc和argv可能始终作为调用堆栈参数存在,以防万一,即使您的函数是使用该签名定义的,即使您的函数碰巧忽略了它们也是如此。

尽管C ++确实具有修饰符,但几乎可以肯定地将C-linkage用于main,而不是一个聪明的链接器,该链接器依次查找每个链接器。因此,它找到了您的wmain并将参数放到调用堆栈中,以防它是int wmain(int, wchar_t*[])版本。


好的,所以多年以来我一直无法将代码移植到Windows widechar,这是我第一次了解为什么会发生这种情况。在这里,拿走我所有的声誉!哈哈
Leonel 2014年

-1

只需花一点时间将其模板化,它就可以处理任何对象列表。

#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';
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.