Яка різниця між _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;
}

Він просто відображає перший символ кожного аргументу.

Яка різниця викликає це?

Відповіді:


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тощо
  • Явно відключити Unicode. Викличте основний та 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 та wcout в режимі Unicode? Я підозрюю, що може бути корисним для нього в цій ситуації. та +1 звичайно, приємна відповідь :)
Йоханнес Шауб - ліб

1
Який недолік може бути вимкненим UNICODE?
joshcomley

2
-1 Жоден із трьох перерахованих варіантів не є практичним. Практичний спосіб програмування Windows - це визначення UNICODE. І деякі інші коригування для C ++ тощо, перш ніж включати <windows.h>. Потім використовуйте такі функції Unicode CreateWindow( як правило, Wв кінці не потрібні).
Ура та хт. - Альф

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 замість основного, а насправді це стандартний стандарт для C ++ Builder Embarcadero.
b1nary.atr0phy

1
Це останній раз, коли ця відповідь чотирирічної дитини була скасована. Було б непогано, якби люди, які звернулися до коментарів, зробили коментар, пояснюючи, які проблеми вони сприймають, і (якщо можливо) як покращити відповідь. b1naryatr0phy знайшов неправильно написане речення, але я це виправив у березні. Будь-яка впевненість буде вдячна.
Майкл Дж

2
Життя для цього занадто коротке.
Майкл Дж.

10

Конвенція _T використовується для вказівки, що програма повинна використовувати набір символів, визначений для програми (Unicode, ASCII, MBCS тощо). Ви можете оточити свої рядки за допомогою _T (), щоб вони зберігалися у правильному форматі.

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

Насправді, MS рекомендує такий підхід, afaik. Зробивши вашу програму unicode-обізнаною, вони її називають ... використовуючи і _t версію всіх функцій маніпулювання рядками.
Глибокий-Б

1
@ Deep-B: І в Windows, саме так ви робите додаток готовим до унікоду (я вважаю за краще термін unicode-ready--ware), якщо він базувався на chars раніше. Якщо ваша програма безпосередньо використовує, wchar_tто ваша програма є unicode.
paercebal

5
До речі, якщо ви намагаєтеся компілювати в UNICODE, то ваш код не буде компілюватися як ваш вихід wchar_t всередині c-файлу cout, де він повинен був бути wcout. Дивіться відповідь Майкла Дж. Про приклад визначення "тютюну" ...
paercebal

1
Ні, якщо це рекомендує Microsoft, в основному, тому що це явно неправильно. Під час компіляції для Unicode код записує значення вказівника у стандартний вихідний потік. -1.
Неочікуваний

5

Гаразд, на питання, здається, відповіли досить добре, перевантаження UNICODE повинно брати широкий масив символів як другий параметр. Отже, якщо параметр командного рядка "Hello", ймовірно, закінчиться так, "H\0e\0l\0l\0o\0\0\0"і ваша програма виведе друк лише 'H'перед тим, як побачить те, що, на його думку, є нульовим термінатором.

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

Добре це компілюється, оскільки вам дозволяється визначити перевантаження функції.

Пов’язання є дещо складнішим питанням. У C немає прикрашеної інформації символу, тому вона просто знаходить функцію, яку називають основною. Аргументи argc та argv, ймовірно, завжди є такими параметрами стека викликів на всякий випадок, навіть якщо ваша функція визначена за допомогою цього підпису, навіть якщо ваша функція ігнорує їх.

Незважаючи на те, що C ++ має прикрашені символи, він майже напевно використовує C-зв'язок для основного, а не розумного лінкера, який шукає кожного по черзі. Тож він знайшов вашу wmain і поставив параметри на стек викликів на випадок, якщо це int wmain(int, wchar_t*[])версія.


Гаразд, тому у мене проблеми з переносом свого коду на широкоформатний windows протягом багатьох років, і це перший раз, коли я зрозумів, чому це відбувається. Ось, прийміть всю мою репутацію! ха-ха
Леонель

-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';
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.