Mengapa argc bukan konstanta?

104
int main( const int argc , const char[] const argv)

Karena C ++ Item # 3 yang efektif menyatakan "Gunakan const bila memungkinkan", saya mulai berpikir "mengapa tidak membuat parameter 'konstan' ini const"?.

Apakah ada skenario di mana nilai dari argcdiubah dalam sebuah program?

Dinushan
sumber
38
Anda bebas untuk menyatakan argcsebagai const.
Oliver Charlesworth
14
Saya telah melihat banyak kode kualitas produksi yang melakukan ini:--argc
Aniket Inge
2
Saya pikir itu ada hubungannya dengan warisan C, tapi tidak tahu caranya.
Luis Machuca
13
Saya menduga bahwa "Gunakan const bila memungkinkan" mengacu pada hal-hal yang diteruskan oleh referensi, di mana setiap modifikasi dalam fungsi tetap ada setelah fungsi keluar. Ini tidak benar dengan hal-hal yang diteruskan oleh nilai, seperti integer, jadi tidak ada yang bisa diperoleh dengan membuatnya const; memang, lewat argcsebagai const intsarana yang tidak bisa Anda gunakan argcsebagai, katakanlah, penghitung di dalam fungsi.
Steve Melnikoff
4
@SteveMelnikoff: Saya tidak setuju bahwa tidak ada yang diperoleh dengan membuat constparameter pass-by-value. Lihat misalnya stackoverflow.com/a/8714278/277304 dan stackoverflow.com/a/117557/277304
leonbloy

Jawaban:

114

Dalam hal ini, sejarah adalah salah satu faktornya. C mendefinisikan input ini sebagai "tidak konstan", dan kompatibilitas dengan (sebagian besar) kode C yang ada adalah tujuan awal C ++.

Beberapa API UNIX, seperti getopt, benar-benar memanipulasi argv[], jadi tidak bisa dibuat constkarena alasan itu juga.

(Selain: Menariknya, meskipun getoptprototipe menyarankan itu tidak akan memodifikasi argv[]tetapi dapat memodifikasi string yang ditunjuk, halaman manual Linux menunjukkan bahwa getoptmemperbolehkan argumennya, dan tampaknya mereka tahu mereka nakal . Halaman manual di Open Grup tidak menyebutkan permutasi ini.)

Menempatkan constpada argcdan argvtidak akan membeli banyak, dan itu akan membatalkan beberapa tua-sekolah praktek, seperti pemrograman:

// print out all the arguments:
while (--argc)
    std::cout << *++argv << std::endl;

Saya telah menulis program seperti itu dalam C, dan saya tahu saya tidak sendiri. Saya menyalin contoh dari suatu tempat .

Joe Z
sumber
1
-1 Re "Beberapa API UNIX, seperti getopt, sebenarnya memanipulasi argv [], jadi tidak bisa dijadikan const karena alasan itu juga.". Seseorang dapat dengan bebas menambahkan level atas constke argv, tanpa memengaruhi fungsi C apa pun yang dapat dilakukan. Juga, getoptdideklarasikan sebagai int getopt(int argc, char * const argv[], const char *optstring);. Dan di sini constbukan tingkat atas, tetapi menyatakan petunjuknya const, janji untuk tidak memodifikasinya (meskipun string yang mereka tunjuk mungkin dapat dimodifikasi).
Cheers and hth. - Alf
2
Maaf, dokumen yang saya lihat tidak konsisten: tanda tangan tidak mengizinkan permutasi seperti itu. Lihat contoh . Namun, teks berikut mengatakan itu permute. Jadi, saya menghapus suara negatif tersebut.
Cheers and hth. - Alf
1
Artinya, Anda harus melakukan beberapa pengeditan untuk "membuka" agar saya dapat menghapus suara negatif tersebut. Dan tidak, Anda salah paham dengan prototipe. Lihat contoh yang saya berikan dan kesalahan kompilasi, "penugasan lokasi hanya baca".
Cheers and hth. - Alf
1
@ Cheersandhth.-Alf: Menarik ... Saya melihat prototipe di tajuk, dan char* const* ___argvada di sana, tetapi antarmuka benar-benar melekat const char **. Kebingungan seperti itu. Saya telah bingung dengan presedensi []dan *sehubungan dengan const. Permintaan maaf saya.
Joe Z
1
FYI: GNU getopt()tahu itu tidak mengikuti standar karena memperhatikan POSIXLY_CORRECTvariabel lingkungan untuk membuatnya berperilaku dengan benar. Secara khusus, POSIX tidak mengubah argumen, dan tidak mencari opsi setelah argumen non-opsi pertama.
Jonathan Leffler
36

Standar C (ISO / IEC 9899: 2011) mengatakan:

5.1.2.2.1 Memulai program

¶1 Nama fungsi yang dipanggil saat program dimulai main. Implementasinya menyatakan tidak ada prototipe untuk fungsi ini. Ini harus didefinisikan dengan tipe kembalian int dan tanpa parameter:

int main(void) { /* ... */ }

atau dengan dua parameter (disebut di sini sebagai argcdan argv, meskipun nama apa pun dapat digunakan, karena bersifat lokal ke fungsi di mana mereka dideklarasikan):

int main(int argc, char *argv[]) { /* ... */ }

atau setara; 10) atau dengan cara lain yang ditentukan implementasi.

¶2 Jika dideklarasikan, parameter mainfungsi harus mematuhi batasan berikut:

  • Nilai argcharus nonnegatif.
  • argv[argc] akan menjadi penunjuk nol.
  • Jika nilai argclebih besar dari nol, anggota array argv[0]melalui argv[argc-1]inklusif harus berisi pointer ke string, yang diberikan nilai yang ditentukan implementasi oleh lingkungan host sebelum program dimulai. Tujuannya adalah untuk memberikan informasi program yang ditentukan sebelum program dimulai dari tempat lain di lingkungan yang dihosting. Jika lingkungan host tidak mampu menyediakan string dengan huruf besar dan kecil, implementasi harus memastikan bahwa string diterima dalam huruf kecil.
  • Jika nilai argclebih besar dari nol, string yang ditunjukkan oleh argv[0] mewakili nama program; argv[0][0]akan menjadi karakter null jika nama program tidak tersedia dari lingkungan host. Jika nilai argclebih besar dari satu, string yang ditunjuk oleh argv[1]melalui argv[argc-1] mewakili parameter Program.
  • Parameter argcdan argvstring yang ditunjukkan oleh argvlarik harus dapat dimodifikasi oleh program, dan mempertahankan nilai yang terakhir disimpan antara startup program dan penghentian program.

10) Jadi, intdapat diganti dengan nama typedef didefinisikan sebagai int, atau jenis argvdapat ditulis sebagai char **argv, dan seterusnya.

Perhatikan poin-poin terakhir. Dikatakan bahwa keduanya argcdan argvharus dapat dimodifikasi. Mereka tidak harus dimodifikasi, tetapi dapat dimodifikasi.

Jonathan Leffler
sumber
Menarik. Pada catatan semi-terkait, saya menemukan bug penyebab crash yang ternyata karena QApplication(argc,argv)konstruktor Qt didefinisikan dengan argcreferensi . Itu mengejutkanku.
HostileFork mengatakan jangan percaya SE
23

argcbiasanya tidak konstan karena tanda tangan fungsi untuk main()tanggal sebelumnya const.

Karena argc adalah variabel tumpukan, mengubahnya tidak akan memengaruhi apa pun selain pemrosesan baris perintah Anda sendiri.

Anda, tentu saja, bebas untuk menyatakannya constjika Anda mau.

razeh
sumber
8

Level teratas constpada argumen formal bukanlah bagian dari tipe fungsi. Anda dapat menambahkan atau menghapusnya sesuka Anda: ini hanya memengaruhi apa yang dapat Anda lakukan dengan argumen dalam implementasi fungsi.

Jadi bagi argcAnda dapat menambahkan dengan bebas menambahkan file const.

Tetapi bagi argvAnda tidak dapat membuat data karakter consttanpa mengubah fungsi tanda tangan. Artinya, ini bukan salah satu maintanda tangan fungsi standar , dan tidak perlu dikenali sebagai mainfungsi. Jadi, bukan ide yang bagus.


Alasan yang baik untuk tidak menggunakan mainargumen standar dalam program non-mainan adalah karena di Windows mereka tidak dapat mewakili argumen program yang sebenarnya seperti nama file dengan karakter internasional. Itu karena di Windows mereka dengan konvensi yang sangat kuat yang dikodekan sebagai Windows ANSI. Di Windows Anda dapat menerapkan beberapa fasilitas akses argumen yang lebih portabel dalam kaitannya dengan GetCommandLinefungsi API.


Menyimpulkan, tidak ada yang mencegah Anda dari menambahkan constke argc, tapi yang paling berguna const-ness pada argvakan memberi Anda non-standar mainfungsi, sebagian besar mungkin tidak diakui sebagai demikian. Untungnya (dengan cara yang ironis) ada alasan bagus untuk tidak menggunakanmain argumen standar untuk kode serius portabel. Sederhananya, untuk praktiknya mereka hanya mendukung ASCII lama, dengan hanya huruf alfabet Inggris.

Cheers and hth. - Alf
sumber
1
Jika Anda mengembangkan untuk Windows dengan cygwin atau libc lain yang mendukung UTF-8 dengan benar (sejauh yang saya tahu, cygwin adalah satu-satunya saat ini tetapi saya memiliki seseorang yang mengerjakan opsi lain), maintanda tangan standar berfungsi dengan baik dan Anda bisa menerima argumen Unicode sewenang-wenang.
R .. GitHub STOP HELPING ICE
@R mereka juga berfungsi dengan baik di sebagian besar platform * nix. masalahnya bukanlah apakah ada platform tempat mereka bekerja. tapi, inisiatif yang sangat bagus , dan kebetulan saya melakukan hal yang sama, kurang lebih serius
Cheers and hth. - Alf
4

Tanda tangan dari mainadalah semacam artefak sejarah dari C. Secara historis Ctidak punya const.

Namun, Anda dapat mendeklarasikan parameter Anda constkarena efek const hanya untuk waktu kompilasi.

George
sumber
Ketika Anda mengatakan "C historis", seberapa historis yang kita bicarakan di sini?
Joe Z
8
constditambahkan ke C89 / C90; sebelumnya, itu bukan bagian dari C.
Jonathan Leffler
@JonathanLeffler: Anda tahu, saya lupa itu. Saya belajar C pada tahun 1992. Sebelumnya saya adalah seorang hacker Pascal, BASIC dan assembly.
Joe Z
2

Karena argcadalah variabel lokal (dan, dalam C ++, bukan referensi atau sesuatu), dan karena tempat khusus mainsarana bahwa kecocokan kompatibilitas mundur memberinya sejumlah besar kelonggaran tanpa alasan kuat untuk memaksa aplikasi membuatnya konstan.

main() {}

int main() {}

main() { return 0; }

main(int argc, char* argv[]) { return 0; }

int main(const int argc, const char** argv) { /* no return*/ }

ini dan banyak variasi lainnya akan dikompilasi pada berbagai kompiler C dan C ++.

Jadi pada akhirnya ini bukan karena argc bukanlah const, hanya saja argc tidak harus seperti itu, tetapi bisa juga jika Anda menginginkannya.

http://ideone.com/FKldHF , C contoh:

main(const int argc, const char* argv[]) { return 0; }

http://ideone.com/m1qc9c , C ++ contoh

main(const int argc) {}
kfsone.dll
sumber
Perhatikan bahwa varian tanpa tipe kembalian eksplisit tidak lagi valid di C99, apalagi C11, meskipun compiler terus mengizinkannya karena alasan kompatibilitas ke belakang. Compiler C ++ tidak boleh menerima varian tanpa tipe kembalian.
Jonathan Leffler
@JonathanLeffler Ya, namun contoh ide terakhir di sana ( ideone.com/m1qc9c ) adalah G ++ 4.8.2 dengan -std=c++11. Clang juga menerimanya tetapi tidak memberi warning: only one parameter on 'main' declaration [-Wmain], dan MSVC hanya memprotes tentang specifier tipe kembalian yang hilang dan kurangnya referensi ke argc. Sekali lagi, 'shenanigans' dan 'kelonggaran' :)
kfsone
1

Selain alasan historis, alasan yang baik untuk menyimpan argc dan argv non- constadalah karena implementasi compiler tidak tahu apa yang akan Anda lakukan dengan argumen ke main, ia hanya tahu bahwa ia harus memberi Anda argumen tersebut.

Saat Anda mendefinisikan fungsi Anda sendiri dan prototipe terkait, Anda tahu parameter mana yang dapat Anda buat constdan mana yang akan diubah oleh fungsi Anda.

Secara ekstrim, Anda dapat mendeklarasikan bahwa semua parameter untuk semua fungsi harus dideklarasikan const, dan kemudian jika Anda memiliki alasan untuk mengubahnya (misalnya menurunkan indeks untuk mencari melalui array), Anda harus membuat non- constvariabel lokal dan salin nilai constargumen ke variabel tersebut. Itu membuat pekerjaan menjadi sibuk dan LOC ekstra tanpa manfaat nyata. Penganalisis statis yang layak akan mengambil jika Anda tidak mengubah nilai argumen, dan menyarankan Anda membuat parameter const.

Sam Skuce
sumber