C / C ++ ana argv neden sadece “char * argv” yerine “char * argv []” olarak ilan edildi?


21

Neden argvsadece "dizinin ilk dizinine işaretçi" olmak yerine "dizinin ilk dizinine işaretçi" olarak bildirildi char* argv?

Burada "göstericiden işaretçiye" kavramı neden gerekli?


4
"dizinin ilk dizinine işaretçi için işaretçi" - Bu, char* argv[]ya da 'nın doğru bir açıklaması değildir char**. Bu, bir karakterin işaretçisine bir göstericidir; özellikle dış işaretçi bir dizideki ilk işaretleyiciye işaret eder ve iç işaretçiler silinmemiş dizgelerin ilk karakterlerine işaret eder. Buraya dahil endeks yok.
Sebastian Redl

12
Eğer sadece char * argv olsaydı ikinci tartışmayı nasıl elde edersiniz?
gnasher729

15
Alanı doğru yere koyduğunuzda hayatınız kolaylaşacak. char* argv[]boşluğu yanlış yere koyuyor. Diyelim char *argv[]ve şimdi bunun "ifade *argv[n]bir değişkendir " anlamına geldiği açık char. İşaretçi nedir ve işaretçi için işaretçi nedir, vb. Çalışmaya çalışırken yakalanmayın. Beyan, bu konuda hangi işlemleri yapabileceğinizi söylüyor .
Eric Lippert

1
Zihinsel char * argv[]olarak benzer C ++ yapısıyla karşılaştırırsanız std::string argv[], ayrıştırılması daha kolay olabilir. ... Sadece aslında bu şekilde yazmaya başlama !
Justin Time 2 Monica

2
@EricLippert, bu sorunun C ++ 'ı da içerdiğini ve orada türüne char &func(int);sahip olmayan örneğin sahip olabileceğinizi unutmayın . &func(5)char
Ruslan,

Yanıtlar:


59

Argv temelde şöyle:

görüntü tanımını buraya girin

Solda, bağımsız değişkenin kendisi - aslında ana argüman olarak geçen şey. Bir işaretçi dizisinin adresini içerir. Bunların her biri, komut satırında iletilen karşılık gelen argümanın metnini içeren bellekte bir yere işaret eder. Ardından, bu dizinin sonunda boş bir işaretçi olduğu garanti edilir.

Bağımsız değişkenler için gerçek depolamanın en azından potansiyel olarak birbirinden ayrı olarak tahsis edildiğine dikkat edin, bu nedenle bellekteki adresleri oldukça rasgele düzenlenebilir (ancak, işlerin nasıl yazıldığına bağlı olarak, aynı zamanda tek bir bitişik blokta da olabilirler). hafıza - sadece bilmiyorsunuz ve umursamamanız gerekiyor).


52
Düzen motoru ne çizdiyse çizsin, minimize-geçiş algoritmalarında hata var!
Eric Lippert

43
@EricLippert Pointelerin bitişik ve sıralı olmayabileceğini vurgulamak için kasıtlı olabilir.
jamesdlin

3
Kasıtlı olduğunu söyleyebilirim
Michael

24
Kesinlikle kasıtlıydı - ve sanırım Eric muhtemelen bunu anlamıştı, ama (doğru şekilde IMO) yorumun yine de komik olduğunu düşündü.
Jerry Coffin,

2
@ JerryCoffin, gerçek argümanlar bellekte bitişik olsalar bile, keyfi uzunlukları olabileceğine işaret edebilir; bu nedenle, her biri argv[i]önceki tüm taramaları gerçekleştirmeden erişebilmeleri için farklı işaretçilere ihtiyaç duyacaklarını söyleyebilir .
ilkkachu

22

Çünkü işletim sisteminin sağladığı şey :-)

Sorunuz biraz tavuk / yumurta inversiyonu sorunudur. Sorun C ++ 'da istediğinizi seçmek değil, sorun C ++' da işletim sisteminin size ne verdiğini söylemektir.

Unix, her biri bir komut argümanı olan bir "dizge" dizisinden geçer. C / C ++ 'da, bir dize bir "char *" dır, bu nedenle bir dizi dizgi tadı gereği char * argv [] veya char ** argv olur.


13
Hayır, tam olarak "C ++ 'da istediğinizi seçme sorunu". Örneğin, Windows komut satırını tek bir dize olarak sağlar ve C / C ++ programları yine de argvdizilerini alır - çalışma zamanı komut satırını belirlemeyi ve argvbaşlangıçta diziyi oluşturmayı önemser .
Joker_vD

14
@Joker_vD Ben bükülmüş bir şekilde bu işletim sistemi size verdiği ile ilgili olduğunu düşünüyorum. Spesifik olarak: C ++ bu şekilde yaptı, çünkü C bu şekilde yaptı ve C, bu şekilde yaptı çünkü C ve Unix'in birbiriyle ayrılmaz bir şekilde bağlantısı vardı ve Unix bu şekilde yaptı.
Daniel Wagner,

1
@DanielWagner: Evet, bu C'nin Unix mirasına ait. Unix / Linux'ta _startçağıran minimal bir kişinin bir göstericiyi bellekteki mevcut diziye maingeçirmesi yeterlidir ; zaten doğru biçimde. Çekirdek onu argv argümanından yeni bir çalıştırılabilir başlatmak için yapılan sistem çağrısına kopyalar . (Linux'ta, argv [] (dizinin kendisi) ve argc, işlem girişindeki yığındadır. Çoğu Unix'in aynı olduğunu varsayıyorum, çünkü bunun için iyi bir yer.)mainargvexecve(const char *filename, char *const argv[], char *const envp[])
Peter Cordes

8
Ancak Joker’in buradaki amacı, C / C ++ standartlarının onu, kaynakların geldiği uygulamaya bırakmasıdır; İşletim sisteminden doğrudan olmak zorunda değiller. Düz bir dizgeden geçen bir işletim sisteminde, iyi bir C ++ uygulaması argc=2, tüm düz dizgiyi ayarlamak ve geçmek yerine, tokenizing içermelidir . (Standart mektubunu takip etmek faydalı olmak için yeterli değildir ; uygulama seçimleri için kasten yer bırakmaktadır.) Bazı Windows programları özel olarak fiyat teklifleri uygulamak isteyecek olsa da, gerçek uygulamalar düz dize almanın bir yolunu sağlar. çok.
Peter Cordes

1
Basile'nin cevabı hemen hemen bu + @ Joker düzeltmesini ve yorumlarımı daha ayrıntılı olarak açıklıyor.
Peter Cordes

15

Birincisi, parametre bildirimi char **argvolarak char *argv[]; her ikisi de bir işaretçiyi (bir dizi veya bir veya daha fazla mümkün kümesi) işaretçilerine dizeleri gösterir.

Daha sonra, eğer sadece "char - char" işaretine sahipseniz - örneğin sadece char *- o zaman nth maddesine erişmek için, nth maddesinin başlangıcını bulmak için ilk n-1 maddelerini taramanız gerekecektir. (Ve bu, aynı zamanda her bir dizginin bitişik olarak kaydedilmesi şartını getirecektir.)

İşaretçiler dizisiyle, nth öğesini doğrudan dizine ekleyebilirsiniz - bu nedenle (kesinlikle gerekli olmasa da - dizelerin bitişik olduğunu varsayarak) genellikle çok daha uygundur.

Göstermek için:

./program merhaba dünya

argc = 3
argv[0] --> "./program\0"
argv[1] --> "hello\0"
argv[2] --> "world\0"

Bir os sağlanan karakter dizisinde mümkündür:

            "./program\0hello\0world\0"
argv[0]      ^
argv[1]                 ^
argv[2]                        ^

argv sadece bir "char işaretçisi" olsaydı

       "./program\0hello\0world\0"
argv    ^

Bununla birlikte (işletim sisteminin tasarımıyla muhtemel olsa da), üç dizenin "./program", "merhaba" ve "dünya" nın bitişik olduğuna dair gerçek bir garanti yoktur. Ayrıca, bu tür "çoklu bitişik dizelere tek gösterici", özellikle dize işaretçiler dizisiyle karşılaştırıldığında, daha sıradışı bir veri tipi yapısıdır (C için).


Ne yerine, eğer argv --> "hello\0world\0"sen sahip argv --> index 0 of the array(Merhaba), sadece normal bir dizi gibi. bu neden yapılamaz? Sonra dizi argczamanlarını okumaya devam edersiniz . o zaman argv 'nin kendisi değil, argv' nin bir göstergesi değil.
bir kullanıcı

@ auser, işte argv -> "./program\0hello\0\world\0" şudur: ilk karaktere bir işaretçi (yani, ".") Eğer bu işaretçiyi ilk \ 0 değerinden sonra alırsanız, o zaman "Merhaba \ 0" işaretçisi var ve ondan sonra "Dünya \ 0" işaretçisi var. Değişken zamanlardan sonra (\ \ "
isabetini

Örnekte argv[4]olduğunu belirtmeyi unuttunNULL
Basile Starynkevitch

3
Bir garantisi var (en azından başlangıçta) argv[argc] == NULL. Bu durumda argv[3], öyle değil argv[4].
Miral

1
@Hill, evet, boş karakter sonlandırıcıları hakkında açık olmaya çalıştığım için teşekkür ederim (ve bunu kaçırdım).
Erik Eidt

13

Neden C / C ++ main argv “char * argv []” olarak ilan edildi?

Muhtemel bir cevap, C11 standardı n1570'in ( §5.1.2.2.1'de Program başlangıcında ) ve C ++ 11 standardı n3337'nin ( §3.6.1'de ana fonksiyonda ) barındırılan ortamlar için gerekli olmasını gerektirmesidir (ancak C standardının bahsettiğine dikkat edin). Ayrıca §5.1.2.1 ortamları müstakil da) Bkz bu .

Sonraki soru, C ve C ++ standartlarının neden mainböyle bir int main(int argc, char**argv)imzayı seçtikleridir ? Açıklama büyük ölçüde tarihsel: ile icat edilmiştir Unix bir sahiptir, kabuk etmez globbing yapmadan önce forkve (bir işlem oluşturmak için bir sistem çağrısı olan) execve, (a programı çalıştırmak için sistem çağrısı olan), ve bu execveileten bir dizi dize program argümanlarının ve mainyürütülen programınkilerle ilgilidir. Unix felsefesi ve ABI'ler hakkında daha fazla bilgi edinin .

Ve C ++, C kurallarını takip etmek ve onunla uyumlu olmak için çok çalıştı. mainC gelenekleriyle uyumsuz olduğunu tanımlayamadı .

Bir işletim sistemini sıfırdan (hala bir komut satırı arayüzüne sahip olan) ve sıfırdan bir programlama dili tasarladıysanız, farklı program başlangıç ​​kuralları icat etmekte özgür olacaksınız. Ve diğer programlama dilleri (örneğin Common Lisp veya Ocaml veya Go) farklı program başlangıç ​​kurallarına sahiptir.

Uygulamada, mainbazı crt0 kodu ile çağrılır . Windows'ta globlamanın her program tarafından crt0'a eşdeğer olarak yapılabildiğine ve bazı Windows programlarının standart olmayan WinMain giriş noktasından başlayabildiğine dikkat edin . Unix'te, globbing kabuk tarafından yapılır (ve crt0ABI ve onun belirttiği ilk çağrı yığını düzenini C uygulamanızın çağrı kurallarına göre uyarlar).


12

"İşaretçi işaretçisi işaretçisi" olarak düşünmek yerine, diziyi []ifade eden ve char*dizgeyi ifade eden "dizge dizisi " olarak düşünmeye yardımcı olur . Bir programı çalıştırdığınızda, bir veya daha fazla komut satırı argümanını iletebilirsiniz ve bunlar argümanlara yansıtılır main: argcbağımsız değişkenlerin sayısıdır ve bağımsız değişkenlere argverişmenize izin verir.


2
+1 Bu! Pek çok dilde - bash, PHP, C, C ++ - argv bir dizge dizisidir. Bunları gördüğünüzde char **ya char *[]da aynı olduğunu düşünmeniz gerekir .
rexkogitans

1

Çoğu durumda cevap "standart olduğu için" dır. C99 standardını alıntılamak için :

- Eğer argc değeri sıfırdan büyükse, argv [argc-1] inclusive içindeki argv [0] dizisi dizileri , program başlangıcından önce host ortamı tarafından uygulama tarafından tanımlanan değerler verilen dizgilere işaretçiler içermelidir .

Elbette, standartlaştırılmadan önce, K&R C tarafından Unix uygulamalarında, komut satırı parametrelerini saklamak amacıyla ( gömülü sistemler gibi /bin/bashya da /bin/shgömülü sistemler gibi Unix kabuğunda dikkat etmeniz gerekenler ) zaten kullanılıyordu. K & R’lerin “C Programlama Dili” nin ilk baskısını sunmak (sayfa 110) :

Birincisi (geleneksel olarak argc olarak adlandırılır ), programın çağrıldığı komut satırı argümanlarının sayısıdır; ikincisi ( argv ), her bir dize olmak üzere, bağımsız değişkenleri içeren bir karakter dizgileri dizisinin göstericisidir.

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.