Sự khác biệt giữa _tmain () và main () trong C ++ là gì?


224

Nếu tôi chạy ứng dụng C ++ của mình với phương thức main () sau thì mọi thứ đều ổn:

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

Tôi nhận được những gì tôi mong đợi và lập luận của tôi được in ra.

Tuy nhiên, nếu tôi sử dụng _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;
}

Nó chỉ hiển thị ký tự đầu tiên của mỗi đối số.

Sự khác biệt gây ra điều này là gì?

Câu trả lời:


357

_tmainkhông tồn tại trong C ++. mainlàm.

_tmain là một phần mở rộng của Microsoft.

mainlà, theo tiêu chuẩn C ++, điểm vào của chương trình. Nó có một trong hai chữ ký sau:

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

Microsoft đã thêm một wmain thay thế chữ ký thứ hai bằng chữ này:

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

Và sau đó, để dễ dàng chuyển đổi giữa Unicode (UTF-16) và bộ ký tự đa nhân của họ, họ đã xác định _tmaincái nào, nếu Unicode được bật, được biên dịch thành wmain, và ngược lại main.

Đối với phần thứ hai của câu hỏi của bạn, phần đầu tiên của câu đố là chức năng chính của bạn là sai. wmainnên wchar_ttranh luận chứ không phải char. Vì trình biên dịch không thực thi điều này cho mainhàm, nên bạn nhận được một chương trình trong đó một wchar_tchuỗi các chuỗi được truyền cho mainhàm, nó diễn giải chúng dưới dạng các charchuỗi.

Bây giờ, trong UTF-16, bộ ký tự được Windows sử dụng khi bật Unicode, tất cả các ký tự ASCII được biểu diễn dưới dạng cặp byte \0theo sau giá trị ASCII.

Và vì CPU x86 là endian nhỏ, thứ tự của các byte này được hoán đổi, do đó giá trị ASCII xuất hiện trước, sau đó là byte rỗng.

Và trong một chuỗi char, chuỗi thường được kết thúc như thế nào? Đúng, bởi một byte null. Vì vậy, chương trình của bạn nhìn thấy một chuỗi các chuỗi, mỗi chuỗi dài một byte.

Nói chung, bạn có ba tùy chọn khi thực hiện lập trình Windows:

  • Hoàn toàn sử dụng Unicode (gọi wmain và đối với mọi hàm API của Windows có các đối số liên quan đến char, hãy gọi -Wphiên bản của hàm. Thay vì CreatWindow, hãy gọi CreateWindowW). Và thay vì sử chardụng wchar_t, vân vân
  • Vô hiệu hóa Unicode. Gọi main và CreateWindowA và sử dụng charcho chuỗi.
  • Cho phép cả hai. (gọi _tmain và CreateWindow, sẽ phân giải chính / _tmain và CreateWindowA / CreateWindowW) và sử dụng TCHAR thay vì char / wchar_t.

Điều tương tự cũng áp dụng cho các loại chuỗi được xác định bởi windows.h: LPCTSTR phân giải thành LPCSTR hoặc LPCWSTR và đối với mọi loại khác bao gồm char hoặc wchar_t, phiên bản -T- luôn tồn tại có thể được sử dụng thay thế.

Lưu ý rằng tất cả điều này là cụ thể của Microsoft. TCHAR không phải là một loại C ++ tiêu chuẩn, nó là một macro được định nghĩa trong windows.h. wmain và _tmain cũng chỉ được xác định bởi Microsoft.


6
tôi tự hỏi liệu họ cũng cung cấp một tcout? để người ta có thể thực hiện tcout << argv [n]; và nó giải quyết cout trong Ansi và wcout ở chế độ Unicode? Tôi nghi ngờ rằng nó có thể hữu ích cho anh ta trong tình huống này. và +1 tất nhiên, câu trả lời hay :)
Julian Schaub - litb

1
Bất lợi nào sẽ vô hiệu hóa UNICODE cung cấp?
joshcomley

2
-1 Không có ba tùy chọn được liệt kê là thực tế. Cách thực tế để lập trình Windows là xác định UNICODE. Và một số điều chỉnh khác cho C ++, vv, trước khi bao gồm <windows.h>. Sau đó sử dụng các hàm Unicode như CreateWindow(nói chung là không Wcần thiết ở cuối).
Chúc mừng và hth. - Alf

11
Tại sao chính xác bạn coi điều đó là thực tế hơn?
jalf

1
"..._ tmain cũng chỉ được xác định bởi Microsoft" Đoạn cuối của bạn hoàn toàn không chính xác , _tmain được triển khai chính xác giống như trong Trình tạo C ++ của RAD Studio. Trên thực tế, dưới ánh xạ _TCHAR mặc định của Trình tạo C ++ , chỉ cần sử dụng chính sẽ thất bại.
b1nary.atr0phy

35

_tmain là một macro được xác định lại tùy thuộc vào việc bạn biên dịch bằng Unicode hay ASCII. Nó là một phần mở rộng của Microsoft và không được bảo đảm để hoạt động trên bất kỳ trình biên dịch nào khác.

Khai báo đúng là

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

Nếu UNICODE vĩ mô được xác định, nó sẽ mở rộng thành

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

Nếu không, nó mở rộng ra

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

Định nghĩa của bạn dành cho từng chút một và (nếu bạn đã xác định UNICODE) sẽ mở rộng sang

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

đó chỉ là sai.

std :: cout hoạt động với các ký tự ASCII. Bạn cần std :: wcout nếu bạn đang sử dụng các ký tự rộng.

thử cái gì đó như thế này

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

Hoặc bạn chỉ có thể quyết định trước nên sử dụng các ký tự rộng hay hẹp. :-)

Cập nhật ngày 12 tháng 11 năm 2013:

Thay đổi "TCHAR" truyền thống thành "_TCHAR" dường như là mốt mới nhất. Cả hai đều hoạt động tốt.

Kết thúc cập nhật


1
"Đây là một phần mở rộng của Microsoft và sẽ không hoạt động trên bất kỳ trình biên dịch nào khác." Không xa như RAD Studio có liên quan.
b1nary.atr0phy

@ b1naryatr0phy - Để chia tóc, công cụ bạn liên kết để sử dụng "_TCHAR", thay vì "TCHAR" để nó không tương thích (mặc dù nó làm sai lệch tuyên bố của tôi). Tuy nhiên tôi nên nói "Đây là một phần mở rộng của Microsoft và không được bảo đảm để hoạt động trên bất kỳ trình biên dịch nào khác." Tôi sẽ sửa đổi bản gốc.
Michael J

@MichaelJ Tôi chủ yếu đề cập đến phần "Thay đổi mã ...", điều này giải thích tại sao RAD Studio hiện sử dụng _tmain thay cho chính và thực tế bây giờ nó là mặc định tiêu chuẩn cho Trình tạo C ++ của Embarcadero.
b1nary.atr0phy

1
Đó là lần thứ hai gần đây, câu trả lời bốn năm tuổi này đã bị hạ thấp. Sẽ thật tuyệt nếu những người downvot bình luận giải thích những vấn đề họ cảm nhận và (nếu có thể) làm thế nào để cải thiện câu trả lời. b1naryatr0phy đã tìm thấy một câu viết tồi, nhưng tôi đã sửa nó vào tháng ba. Bất kỳ dự đoán sẽ được đánh giá cao.
Michael J

2
Cuộc sống quá ngắn cho việc này.
Michael J

10

quy ước _T được sử dụng để biểu thị chương trình nên sử dụng bộ ký tự được xác định cho ứng dụng (Unicode, ASCII, MBCS, v.v.). Bạn có thể bao quanh chuỗi của mình bằng _T () để chúng được lưu trữ ở định dạng chính xác.

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

Trong thực tế, MS khuyến nghị phương pháp này, afaik. Làm cho ứng dụng của bạn không nhận biết được, họ gọi nó là ... bằng cách sử dụng phiên bản _t của tất cả các hàm thao tác chuỗi.
Deep-B

1
@ Deep-B: Và trên Windows, đây cách bạn làm cho ứng dụng của mình không sẵn sàng (tôi thích thuật ngữ unicode sẵn sàng để nhận biết), nếu nó dựa trên chars trước đó. Nếu ứng dụng của bạn trực tiếp sử dụng wchar_tthì ứng dụng của bạn unicode.
paercebal

5
Nhân tiện, nếu bạn cố gắng biên dịch trên UNICODE, thì mã của bạn sẽ không được biên dịch dưới dạng wchar_t đầu ra của bạn bên trong một cout dựa trên char, nơi nó đáng lẽ phải là wcout. Xem câu trả lời của Michael J để biết ví dụ về việc xác định "tcout" ...
paercebal

1
Không, nếu điều này được Microsoft khuyến nghị, phần lớn, vì nó hoàn toàn sai. Khi biên dịch cho Unicode, mã ghi các giá trị con trỏ vào luồng đầu ra tiêu chuẩn. -1.
IInspectable

5

Ok, câu hỏi dường như đã được trả lời khá tốt, quá tải UNICODE nên lấy một mảng ký tự rộng làm tham số thứ hai của nó. Vì vậy, nếu tham số dòng lệnh "Hello"có thể sẽ kết thúc như"H\0e\0l\0l\0o\0\0\0" và chương trình của bạn sẽ chỉ in 'H'trước khi nó thấy những gì nó nghĩ là một đầu cuối null.

Vì vậy, bây giờ bạn có thể tự hỏi tại sao nó thậm chí biên dịch và liên kết.

Vâng, nó biên dịch bởi vì bạn được phép xác định quá tải cho một hàm.

Liên kết là một vấn đề phức tạp hơn một chút. Trong C, không có thông tin biểu tượng được trang trí nên nó chỉ tìm thấy một hàm gọi là chính. Argc và argv có thể luôn ở đó dưới dạng tham số ngăn xếp cuộc gọi chỉ trong trường hợp ngay cả khi chức năng của bạn được xác định bằng chữ ký đó, ngay cả khi chức năng của bạn xảy ra để bỏ qua chúng.

Mặc dù C ++ không có biểu tượng trang trí, nhưng nó gần như chắc chắn sử dụng liên kết C cho chính, thay vì một trình liên kết thông minh tìm kiếm lần lượt từng cái. Vì vậy, nó đã tìm thấy wmain của bạn và đặt các tham số vào ngăn xếp cuộc gọi trong trường hợp đó là int wmain(int, wchar_t*[])phiên bản.


Ok, vì vậy tôi gặp vấn đề khi chuyển mã của mình sang windows widechar trong nhiều năm nay và đó là lần đầu tiên tôi hiểu tại sao điều này xảy ra. Ở đây, lấy tất cả danh tiếng của tôi! haha
Leonel

-1

Với một chút nỗ lực tạo khuôn mẫu này, nó sẽ hoạt động với bất kỳ danh sách các đối tượng.

#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';
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.