В чем разница между _tmain () и main () в C ++?

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

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

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

joshcomley
источник

Ответы:

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

Теперь в 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.

jalf
источник
6
Интересно, предоставляют ли они также? так что можно просто сделать tcout << argv [n]; и он разрешает cout в Ansi и wcout в режиме Unicode? Я подозреваю, что это может быть полезно для него в этой ситуации. и +1, конечно, хороший ответ :)
Йоханнес Шауб -
1
Какой недостаток обеспечит отключение UNICODE?
Джошкомли
2
-1 Ни один из трех перечисленных вариантов не является практичным. Практический способ программирования Windows заключается в определении UNICODE. И некоторые другие настройки для C ++ и т. Д., Прежде чем включать <windows.h>. Затем используйте такие функции Unicode, как CreateWindow(обычно без Wнеобходимости в конце).
ура и hth. - Альф
11
Почему именно вы считаете это более практичным?
jalf
1
«..._tmain также определяются только Microsoft» Ваш последний абзац абсолютно неточен , _tmain реализован точно так же в C ++ Builder RAD Studio. На самом деле, при отображении _TCHAR по умолчанию в C ++ Builder простое использование main не удастся.
b1nary.atr0phy
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;
}

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

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

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

Конец обновления

Майкл Дж
источник
1
«Это расширение Microsoft, и оно не будет работать на других компиляторах». Не так сильно, как RAD Studio.
b1nary.atr0phy
@ b1naryatr0phy - Чтобы разделить волосы, инструмент, на который вы ссылаетесь, использует «_TCHAR», а не «TCHAR», поэтому он не совместим (хотя он и фальсифицирует мое утверждение). Однако я должен был сказать: «Это расширение от Microsoft, и не обязательно работать на любых других компиляторах». Я исправлю оригинал.
Майкл Дж
@MichaelJ Я имел в виду, в основном, раздел «Изменения кода ...», в котором объясняется, почему RAD Studio теперь использует _tmain вместо main, и фактически это стандартное значение по умолчанию для Embarcadero C ++ Builder.
b1nary.atr0phy
1
Это уже второй раз, когда этот четырехлетний ответ был отвергнут. Было бы хорошо, если бы downvoters сделали комментарий, объясняющий, какие проблемы они чувствуют и (если возможно), как улучшить ответ. b1naryatr0phy нашел плохо написанное предложение, но я исправил это в марте. Любое руководство будет оценено.
Майкл Дж
2
Жизнь слишком коротка для этого.
Майкл Дж
10

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

 cout << _T( "There are " ) << argc << _T( " arguments:" ) << endl;
Пол Александр
источник
На самом деле, MS рекомендует этот подход, afaik. Делая ваше приложение ориентированным на Unicode, они вызывают его ... используя версию _t всех функций манипуляции со строками.
Deep-B
1
@ Deep-B: А в Windows это то , как вы делаете свое приложение готовым к юникоду (я предпочитаю термин «готовый к юникоду» - «осведомленному»), если оно было основано на chars раньше. Если ваше приложение использует напрямую, wchar_tто ваше приложение является Unicode.
paercebal
5
Кстати, если вы попытаетесь скомпилировать на UNICODE, ваш код не скомпилируется как вывод wchar_t внутри cout на основе символов, где он должен был быть wcout. См. Ответ Майкла Дж. За пример определения «ткота» ...
paercebal
1
Ни один, если это не рекомендуется Microsoft, в значительной степени, потому что это совершенно неправильно. При компиляции для Unicode код записывает значения указателя в стандартный поток вывода. -1.
Инспектируемый
5

Хорошо, вопрос, кажется, был получен достаточно хорошо, перегрузка 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*[])версия.

Дойная корова
источник
Итак, у меня проблемы с переносом кода в 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';
}
Misgevolution
источник