Haruskah saya menggunakan char ** argv atau char * argv []?

125

Saya baru belajar C dan bertanya-tanya mana yang harus saya gunakan dalam metode utama saya. Apakah ada perbedaan? Mana yang lebih umum?

Kredns
sumber
2
Saya lebih suka 'char ** argv', dan jadi saya melihatnya lebih sering, tetapi keduanya benar dan saya tidak akan mengubah deklarasi hanya karena itu ditulis 'char * argv []'.
Jonathan Leffler
+1 karena masalah yang lebih dalam dari array vs pointer penting untuk dipahami dengan baik.
RBerteig
2
Sungguh, pertanyaan yang sangat bagus. Terima kasih.
Frank V
5
Baca bagian 6 dari FAQ comp.lang.c ; itu penjelasan terbaik tentang hubungan antara array C dan pointer yang pernah saya lihat. Dua poin yang relevan: 1. char **argvpersis sama dengan char *argv[]sebagai deklarasi parameter (dan hanya sebagai deklarasi parameter). 2. array bukan pointer.
Keith Thompson

Jawaban:

160

Karena Anda baru belajar C, saya sarankan Anda untuk benar-benar mencoba memahami perbedaan antara array dan pointer terlebih dahulu daripada hal - hal umum .

Di bidang parameter dan array, ada beberapa aturan membingungkan yang harus jelas sebelum melanjutkan. Pertama, apa yang Anda nyatakan dalam daftar parameter diperlakukan istimewa. Ada beberapa situasi di mana hal-hal yang tidak masuk akal sebagai parameter fungsi dalam C. Ini adalah

  • Berfungsi sebagai parameter
  • Array sebagai parameter

Array sebagai parameter

Yang kedua mungkin tidak segera jelas. Tetapi menjadi jelas ketika Anda menganggap bahwa ukuran dimensi array adalah bagian dari tipe C (dan array yang ukuran dimensi tidak diberikan memiliki tipe tidak lengkap). Jadi, jika Anda ingin membuat fungsi yang mengambil array dengan nilai (menerima salinan), maka itu bisa dilakukan hanya untuk satu ukuran! Selain itu, array dapat menjadi besar, dan C mencoba untuk menjadi secepat mungkin.

Di C, karena alasan ini, nilai array tidak ada. Jika Anda ingin mendapatkan nilai array, yang Anda dapatkan adalah pointer ke elemen pertama array itu. Dan di sini sebenarnya sudah ada solusinya. Alih-alih menggambar parameter array tidak valid di muka, kompiler C akan mengubah tipe parameter masing-masing menjadi pointer. Ingat ini, ini sangat penting. Parameter tidak akan menjadi array, melainkan akan menjadi pointer ke tipe elemen masing-masing.

Sekarang, jika Anda mencoba untuk melewatkan sebuah array, apa yang diteruskan adalah sebuah pointer ke elemen pertama array.

Ekskursi: Berfungsi sebagai parameter

Untuk penyelesaiannya, dan karena saya pikir ini akan membantu Anda lebih memahami masalah ini, mari kita lihat seperti apa keadaannya saat Anda mencoba memiliki fungsi sebagai parameter. Memang, pertama tidak masuk akal. Bagaimana suatu parameter bisa berfungsi? Huh, tentu saja kami menginginkan variabel di tempat itu! Jadi apa yang dilakukan kompiler ketika itu terjadi adalah, sekali lagi, untuk mengubah fungsi menjadi sebuah fungsi pointer . Mencoba untuk melewatkan suatu fungsi akan meneruskan sebuah pointer ke fungsi masing-masing sebagai gantinya. Jadi, berikut ini sama (analog dengan contoh array):

void f(void g(void));
void f(void (*g)(void));

Perhatikan bahwa tanda kurung *gdiperlukan. Jika tidak, itu akan menentukan fungsi yang kembali void*, bukan penunjuk ke fungsi yang kembali void.

Kembali ke array

Sekarang, saya katakan di awal bahwa array dapat memiliki tipe yang tidak lengkap - yang terjadi jika Anda belum memberi ukuran. Karena kita sudah mengetahui bahwa parameter array tidak ada tetapi parameter array adalah pointer, ukuran array tidak menjadi masalah. Itu artinya, kompiler akan menerjemahkan semua hal berikut, dan semuanya adalah hal yang sama:

int main(int c, char **argv);
int main(int c, char *argv[]);
int main(int c, char *argv[1]);
int main(int c, char *argv[42]);

Tentu saja, tidak masuk akal untuk dapat menempatkan ukuran apa pun di dalamnya, dan itu hanya dibuang begitu saja. Karena alasan itu, C99 memunculkan makna baru untuk angka-angka itu, dan memungkinkan hal-hal lain muncul di antara tanda kurung:

// says: argv is a non-null pointer pointing to at least 5 char*'s
// allows CPU to pre-load some memory. 
int main(int c, char *argv[static 5]);

// says: argv is a constant pointer pointing to a char*
int main(int c, char *argv[const]);

// says the same as the previous one
int main(int c, char ** const argv);

Dua baris terakhir mengatakan bahwa Anda tidak akan dapat mengubah "argv" dalam fungsi - itu telah menjadi pointer const. Hanya beberapa kompiler C yang mendukung fitur-fitur C99 itu. Tetapi fitur-fitur ini memperjelas bahwa "array" sebenarnya bukan satu. Ini sebuah pointer.

Sebuah kata peringatan

Perhatikan bahwa semua yang saya katakan di atas hanya benar ketika Anda sudah mendapatkan array sebagai parameter fungsi. Jika Anda bekerja dengan array lokal, array tidak akan menjadi pointer. Ini akan berperilaku sebagai pointer, karena seperti yang dijelaskan sebelumnya sebuah array akan dikonversi menjadi sebuah pointer ketika nilainya dibaca. Tapi jangan sampai bingung dengan pointer.

Salah satu contoh klasik adalah sebagai berikut:

char c[10]; 
char **c = &c; // does not work.

typedef char array[10];
array *pc = &c; // *does* work.

// same without typedef. Parens needed, because [...] has 
// higher precedence than '*'. Analogous to the function example above.
char (*array)[10] = &c;
Johannes Schaub - litb
sumber
2
Tidak tahu yang ini ada: * argv [statis 1]
superlukas
12

Anda bisa menggunakan keduanya. Mereka sepenuhnya setara. Lihat komentar litb dan jawabannya .

Ini benar-benar tergantung bagaimana Anda ingin menggunakannya (dan Anda dapat menggunakannya dalam hal apa pun):

// echo-with-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char **argv)
{
  while (--argc > 0)
  {
    printf("%s ", *++argv);
  }
  printf("\n");
  return 0;
}

// echo-without-pointer-arithmetic.c
#include <stdio.h>
int main(int argc, char *argv[])
{
  int i;
  for (i=1; i<argc; i++)
  {
    printf("%s ", argv[i]);
  }
  printf("\n");
  return 0;
}

Adapun yang lebih umum - tidak masalah. Setiap programmer C yang berpengalaman membaca kode Anda akan melihat keduanya sebagai dipertukarkan (dalam kondisi yang tepat). Seperti halnya seorang penutur bahasa Inggris yang berpengalaman membaca "mereka" dan "mereka" sama-sama mudah.

Yang lebih penting adalah Anda belajar membacanya dan mengenali betapa miripnya mereka. Anda akan membaca lebih banyak kode daripada yang Anda tulis, dan Anda harus merasa nyaman dengan keduanya.

rampion
sumber
7
char * argv [] adalah 100% setara dengan char ** argv ketika digunakan sebagai tipe parameter dari suatu fungsi. tidak ada "const" yang terlibat, juga tidak secara implisit. Keduanya adalah pointer ke pointer ke karakter. Ini berbeda dalam hal apa yang Anda nyatakan. Tetapi kompiler menyesuaikan jenis parameter menjadi penunjuk ke penunjuk, meskipun Anda mengatakan itu adalah array. Jadi, semuanya adalah sama: void f (char * p [100]); batal f (char * p []); batal f (char ** p);
Johannes Schaub - litb
4
Dalam C89 (yang kebanyakan orang gunakan) juga tidak ada cara untuk mengambil keuntungan dari fakta bahwa Anda mendeklarasikannya sebagai array (jadi secara semantik tidak masalah apakah Anda mendeklarasikan pointer atau array di sana - keduanya akan diambil sebagai pointer). Dimulai dengan C99, Anda bisa mendapat manfaat dari mendeklarasikannya sebagai array. Berikut ini mengatakan: "p selalu non-null dan menunjuk ke suatu wilayah dengan setidaknya 100bytes": void f (char p [static 100]); Perhatikan bahwa tipe-bijaksana, bagaimanapun, p masih pointer.
Johannes Schaub - litb
5
(khususnya, & p akan memberi Anda char **, tetapi bukan char ( ) [100] yang akan menjadi kasus jika p * akan menjadi array). Saya terkejut belum ada yang disebutkan dalam jawaban. Saya menganggapnya sangat penting untuk dipahami.
Johannes Schaub - litb
Secara pribadi saya lebih suka char**karena itu mengingatkan saya itu tidak boleh diperlakukan sebagai array nyata, seperti melakukan sizeof[arr] / sizeof[*arr].
raymai97
9

Itu tidak membuat perbedaan, tapi saya menggunakan char *argv[]karena itu menunjukkan bahwa itu adalah array ukuran tetap dari string panjang variabel (yang biasanya char *).

Zifre
sumber
4

Itu tidak benar-benar membuat perbedaan, tetapi yang terakhir lebih mudah dibaca. Apa yang Anda diberikan adalah array pointer char, seperti versi kedua katakan. Secara implisit dapat dikonversi menjadi pointer char ganda seperti pada versi pertama.

jalf
sumber
2

Anda harus mendeklarasikannya sebagai char *argv[], karena semua cara yang setara untuk mendeklarasikannya, yang paling mendekati makna intuisi: serangkaian string.

Andras
sumber
1

char ** → pointer ke pointer karakter dan char * argv [] berarti array dari pointer karakter. Karena kita dapat menggunakan pointer daripada array, keduanya dapat digunakan.

Alphaneo
sumber
0

Saya melihat tidak ada manfaat khusus menggunakan salah satu pendekatan dan bukan yang lain - gunakan konvensi yang paling sejalan dengan sisa kode Anda.

Christoffer
sumber
-2

Jika Anda membutuhkan jumlah string yang bervariasi atau dinamis, char ** mungkin lebih mudah untuk dikerjakan. Jika jumlah string Anda tetap, char * var [] akan lebih disukai.

samoz
sumber
-2

Saya tahu ini sudah usang, tetapi jika Anda baru belajar bahasa pemrograman C dan tidak melakukan apa pun yang utama dengan itu, jangan gunakan opsi baris perintah.

Jika Anda tidak menggunakan argumen baris perintah, jangan gunakan keduanya. Deklarasikan fungsi utama sebagai int main() Jika Anda

  • Ingin pengguna program Anda dapat menyeret file ke program Anda sehingga Anda dapat mengubah hasil program Anda dengan itu atau
  • Ingin menangani opsi baris perintah ( -help, /?, atau hal lain yang terjadi setelah program namedi terminal atau command prompt)

gunakan mana yang lebih masuk akal bagi Anda. Jika tidak, cukup gunakan int main() Setelah semua, jika Anda akhirnya ingin menambahkan opsi baris perintah, Anda dapat dengan mudah mengeditnya nanti.

Jesse
sumber
Ini tidak menjawab pertanyaan.
Lundin