В чем разница между _tmain () и main () в C ++? - PullRequest
218 голосов
/ 22 мая 2009

Если я запускаю свое приложение 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;
}

Отображает только первый символ каждого аргумента.

Какая разница вызывает это?

Ответы [ 5 ]

343 голосов
/ 22 мая 2009

_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 строки.

Теперь в UTF-16, наборе символов, используемом Windows, когда включен Unicode, все символы ASCII представлены в виде пары байтов \0, за которой следует значение ASCII.

А поскольку процессор x86 имеет младший порядок, порядок этих байтов поменялся местами, так что сначала следует значение ASCII, а затем нулевой байт.

А в строке char, как обычно завершается строка? Да, нулевым байтом. Итак, ваша программа видит кучу строк, каждая длиной в один байт.

Как правило, у вас есть три варианта программирования Windows:

  • Явно используйте Unicode (вызовите wmain, и для каждой функции Windows API, которая принимает аргументы, связанные с символами, вызовите версию функции -W. Вместо CreateWindow, вызовите CreateWindowW). И вместо использования char используйте wchar_t и т. Д.
  • Явно отключить Юникод. Вызовите 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.

34 голосов
/ 23 мая 2009

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

Или вы можете заранее решить, использовать ли широкие или узкие символы. : -)

Обновлено 12 ноября 2013 г .:

Изменил традиционный "TCHAR" на "_TCHAR", что, похоже, является последней модой. Оба работают нормально.

Окончание обновления

9 голосов
/ 22 мая 2009

Соглашение _T используется, чтобы указать, что программа должна использовать набор символов, определенный для приложения (Unicode, ASCII, MBCS и т. Д.). Вы можете заключить строки в _T (), чтобы сохранить их в правильном формате.

 cout << _T( "There are " ) << argc << _T( " arguments:" ) << endl;
5 голосов
/ 15 октября 2012

Хорошо, вопрос, кажется, был получен достаточно хорошо, перегрузка UNICODE должна принимать массив широких символов в качестве второго параметра. Поэтому, если параметр командной строки равен "Hello", это, вероятно, в конечном итоге будет равно "H\0e\0l\0l\0o\0\0\0", и ваша программа напечатает только 'H', прежде чем увидит то, что она считает нулевым терминатором.

Так что теперь вы можете удивиться, почему он даже компилирует и ссылается.

Ну, это компилируется, потому что вы можете определить перегрузку для функции.

Связывание - это немного более сложный вопрос. В C нет информации о декорированных символах, поэтому он просто находит функцию с именем main. Argc и argv, вероятно, всегда присутствуют в качестве параметров стека вызовов на всякий случай, даже если ваша функция определена с этой сигнатурой, даже если ваша функция игнорирует их.

Несмотря на то, что в C ++ действительно есть декорированные символы, он почти наверняка использует C-linkage для main, а не для умного компоновщика, который ищет каждый из них по очереди. Таким образом, он нашел ваш wmain и поместил параметры в стек вызовов на случай, если это версия int wmain(int, wchar_t*[]).

0 голосов
/ 02 июля 2017

Приложив немного усилий для его настройки, он сможет работать с любым списком объектов.

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