Char ** argv veya char * argv [] kullanmalı mıyım?


125

Sadece C öğreniyorum ve ana yöntemimde bunlardan hangisini kullanmam gerektiğini merak ediyordum. Herhangi bir fark var mı? Hangisi daha yaygındır?


2
"Char ** argv" yi tercih ederim ve bu yüzden onu daha sık görüyorum, ancak her ikisi de doğru ve bir bildirimi sırf "char * argv []" yazıldığı için değiştirmem.
Jonathan Leffler

+1 çünkü dizi ile işaretçi arasındaki daha derin meselelerin iyi anlaşılması önemlidir.
RBerteig

2
Gerçekten, gerçekten güzel soru. Teşekkür ederim.
Frank V

5
Comp.lang.c SSS bölüm 6'yı okuyun ; C dizileri ve gördüğüm işaretçiler arasındaki ilişkinin en iyi açıklaması. İlgili iki nokta: 1. bir parametre bildirimine char **argvtam olarak eşdeğerdir char *argv[](ve yalnızca bir parametre bildirimi olarak). 2. diziler işaretçi değildir.
Keith Thompson

Yanıtlar:


160

Henüz C öğrenirken, önce sıradan şeyler yerine diziler ve işaretçiler arasındaki farkları gerçekten anlamaya çalışmanızı tavsiye ederim .

Parametreler ve diziler alanında, devam etmeden önce açık olması gereken birkaç kafa karıştırıcı kural vardır. İlk olarak, bir parametre listesinde bildirdiğiniz şey özel olarak ele alınır. C'de işlev parametresi olarak şeylerin anlamsız olduğu durumlar vardır. Bunlar şunlardır:

  • Parametre olarak işlevler
  • Parametre olarak diziler

Parametre olarak diziler

İkincisi belki hemen belli değil. Ancak, bir dizi boyutunun boyutunun C'deki türün bir parçası olduğunu (ve boyut boyutu verilmeyen bir dizinin eksik bir türü olduğunu) düşündüğünüzde netleşir. Öyleyse, diziyi değerine göre alan (bir kopya alan) bir işlev oluşturacaksanız, bunu yalnızca bir boyut için yapabilir! Ek olarak, diziler büyüyebilir ve C olabildiğince hızlı olmaya çalışır.

C'de, bu nedenlerden dolayı, dizi değerleri mevcut değildir. Bir dizinin değerini almak istiyorsanız, onun yerine elde ettiğiniz şey, o dizinin ilk elemanına bir göstericidir. Ve aslında zaten çözüm burada yatıyor. Bunun yerine, bir dizi parametre çizim-ön geçersiz, bir Cı-derleyici dönüşümü , ilgili parametresinin türü bir işaretçi olduğu. Bunu unutma, bu çok önemli. Parametre bir dizi olmayacak, bunun yerine ilgili öğe türüne bir gösterici olacaktır.

Şimdi, bir dizi geçirmeye çalışırsanız, bunun yerine iletilen şey dizilerin ilk elemanına bir göstericidir.

Gezi: Parametre olarak işlevler

Tamamlanması için ve bunun konuyu daha iyi anlamanıza yardımcı olacağını düşündüğüm için, parametre olarak bir işleve sahip olmaya çalıştığınızda durumun ne olduğuna bakalım. Aslında, ilk önce hiçbir anlam ifade etmeyecektir. Bir parametre nasıl bir işlev olabilir? Huh, o yerde elbette bir değişken istiyoruz! Ne bu olduğunda derleyici yapmasıdır Yani, yine, hiç dönüşümü bir içine işlev işlev işaretçisi . Bir işlevi iletmeye çalışmak, bunun yerine ilgili işleve bir işaretçi iletecektir. Dolayısıyla, aşağıdakiler aynıdır (dizi örneğine benzer):

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

Parantezlerin *ggerekli olduğunu unutmayın . Aksi takdirde, dönen bir işleve void*bir işaretçi yerine geri dönen bir işlevi belirtirdi void.

Dizilere dön

Şimdi, başlangıçta dizilerin eksik bir türe sahip olabileceğini söyledim - bu, henüz bir boyut vermezseniz olur. Bir dizi parametresinin olmadığını, bunun yerine herhangi bir dizi parametresinin bir işaretçi olduğunu zaten anladığımız için, dizinin boyutunun önemi yoktur. Bu, derleyicinin aşağıdakilerin tümünü çevireceği ve hepsi aynı şey olduğu anlamına gelir:

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]);

Elbette, içine herhangi bir boyut koyabilmek pek mantıklı değil ve sadece atılıyor. Bu nedenle, C99 bu sayılar için yeni bir anlam buldu ve parantezler arasında başka şeylerin görünmesine izin verdi:

// 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);

Son iki satır, işlev içinde "argv" yi değiştiremeyeceğinizi söylüyor - bu bir const işaretçisi haline geldi. Yine de yalnızca birkaç C derleyicisi bu C99 özelliklerini destekler. Ancak bu özellikler, "dizi" nin aslında bir olmadığını açıkça ortaya koyuyor. Bu bir işaretçi.

Uyarı kelimesi

Yukarıda söylediğim her şeyin yalnızca bir işlevin parametresi olarak bir diziye sahip olduğunuzda doğru olduğuna dikkat edin . Yerel dizilerle çalışıyorsanız, dizi bir işaretçi olmayacaktır. Bu olacak davranmaya değeri okunduğunda gibi bir işaretçi önceki bir dizi dönüştürülecektir açıkladı çünkü bir işaretçi olarak. Ancak işaretçilerle karıştırılmamalıdır.

Klasik bir örnek şudur:

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;

2
Bunun var olduğu hakkında hiçbir fikrim yoktu: * argv [statik 1]
superlukas

12

İkisini de kullanabilirsin. Tamamen eşdeğerler. Bkz LITB yaptığı yorumları ve onun cevabını .

Gerçekten onu nasıl kullanmak istediğinize bağlı (ve her durumda ikisini de kullanabilirsiniz):

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

Hangisinin daha yaygın olduğu önemli değil. Kodunuzu okuyan herhangi bir deneyimli C programcısı, her ikisini de birbirinin yerine kullanılabilir (doğru koşullar altında) görecektir. Tıpkı deneyimli bir İngilizce konuşmacının "onlar" ve "onlar" kelimesini eşit derecede kolayca okuması gibi.

Daha da önemlisi, onları okumayı öğrenmeniz ve ne kadar benzer olduklarını fark etmenizdir. Yazdığınızdan daha fazla kod okuyacaksınız ve her ikisinde de eşit derecede rahat olmanız gerekecek.


7
char * argv [], bir işlevin parametre türü olarak kullanıldığında char ** argv'ye% 100 eşdeğerdir. hiçbir "const" dahil değildir, ayrıca örtük olarak değil. Her ikisi de karakterlere işaret eden işaretçilerdir. Ne beyan ettiğinize göre farklı. Ancak derleyici, bir dizi olduğunu söylemiş olsanız bile, parametrenin türünü bir işaretçiye işaretçi olacak şekilde ayarlar. Böylece, aşağıdakilerin tümü aynıdır: void f (char * p [100]); void f (char * p []); void f (char ** p);
Johannes Schaub - litb

4
C89'da (çoğu insanın kullandığı), onu bir dizi olarak tanımladığınız gerçeğinden yararlanmanın bir yolu da yoktur (bu nedenle, anlamsal olarak, orada bir işaretçi mi yoksa bir dizi mi bildirdiğiniz önemli değildir - her ikisi de bir Işaretçi). C99'dan başlayarak, onu bir dizi olarak bildirmekten faydalanabilirsiniz. Aşağıdakiler şöyle der: "p her zaman boş değildir ve en az 100 baytlık bir bölgeyi gösterir": void f (char p [statik 100]); Yazım açısından p'nin yine de bir işaretçi olduğuna dikkat edin .
Johannes Schaub -

5
(özellikle, & p size bir char ** verecektir, ancak bir char ( ) [100] vermeyecektir, p * bir dizi olursa bu durum böyle olacaktır ). Henüz kimsenin bir cevapta belirtilmemesine şaşırdım. Bunu anlamanın çok önemli olduğunu düşünüyorum.
Johannes Schaub - litb

Şahsen tercih ederim char**çünkü bana yapmak gibi gerçek bir dizi olarak değerlendirilmemesi gerektiğini hatırlatıyor sizeof[arr] / sizeof[*arr].
raymai97

9

Bir fark yaratmaz, ancak kullanıyorum char *argv[]çünkü bunun değişken uzunluklu dizelerden oluşan sabit boyutlu bir dizi olduğunu gösteriyor (genellikle bunlar char *).



4

Gerçekten bir fark yaratmaz, ancak ikincisi daha okunaklı. Size verilen şey, ikinci versiyonun dediği gibi bir dizi karakter işaretidir. Bununla birlikte, ilk versiyondaki gibi dolaylı olarak bir çift karakterli göstericiye dönüştürülebilir.


2

Bunu, onu char *argv[]tanımlamanın birçok eşdeğer yolu nedeniyle, sezgisel anlamına en yakın olan bir dizge dizisi olarak ilan etmelisiniz .


1

char ** → karakter göstericisine işaretçi ve char * argv [], karakter işaretçileri dizisi anlamına gelir. Dizi yerine işaretçi kullanabildiğimiz için her ikisi de kullanılabilir.


0

Her iki yaklaşımı da diğerinin yerine kullanmanın özel bir yararı görmüyorum - kodunuzun geri kalanıyla en uyumlu olan kuralı kullanın.


-2

Değişken veya dinamik sayıda dizeye ihtiyacınız varsa, char ** ile çalışmak daha kolay olabilir. Dizge sayınız sabitse, char * var [] tercih edilir.


-2

Bunun modası geçmiş olduğunu biliyorum, ancak sadece C programlama dilini öğreniyorsanız ve onunla büyük bir şey yapmıyorsanız, komut satırı seçeneklerini kullanmayın.

Komut satırı bağımsız değişkenlerini kullanmıyorsanız, ikisini de kullanmayın. Sadece ana işlevi int main() siz

  • Programınızın kullanıcısının, programınızın sonucunu değiştirebilmeniz için programınıza bir dosya sürükleyebilmesini veya
  • Komut satırı seçeneklerini ( -help, /?veya program nameterminalde veya komut isteminde sonra gelen herhangi bir şeyi) işlemek ister

Hangisi size daha anlamlı geliyorsa onu kullanın. Aksi takdirde, Sonuçta'yı kullanın int main() , sonunda komut satırı seçenekleri eklemek isterseniz, bunları daha sonra kolayca düzenleyebilirsiniz.


Bu soruya cevap vermiyor.
Lundin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.