C ++での_tmain()とmain()の違いは何ですか?


224

C ++アプリケーションを次のmain()メソッドで実行すると、すべて問題ありません。

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

各引数の最初の文字を表示するだけです。

これを引き起こす違いは何ですか?

回答:


357

_tmainC ++には存在しません。mainします。

_tmain Microsoftの拡張機能です。

mainC ++標準によると、プログラムのエントリポイントです。次の2つのシグネチャのいずれかがあります。

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

Microsoftは、2番目の署名をこれで置き換えるwmainを追加しました:

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

そして、Unicode(UTF-16)とマルチバイト文字セットを簡単に切り替えることができるように_tmain、Unicodeが有効になっている場合はとしてコンパイルされwmain、そうでない場合はとしてコンパイルされるように定義していmainます。

質問の2番目の部分については、パズルの最初の部分はあなたの主な機能が間違っているということです。ではなくwmainwchar_t引数を取る必要がありますchar。コンパイラーはmain関数に対してこれを強制しないため、wchar_t文字列の配列がmain関数に渡され、それをchar文字列として解釈するプログラムを取得します。

現在、UTF-16では、Unicodeが有効になっているときにWindowsによって使用される文字セットであり、すべてのASCII文字は\0、ASCII値が後に続くバイトのペアとして表されます。

また、x86 CPUはリトルエンディアンであるため、これらのバイトの順序が入れ替わり、ASCII値が最初に来て、その後にnullバイトが続きます。

そしてchar文字列では、文字列は通常どのように終了しますか?はい、nullバイトです。したがって、プログラムは、それぞれが1バイト長の一連の文字列を認識します。

一般に、Windowsプログラミングを行う場合、3つのオプションがあります。

  • 明示的にUnicodeを使用します(wmainを呼び出し、char関連の引数を受け取るすべてのWindows API関数については-W、関数のバージョンを呼び出します。CreateWindowの代わりにCreateWindowWを呼び出します)。そして、charuse wchar_tなどを使用する代わりに
  • Unicodeを明示的に無効にします。mainとCreateWindowAを呼び出しchar、文字列に使用します。
  • 両方を許可します。(main _tmainとCreateWindowA / CreateWindowWに解決される_tmainとCreateWindowを呼び出します)、char / wchar_tの代わりにTCHARを使用します。

同じことが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を無効にするとどのようなデメリットがありますか?
joshcomley 2009年

2
-1リストされている3つのオプションのいずれも実用的ではありません。Windowsをプログラムする実際的な方法は、を定義することUNICODEです。とインクルードする前のC ++などのその他の調整<windows.h>。次に、次のようなUnicode関数を使用しますCreateWindow(通常W、最後に不要です)。
乾杯とhth。

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 2013年

@ b1naryatr0phy-ヘアを分割するために、リンク先のツールは「_TCHAR」ではなく「_TCHAR」を使用するため、互換性がありません(ただし、私の説明を改ざんします)。ただし、「Microsoftの拡張機能であり、他のコンパイラでの動作が保証されているわけではありません。」オリジナルを修正します。
マイケルJ

@MichaelJ私は主に「コードの変更...」セクションを参照していました。これは、RAD Studioがmainの代わりに_tmainを使用するようになった理由を説明し、実際にはEmbarcaderoのC ++ Builderの標準デフォルトになっています。
b1nary.atr0phy 2013年

1
この4年前の回答が反対票を投じられたのは最近2回目です。反対投票者が、彼らが認識している問題と(可能であれば)回答を改善する方法を説明するコメントを作成するとよいでしょう。b1naryatr0phyが不適切な文章を見つけましたが、3月に修正しました。どんな指導もいただければ幸いです。
Michael J

2
これには寿命が短すぎます。
マイケルJ

10

_T規則は、プログラムがアプリケーションに定義された文字セット(Unicode、ASCII、MBCSなど)を使用する必要があることを示すために使用されます。文字列を_T()で囲んで、正しい形式で保存することができます。

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

実際、MSはこのアプローチを推奨しています。アプリケーションをユニコード対応にすると、彼らはそれを呼び出します...すべての文字列操作関数の_tバージョンも使用します。
Deep-B

1
@ Deep-B:そして、Windowsでは、これchar以前のsに基づいていた場合、アプリケーションをユニコード対応にする方法です(私は、ユニコード対応という用語を-awareよりも優先します)。アプリケーションが直接使用するwchar_t場合、アプリケーションユニコードです。
paercebal

5
ちなみに、UNICODEでコンパイルしようとすると、コードは、wcoutであるはずのcharベースのcout内の出力wchar_tとしてコンパイルされません。「tcout」を定義する例については、Michael Jの回答を参照してください...
paercebal

1
これが明らかに間違っているため、これがMicrosoftによって主に推奨されている場合はありません。Unicode用にコンパイルするとき、コードはポインター値を標準出力ストリームに書き込みます。-1。
IInspectable 2017

5

さて、質問はかなりよく答えられたようです。UNICODEオーバーロードは、2番目のパラメーターとしてワイド文字配列を取る必要があります。そのため、コマンドラインパラメータが"Hello"多分最終的に"H\0e\0l\0l\0o\0\0\0"なり、プログラムが'H'nullターミネータであると考えるものを見る前にのみを出力することになります。

だから、なぜコンパイルしてリンクするのか疑問に思うかもしれません。

関数へのオーバーロードを定義できるので、コンパイルされます。

リンクは少し複雑な問題です。Cでは、装飾されたシンボル情報はないので、mainという関数を見つけるだけです。argcとargvは、たとえ関数がそのシグニチャーで定義されている場合でも、たとえ関数がそれらを無視した場合でも、おそらく常に呼び出しスタックパラメーターとして存在します。

C ++には装飾されたシンボルがありますが、ほぼ確実にmainにCリンケージを使用し、それぞれを順番に検索する賢いリンカではありません。したがって、それはあなたのwmainを見つけ、それがint wmain(int, wchar_t*[])バージョンである場合に備えて、パラメーターを呼び出しスタックに入れました。


わかりました、それで私は何年もの間私のコードをwindows widecharに移植することに問題を抱えています、そしてそれがなぜこれが起こるのかを理解したのはこれが初めてです。ここで、私の評判をすべて取りなさい!(笑)
レオネル

-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';
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.