ความแตกต่างระหว่าง _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 ถูกเปิดใช้งานจะถูกรวบรวมเป็นและเป็นอย่างอื่นwmainmain

สำหรับส่วนที่สองของคำถามของคุณส่วนแรกของปริศนาคือหน้าที่หลักของคุณผิด wmainควรจะโต้แย้งไม่ได้wchar_t charเนื่องจากคอมไพเลอร์ไม่บังคับใช้mainฟังก์ชันนี้คุณจะได้รับโปรแกรมที่wchar_tส่งผ่านอาร์เรย์ของสตริงไปยังmainฟังก์ชันซึ่งแปลความหมายเหล่านั้นเป็นcharสตริง

ตอนนี้ใน UTF-16 ชุดอักขระที่ใช้โดย Windows เมื่อเปิดใช้งาน Unicode อักขระ ASCII ทั้งหมดจะแสดงเป็นคู่ของไบต์\0ตามด้วยค่า ASCII

และเนื่องจากซีพียู x86 นั้นเป็นแบบเอนด์เอนด์ลำดับของไบต์เหล่านี้จึงถูกสับเปลี่ยนดังนั้นค่า ASCII จะมาก่อนจากนั้นตามด้วยไบต์ว่าง

และในสตริงอักขระสตริงมักจะยกเลิกอย่างไร ใช่แล้วโดย null null ดังนั้นโปรแกรมของคุณจะเห็นสตริงจำนวนหนึ่งยาวหนึ่งไบต์

โดยทั่วไปคุณมีสามตัวเลือกเมื่อทำการเขียนโปรแกรม Windows:

  • ใช้ Unicode อย่างชัดแจ้ง (โทร wmain และสำหรับทุกฟังก์ชั่น Windows API ที่รับอาร์กิวเมนต์ที่เกี่ยวข้องกับถ่านให้เรียก-Wรุ่นของฟังก์ชันแทน CreateWindow ให้โทร CreateWindowW) และแทนที่จะใช้การcharใช้งานwchar_tเป็นต้น
  • ปิดใช้งาน Unicode อย่างชัดเจน โทรหลักและ CreateWindowA และใช้charสำหรับสตริง
  • อนุญาตให้ทั้งสอง (โทร _tmain และ CreateWindow ซึ่งแก้ไขเป็นหลัก / _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]; และมันจะแก้ไขการ cout ใน Ansi และ wcout ในโหมด Unicode หรือไม่ ฉันสงสัยว่าอาจเป็นประโยชน์สำหรับเขาในสถานการณ์นี้ และ +1 แน่นอนคำตอบที่ดี :)
โยฮันเน Schaub - litb

1
ข้อเสียอะไรที่จะปิดการใช้งานของ UNICODE
joshcomley

2
-1 ไม่มีตัวเลือกทั้งสามในรายการที่ใช้งานได้จริง วิธีการปฏิบัติเพื่อให้ Windows UNICODEโปรแกรมคือการกำหนด และการปรับแต่งอื่น ๆ สำหรับ C ++ เป็นต้นก่อนรวมถึง<windows.h>ก่อนที่จะรวมถึง จากนั้นใช้ฟังก์ชัน Unicode เช่นCreateWindow(โดยทั่วไปโดยไม่Wต้องใช้ในตอนท้าย)
ไชโยและ hth - Alf

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 พฤศจิกายน 2556:

เปลี่ยน "TCHAR" ดั้งเดิมเป็น "_TCHAR" ซึ่งดูเหมือนจะเป็นแฟชั่นล่าสุด ทำงานได้ดีทั้งคู่

สิ้นสุดการอัปเดต


1
"เป็นส่วนเสริมของ Microsoft และจะไม่ทำงานกับคอมไพเลอร์อื่น ๆ " ไม่ไกลเท่าที่ RAD Studio เกี่ยวข้อง
b1nary.atr0phy มี. ค.

@ b1naryatr0phy - หากต้องการแยกขนเครื่องมือที่คุณเชื่อมโยงไปใช้ใช้ "_TCHAR" แทนที่จะเป็น "TCHAR" ดังนั้นจึงไม่สามารถใช้งานร่วมกันได้ (แม้ว่าจะทำให้ข้อความของฉันเป็นเท็จ) อย่างไรก็ตามฉันควรจะพูดว่า "มันเป็นส่วนเสริมของ Microsoft และไม่รับประกันว่าจะทำงานกับคอมไพเลอร์อื่น ๆ " ฉันจะแก้ไขต้นฉบับ
Michael J

@MichaelJ ฉันหมายถึงส่วนใหญ่ "การเปลี่ยนแปลงรหัส ... " ซึ่งอธิบายว่าทำไม RAD Studio จึงใช้ _tmain แทนหลักและที่จริงตอนนี้มันเป็นมาตรฐานเริ่มต้นสำหรับ C ++ Builder ของ Embarcadero
b1nary.atr0phy มี. ค.

1
นั่นเป็นครั้งที่สองเมื่อเร็ว ๆ นี้ที่คำตอบสี่ปีนี้ถูกลดระดับลง มันจะดีถ้า downvoters แสดงความคิดเห็นอธิบายสิ่งที่พวกเขารับรู้ปัญหาและ (ถ้าเป็นไปได้) วิธีการปรับปรุงคำตอบ b1naryatr0phy พบประโยคที่เขียนไม่ดี แต่ฉันได้แก้ไขมันในเดือนมีนาคม ความคิดเห็นใด ๆ ที่จะได้รับการชื่นชม
Michael J

2
ชีวิตนี้สั้นเกินไปสำหรับสิ่งนี้
Michael J

10

การประชุม _T ใช้เพื่อระบุว่าโปรแกรมควรใช้ชุดอักขระที่กำหนดไว้สำหรับแอปพลิเคชัน (Unicode, ASCII, MBCS, ฯลฯ ) คุณสามารถล้อมรอบสตริงของคุณด้วย _T () เพื่อเก็บไว้ในรูปแบบที่ถูกต้อง

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

ในความเป็นจริง MS แนะนำวิธีการนี้ afaik ทำให้แอปพลิเคชันของคุณรับรู้ unicode พวกมันเรียกมันว่า ... โดยใช้เวอร์ชั่น _t ของฟังก์ชั่นการจัดการสตริงทั้งหมดเช่นกัน
Deep-B

1
@ Deep-B: และบน Windows นี่คือวิธีที่คุณทำให้แอปพลิเคชันของคุณพร้อมยูนิโค้ด (ฉันชอบคำศัพท์ของยูนิโค้ดที่พร้อมที่จะรับรู้) ถ้ามันเป็นพื้นฐานcharของ s มาก่อน หากแอปพลิเคชันของคุณใช้โดยตรงแสดงว่าwchar_tแอปพลิเคชันของคุณเป็นแบบ Unicode
paercebal

5
โดยวิธีการถ้าคุณพยายามที่จะรวบรวมใน UNICODE แล้วรหัสของคุณจะไม่รวบรวมตามการส่งออกของคุณ wchar_t ภายใน cout ตามถ่านซึ่งมันควรจะเป็น wcout ดูคำตอบของ Michael J สำหรับตัวอย่างของการกำหนด "tcout" ...
paercebal

1
ไม่มีถ้าแนะนำโดย Microsoft ส่วนใหญ่เพราะมันผิดธรรมดา เมื่อรวบรวมสำหรับ Unicode โค้ดจะเขียนค่าตัวชี้ไปยังสตรีมเอาต์พุตมาตรฐาน -1
IIsspectable

5

ตกลงคำถามดูเหมือนว่าจะได้รับการตอบค่อนข้างดีเกิน UNICODE ควรใช้ตัวอักษรกว้างเป็นพารามิเตอร์ที่สอง ดังนั้นหากพารามิเตอร์บรรทัดคำสั่งคือ"Hello"ว่าอาจจะสิ้นสุด"H\0e\0l\0l\0o\0\0\0"และโปรแกรมของคุณจะพิมพ์เท่านั้น'H'ก่อนที่จะเห็นสิ่งที่มันคิดว่าเป็นตัวสิ้นสุดโมฆะ

ดังนั้นตอนนี้คุณอาจสงสัยว่าทำไมมันถึงรวบรวมและเชื่อมโยง

มันรวบรวมเพราะคุณได้รับอนุญาตให้กำหนดโอเวอร์โหลดกับฟังก์ชั่น

การเชื่อมโยงเป็นปัญหาที่ซับซ้อนกว่าเล็กน้อย ใน C ไม่มีข้อมูลสัญลักษณ์ตกแต่งดังนั้นมันจึงพบฟังก์ชันที่เรียกว่า main argc และ argv อาจเป็นพารามิเตอร์ call-stack เสมอในกรณีที่ฟังก์ชันของคุณถูกกำหนดด้วยลายเซ็นนั้นแม้ว่าฟังก์ชันของคุณจะละเว้นก็ตาม

แม้ว่า C ++ จะมีสัญลักษณ์ตกแต่ง แต่ก็ใช้ C-linkage เป็นหลักแทนการใช้ตัวเชื่อมโยงที่ฉลาดซึ่งมองหาสัญลักษณ์แต่ละอัน ดังนั้นจึงพบ wmain ของคุณและวางพารามิเตอร์ลงใน call-stack ในกรณีที่เป็นint wmain(int, wchar_t*[])version


ตกลงดังนั้นฉันมีปัญหาในการย้ายรหัสไปยัง windows widechar เป็นเวลาหลายปีและนั่นเป็นครั้งแรกที่ฉันเข้าใจว่าทำไมสิ่งนี้ถึงเกิดขึ้น ที่นี่เอาชื่อเสียงของฉันทั้งหมด! ฮ่าฮ่า
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.