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

각 인수의 첫 문자 만 표시합니다.

이것을 일으키는 차이점은 무엇입니까?

답변:


357

_tmainC ++에는 없습니다. main그렇습니다.

_tmain Microsoft 확장입니다.

mainC ++ 표준에 따르면 프로그램의 진입 점입니다. 다음 두 가지 서명 중 하나가 있습니다.

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

Microsoft는 두 번째 서명을 다음과 같이 바꾸는 wmain을 추가했습니다.

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

그런 다음 유니 코드 (UTF-16)와 멀티 바이트 문자 세트를 쉽게 전환 할 수 있도록 _tmain유니 코드가 활성화 된 wmain경우로 컴파일 되고 그렇지 않으면로 컴파일되는 것을 정의 했습니다 main.

질문의 두 번째 부분은 퍼즐의 첫 번째 부분이 주요 기능이 잘못되었다는 것입니다. 아닌 인수를 wmain취해야합니다 . 컴파일러는 함수에 대해 이것을 강제하지 않기 때문에 문자열 배열을 함수 로 전달 하는 프로그램을 얻습니다.이 문자열은 문자열로 해석 됩니다.wchar_tcharmainwchar_tmainchar

이제 UTF-16에서 유니 코드가 사용 가능한 경우 Windows에서 사용하는 문자 세트는 모든 ASCII 문자가 바이트 쌍으로 표시되고 \0ASCII 값이 뒤에옵니다.

x86 CPU는 리틀 엔디안이므로이 바이트의 순서가 바뀌어 ASCII 값이 먼저 온 다음 널 바이트가 뒤 따릅니다.

그리고 char 문자열에서 문자열은 일반적으로 어떻게 종료됩니까? 네, null 바이트입니다. 따라서 프로그램은 1 바이트 길이의 많은 문자열을 봅니다.

일반적으로 Windows 프로그래밍을 수행 할 때 세 가지 옵션이 있습니다.

  • 명시 적으로 유니 코드를 사용하십시오 (wmain을 호출하고 char 관련 인수를 취하는 모든 Windows API 함수 -W에 대해 함수 버전을 호출하십시오 . CreateWindow 대신 CreateWindowW를 호출하십시오). 그리고 charuse wchar_t등 을 사용 하는 대신
  • 유니 코드를 명시 적으로 비활성화하십시오. 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으로, 유니 코드 모드에서 wcout으로 해결됩니까? 이 상황에서 그에게 도움이 될 것으로 생각됩니다. +1, 물론 좋은 답변 :)
Johannes Schaub-litb

1
UNICODE를 비활성화하면 어떤 단점이 있습니까?
joshcomley

2
-1 나열된 세 가지 옵션 중 어느 것도 실용적인 것은 아닙니다. Windows를 프로그래밍하는 실용적인 방법은 다음을 정의하는 것 UNICODE입니다. 포함하기 전에 C ++ 등에 대한 다른 조정 사항이 <windows.h>있습니다. 그런 다음과 같은 유니 코드 함수를 사용하십시오 CreateWindow(일반적으로 W끝에 필요 없음 ).
건배와 hth. -Alf

11
왜 이것이 더 실용적이라고 생각합니까?
jalf

1
"..._ tmain도 Microsoft에서만 정의됩니다." 마지막 단락은 절대적으로 정확하지 않습니다 . _tmain은 RAD Studio의 C ++ Builder에서 정확히 동일하게 구현됩니다. 실제로 C ++ Builder의 기본 _TCHAR 매핑 에서 main을 사용하면 실패합니다.
b1nary.atr0phy 5

35

_tmain은 유니 코드 또는 ASCII로 컴파일하는지 여부에 따라 재정의되는 매크로입니다. Microsoft 확장이며 다른 컴파일러에서 작동한다고 보장되지 않습니다.

올바른 선언은

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

매크로 UNICODE가 정의 된 경우

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

그렇지 않으면

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

정의는 각각 조금씩 진행되며 (유니 코드가 정의 된 경우)

 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 확장이며 다른 컴파일러에서 작동한다고 보장하지 않습니다."라고 말해야했습니다. 원본을 수정하겠습니다.
Michael J

@MichaelJ 저는 주로 "Code Changes ..."섹션을 언급하고 있는데, 여기서 RAD Studio가 main 대신 _tmain을 사용하는 이유를 설명하고 실제로는 현재 Embarcadero의 C ++ Builder의 표준 기본값입니다.
b1nary.atr0phy

1
이 네 살짜리 답이 다운 보트 된 것은 최근 두 번째입니다. 다운 보터가 어떤 문제를인지하고 (가능한 경우) 답변을 개선하는 방법을 설명하는 것이 좋을 것입니다. b1naryatr0phy가 잘못 작성된 문장을 찾았지만 3 월에 수정했습니다. 어떤 지침이라도 감사하겠습니다.
Michael J

2
인생은 이것으로 너무 짧습니다.
Michael J

10

_T 규칙은 프로그램이 응용 프로그램에 정의 된 문자 집합 (유니 코드, ASCII, MBCS 등)을 사용해야 함을 나타내는 데 사용됩니다. 문자열을 _T ()로 묶어 올바른 형식으로 저장할 수 있습니다.

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

실제로 MS는이 접근 방식을 권장합니다. 응용 프로그램을 유니 코드 인식으로 만들면 모든 문자열 조작 함수의 _t 버전을 사용하여 호출합니다.
Deep-B

1
깊은-B @ : 그리고 Windows에서,이 이 기반으로 한 경우, (I 유니 코드 - 준비 - 인식의 용어를 선호) 응용 프로그램 유니 코드가 준비 할 방법 char이전의. 응용 프로그램이 직접 사용하는 경우 wchar_t응용 프로그램 유니 코드입니다.
paercebal

5
그건 그렇고, UNICODE에서 컴파일하려고하면 코드는 wcout이 있어야하는 char 기반 cout 내에서 출력 wchar_t로 컴파일되지 않습니다. "tcout"을 정의하는 예를 보려면 Michael J의 답변을 참조하십시오.
paercebal

1
마이크로 소프트가 권장하는 경우는 대체로 잘못된 것이기 때문이다. 유니 코드를 컴파일 할 때 코드는 포인터 값을 표준 출력 스트림에 씁니다. -1.
IInspectable

5

문제는 상당히 잘 대답 된 것 같습니다. 유니 코드 과부하는 두 번째 매개 변수로 넓은 문자 배열을 가져야합니다. 따라서 명령 줄 매개 변수가 "Hello"그 결과로 끝나고 "H\0e\0l\0l\0o\0\0\0"프로그램이 'H'null 종결 자라고 생각하는 것을보기 전에 만 인쇄합니다 .

이제 왜 컴파일하고 링크하는지 궁금 할 것입니다.

함수에 과부하를 정의 할 수 있기 때문에 컴파일됩니다.

연결은 약간 더 복잡한 문제입니다. C에는 장식 된 기호 정보가 없으므로 main이라는 함수 만 찾습니다. argc 및 argv는 함수가 해당 시그니처로 정의되어 있어도 함수가 무시하더라도 호출 스택 매개 변수로 항상 존재합니다.

C ++에는 장식 된 기호가 있지만 각 문자를 차례로 찾는 영리한 링커보다는 C- 링키지를 기본으로 사용합니다. 따라서 wmain을 발견하고 매개 변수가 int wmain(int, wchar_t*[])버전 인 경우 매개 변수를 호출 스택에 넣습니다 .


좋아, 그래서 몇 년 동안 내 코드를 windows widechar로 이식하는 데 문제가 있으며 이것이 왜 이런 일이 일어나는지를 이해하는 것은 처음입니다. 여기, 내 명성을 모두 가져라! haha
Leonel

-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.