Apa perbedaan antara _tmain () dan main () di C ++?

224

Jika saya menjalankan aplikasi C ++ saya dengan metode main () berikut semuanya OK:

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

Saya mendapatkan apa yang saya harapkan dan argumen saya dicetak.

Namun, jika saya menggunakan _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;
}

Itu hanya menampilkan karakter pertama dari setiap argumen.

Apa perbedaan yang menyebabkan ini?

Joshcomley
sumber

Jawaban:

357

_tmaintidak ada di C ++. maintidak.

_tmain adalah ekstensi Microsoft.

mainadalah, menurut standar C ++, titik masuk program. Ia memiliki salah satu dari dua tanda tangan ini:

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

Microsoft telah menambahkan peringatan yang menggantikan tanda tangan kedua dengan ini:

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

Dan kemudian, untuk membuatnya lebih mudah untuk beralih antara Unicode (UTF-16) dan set karakter multibyte mereka, mereka telah menentukan _tmain, jika Unicode diaktifkan, dikompilasi sebagai wmain, dan sebaliknya sebagaimain .

Adapun bagian kedua dari pertanyaan Anda, bagian pertama dari teka-teki adalah bahwa fungsi utama Anda salah. wmainharus mengambil wchar_targumen, bukan char. Karena kompiler tidak menjalankan ini untuk mainfungsi, Anda mendapatkan program di mana array wchar_tstring diteruskan ke mainfungsi, yang menafsirkannya sebagai charstring.

Sekarang, di UTF-16, set karakter yang digunakan oleh Windows ketika Unicode diaktifkan, semua karakter ASCII diwakili sebagai pasangan byte \0 diikuti oleh nilai ASCII.

Dan karena CPU x86 adalah little-endian, urutan byte ini ditukar, sehingga nilai ASCII datang terlebih dahulu, kemudian diikuti oleh byte nol.

Dan dalam string char, bagaimana string biasanya diakhiri? Yap, dengan byte nol. Jadi program Anda melihat banyak string, masing-masing panjangnya satu byte.

Secara umum, Anda memiliki tiga opsi saat melakukan pemrograman Windows:

  • Secara eksplisit menggunakan Unicode (panggil wmain, dan untuk setiap fungsi Windows API yang mengambil argumen terkait char, panggil -Wversi fungsi. Alih-alih CreateWindow, panggil CreateWindowW). Dan alih-alih menggunakan chargunakan wchar_t, dan sebagainya
  • Nonaktifkan Unicode secara eksplisit. Panggil main, dan CreateWindowA, dan gunakan charuntuk string.
  • Izinkan keduanya. (panggil _tmain, dan CreateWindow, yang memutuskan untuk main / _tmain dan CreateWindowA / CreateWindowW), dan gunakan TCHAR sebagai ganti char / wchar_t.

Hal yang sama berlaku untuk tipe string yang didefinisikan oleh windows.h: LPCTSTR memutuskan untuk LPCSTR atau LPCWSTR, dan untuk setiap tipe lain yang menyertakan char atau wchar_t, versi -T- selalu ada yang dapat digunakan sebagai gantinya.

Perhatikan bahwa semua ini khusus untuk Microsoft. TCHAR bukan tipe C ++ standar, ini adalah makro yang didefinisikan di windows.h. wmain dan _tmain juga hanya ditentukan oleh Microsoft.

jalf
sumber
6
Saya bertanya-tanya apakah mereka menyediakan tcout juga? sehingga orang bisa melakukan tcout << argv [n]; dan itu memutuskan untuk cout di Ansi dan wcout dalam mode Unicode? Saya menduga itu bisa berguna baginya dalam situasi ini. dan 1 tentu saja, bagus jawaban :)
Johannes Schaub - litb
1
Kerugian apa yang akan diberikan penonaktifan UNICODE?
joshcomley
2
-1 Tidak satu pun dari tiga opsi yang terdaftar praktis. Cara praktis untuk memprogram Windows adalah dengan mendefinisikan UNICODE. Dan beberapa penyesuaian lain untuk C ++ dll, sebelum termasuk <windows.h>. Kemudian gunakan fungsi Unicode seperti CreateWindow(secara umum tanpa Wdiperlukan di akhir).
Ceria dan hth. - Alf
11
Mengapa Anda menganggap itu lebih praktis?
jalf
1
"..._ tmain juga hanya ditentukan oleh Microsoft" Paragraf terakhir Anda benar-benar tidak akurat , _tmain diimplementasikan persis sama di C ++ Builder RAD Studio. Bahkan, di bawah pemetaan _TCHAR default C ++ Builder , cukup menggunakan main akan gagal.
b1nary.atr0phy
35

_tmain adalah makro yang didefinisikan ulang tergantung pada apakah Anda mengkompilasi dengan Unicode atau ASCII. Ini adalah ekstensi Microsoft dan tidak dijamin untuk bekerja pada kompiler lain.

Deklarasi yang benar adalah

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

Jika UNICODE makro didefinisikan, yang diperluas menjadi

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

Kalau tidak, itu akan berkembang

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

Definisi Anda berlaku untuk masing-masing, dan (jika Anda memiliki UNICODE didefinisikan) akan diperluas ke

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

yang jelas salah.

std :: cout bekerja dengan karakter ASCII. Anda perlu std :: wcout jika Anda menggunakan karakter lebar.

coba sesuatu seperti ini

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

Atau Anda bisa saja memutuskan terlebih dahulu apakah akan menggunakan karakter lebar atau sempit. :-)

Diperbarui 12 Nov 2013:

Mengubah "TCHAR" tradisional menjadi "_TCHAR" yang tampaknya menjadi mode terbaru. Keduanya bekerja dengan baik.

Akhiri Pembaruan

Michael J
sumber
1
"Ini adalah ekstensi Microsoft dan tidak akan berfungsi pada kompiler lain." Tidak sejauh RAD Studio yang bersangkutan.
b1nary.atr0phy
@ b1naryatr0phy - Untuk memisahkan rambut, alat yang Anda tautkan menggunakan "_TCHAR", bukan "TCHAR" sehingga tidak kompatibel (meskipun itu memalsukan pernyataan saya). Namun saya seharusnya mengatakan "Ini adalah ekstensi Microsoft dan tidak dijamin untuk bekerja pada kompiler lain." Saya akan mengubah yang asli.
Michael J
@MichaelJ Saya terutama merujuk ke bagian "Perubahan Kode ...", yang menjelaskan mengapa RAD Studio sekarang menggunakan _tmain sebagai ganti main, dan sebenarnya ini sekarang merupakan standar standar untuk C ++ Builder dari Embarcadero.
b1nary.atr0phy
1
Itu adalah kedua kalinya baru-baru ini bahwa jawaban empat tahun ini telah diturunkan. Akan lebih baik jika downvoters berkomentar menjelaskan masalah apa yang mereka rasakan dan (jika mungkin) bagaimana meningkatkan jawabannya. b1naryatr0phy menemukan kalimat yang ditulis dengan buruk, tetapi saya memperbaikinya pada bulan Maret. Bimbingan apa pun akan dihargai.
Michael J
2
Hidup ini terlalu singkat untuk ini.
Michael J
10

konvensi _T digunakan untuk menunjukkan program harus menggunakan set karakter yang ditentukan untuk aplikasi (Unicode, ASCII, MBCS, dll.). Anda dapat mengelilingi string Anda dengan _T () agar disimpan dalam format yang benar.

 cout << _T( "There are " ) << argc << _T( " arguments:" ) << endl;
Paul Alexander
sumber
Bahkan, MS merekomendasikan pendekatan ini, afaik. Membuat aplikasi Anda sadar-kode, mereka menyebutnya ... menggunakan versi _t dari semua fungsi manipulasi string juga.
Deep-B
1
@ Deep-B: Dan di Windows, ini adalah bagaimana Anda membuat aplikasi Anda siap-unicode (Saya lebih suka istilah unicode-ready untuk -tahu), jika didasarkan pada chars sebelumnya. Jika aplikasi Anda langsung digunakan wchar_tmaka aplikasi Anda adalah unicode.
paercebal
5
Ngomong-ngomong, jika Anda mencoba mengkompilasi pada UNICODE, maka kode Anda tidak akan dikompilasi sebagai wchar_t keluaran Anda di dalam cout berbasis char, di mana seharusnya wcout. Lihat jawaban Michael J untuk contoh mendefinisikan "tcout" ...
paercebal
1
Tidak ada jika ini direkomendasikan oleh Microsoft, terutama, karena itu jelas salah. Ketika mengkompilasi untuk Unicode, kode menulis nilai pointer ke aliran output standar. -1.
IInspectable
5

Ok, pertanyaannya tampaknya telah dijawab dengan cukup baik, UNICODE yang berlebihan harus mengambil array karakter yang lebar sebagai parameter kedua. Jadi jika parameter baris perintah "Hello"yang mungkin berakhir "H\0e\0l\0l\0o\0\0\0"dan program Anda hanya akan mencetak 'H'sebelum ia melihat apa yang dianggapnya adalah terminator nol.

Jadi sekarang Anda mungkin bertanya-tanya mengapa itu mengkompilasi dan tautan.

Yah itu dikompilasi karena Anda diizinkan untuk mendefinisikan kelebihan fungsi.

Menautkan adalah masalah yang sedikit lebih kompleks. Di C, tidak ada informasi simbol yang didekorasi sehingga hanya menemukan fungsi yang disebut main. Argc dan argv mungkin selalu ada sebagai parameter call-stack untuk berjaga-jaga bahkan jika fungsi Anda didefinisikan dengan tanda tangan itu, bahkan jika fungsi Anda kebetulan mengabaikannya.

Meskipun C ++ memang memiliki simbol yang didekorasi, hampir pasti menggunakan C-linkage untuk main, daripada linker yang pintar yang mencari masing-masing simbol secara bergantian. Jadi ia menemukan wmain Anda dan meletakkan parameter ke tumpukan panggilan jika itu adalah int wmain(int, wchar_t*[])versi.

Uang tunai
sumber
Ok, jadi saya punya masalah porting kode saya ke windows widechar selama bertahun-tahun sekarang dan ITULAH pertama kali saya mengerti mengapa ini terjadi. Sini, ambil semua reputasiku! haha
Leonel
-1

Dengan sedikit upaya templatizing ini, ia akan bekerja dengan daftar objek.

#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
sumber