Yanıtlar:
Bunlar TAMAMEN eşdeğerdir. Ancak,
int *myVariable, myVariable2;
Bu myVariable tipi olduğu aşikardır int * myVariable2 tipi varken, int . İçinde
int* myVariable, myVariable2;
her iki tipi olduğu aşikar görünebilir int * , ancak bu doğru değildir myVariable2
tipi vardır int .
Bu nedenle, ilk programlama stili daha sezgiseldir.
int* someVar
kişisel projeler için uğraşıyorum. Daha mantıklı.
int[10] x
. Bu sadece C'nin sözdizimi değildir. Dilbilgisi açıkça şu şekilde ayrılır: int (*x)
değil (int *) x
, yıldız işaretini sola yerleştirmek yanıltıcıdır ve C bildirimi sözdiziminin yanlış anlaşılmasına dayanır.
int* myVariable; int myVariable2;
Bunun yerine kesinlikle karışıklık olmaz .
Eğer ona başka bir şekilde bakarsanız, bir anlam ifade eden *myVariable
tiptedir int
.
myVariable
NULL olabilir, bu durumda *myVariable
bir segmentasyon hatasına neden olur, ancak NULL türü yoktur.
int x = 5; int *pointer = &x;
çünkü int'i kendisi *pointer
değil, bir değere ayarladığımızı düşündürmektedir pointer
.
Çünkü bu satırdaki *, değişkene türden daha yakından bağlanır:
int* varA, varB; // This is misleading
@Lundin'in aşağıda belirttiği gibi, const düşünülecek daha fazla incelik ekler. Asla muğlak olmayan, her satırda bir değişken bildirerek bunu tamamen kaldırabilirsiniz:
int* varA;
int varB;
Açık kod ve özlü kod arasındaki dengeyi bulmak zordur - bir düzine gereksiz satır int a;
da iyi değildir. Yine de, varsayılan olarak satır başına bir bildirime ve daha sonra kodu birleştirme konusunda endişeleniyorum.
int *const a, b;
. * "Bağlanıyor" nerede? Tipi a
DİR int* const
, bu yüzden nasıl * o tip kendisinin bir parçası olan değişkene ait olduğunu söyleyebiliriz?
Burada şimdiye kadar kimsenin bahsetmediği bir şey, bu yıldız işaretinin aslında C'deki " dereference operatörü " olmasıdır.
*a = 10;
Çizgi üstünde ben atamak istediğiniz anlamına gelmez 10
kadar a
, ben atamak istediğiniz anlamına gelir 10
ne olursa olsun hafıza yeri için a
için puan. Ve hiç kimsenin yazdığını görmedim
* a = 10;
senin varmi? Yani KQUEUE operatör hemen hemen her zaman bir boşluk olmadan yazılır. Bu muhtemelen onu birden çok satırda kesilmiş bir çarpmadan ayırt etmektir:
x = a * b * c * d
* e * f * g;
İşte *e
yanıltıcı olurdu, değil mi?
Tamam, şimdi aşağıdaki satır aslında ne anlama geliyor:
int *a;
Çoğu insan şöyle der:
Bunun anlamı a
bir int
değerin göstergesidir .
Bu teknik olarak doğrudur, çoğu insan onu bu şekilde görmeyi / okumayı sever ve modern C standartlarının bunu nasıl tanımlayacağını (C dilinin kendisinin tüm ANSI ve ISO standartlarından önce geldiğini unutmayın). Ama ona bakmanın tek yolu bu değil. Bu satırı aşağıdaki gibi de okuyabilirsiniz:
Şerefine edilmemiş değeri a
türdür int
.
Aslında bu bildirideki yıldız işareti, yerleşimini de açıklayan bir dereference operatörü olarak görülebilir. Ve bu a
bir işaretçi gerçekten hiç beyan edilmemiştir, gerçekte dereference yapabileceğiniz tek şeyin bir işaretçi olduğu gerçeğiyle örtülüdür.
C standardı, *
operatör için sadece iki anlam tanımlar :
Ve dolaylı anlatım sadece tek bir anlamdır, bir işaretçiyi bildirmek için fazladan bir anlam yoktur, sadece dolaylı anlatım vardır, dereference işleminin yaptığı şey dolaylı bir erişim gerçekleştirir, dolayısıyla bunun gibi bir ifade içinde int *a;
dolaylı bir erişimdir ( *
araçlar dolaylı erişim) ve dolayısıyla yukarıdaki ikinci ifade standarda birincisinden çok daha yakındır.
int a, *b, (*c)();
"şu nesneleri şu şekilde bildir int
: nesne a
, işaret b
ettiği nesne ve işlev tarafından döndürülen nesne ile işaret etti " gibi bir şey okudum c
.
*
İçinde int *a;
bir operatör değildir ve bunun dereferencing değil a
(hatta henüz tanımlanmamış olan)
*
(o değil, toplam ifadesi sadece anlam veren *
ifadesi içinde!). Diyor ki "int a;" hiçbir zaman başka türlü iddia edilmediğini, ancak bir anlamı olmadan *
, * değerinin okunması a
bir int değerinin okunması hala tamamen geçerlidir, çünkü aynı olgusal anlamı vardır. Hiçbir şey, 6.7.6.1'de yazılmış hiçbir şey bu ifadeyle çelişmeyecektir.
int *a;
ifadedir, bir ifade değil. a
tarafından kayıttan çıkarılmadı int *a;
. a
henüz *
işlendiği noktada mevcut değildir . Sizce int *a = NULL;
bir hata çünkü boş gösterici dereferences?
Burada bir kol çıkmak ve söylemek için gidiyorum bu soruya basit bir cevap var yıldız adının yanındaki dönmeli olan değişken bildirimleri ve parametre ve dönüş türleri için hem: int *myVariable;
. Nedenini anlamak için, C'deki diğer sembol türlerini nasıl bildirdiğinize bakın:
int my_function(int arg);
bir işlev için;
float my_array[3]
bir dizi için.
Bildirim olarak adlandırılan genel desen, bir sembolün türünün addan önceki bölüme ve adın etrafındaki bölümlere ayrılması ve adın etrafındaki bu bölümlerin, soldaki türün değeri:
int a_return_value = my_function(729);
float an_element = my_array[2];
ve: int copy_of_value = *myVariable;
.
C ++, başvuruları olan çalışmalarda bir anahtar atar, çünkü başvuruları kullandığınız noktadaki sözdizimi değer türleriyle aynıdır, bu nedenle C ++ 'nın C'ye farklı bir yaklaşım aldığını iddia edebilirsiniz. işaretçiler durumunda C'nin davranışı, bu nedenle referanslar gerçekten bu açıdan garip olanıdır.
Bu sadece bir tercih meselesi.
Kodu okuduğunuzda, değişkenler ve işaretçiler arasında ayrım yapmak ikinci durumda daha kolaydır, ancak ortak bir türün hem değişkenlerini hem de işaretleyicilerini tek bir satıra koyarsanız karışıklığa yol açabilir (ki bu genellikle proje yönergeleri tarafından caydırılır, çünkü okunabilirliği azaltır).
İşaretçileri tür adının yanında karşılık gelen işaretiyle bildirmeyi tercih ederim
int* pMyPointer;
Büyük bir guru bir keresinde "Derleyicinin yolunu okuyun, yapmanız gerekir" dedi.
http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835
Bu, const yerleştirme konusunda yapıldı, ancak aynı kural burada geçerlidir.
Derleyici bunu şöyle okur:
int (*a);
olarak değil:
(int*) a;
Yıldızı değişkenin yanına yerleştirme alışkanlığına sahip olursanız, bildirimlerinizin okunmasını kolaylaştıracaktır. Aynı zamanda gözenekleri de önler:
int* a[10];
-- Düzenle --
Ben o kadar çözümlenen oluyor derken ne demek istediğimi tam olarak açıklamak için int (*a)
araçlar o, *
daha sıkı bağlanan a
öyle daha hiç int
, çok şekilde ifade o 4 + 3 * 7
3
daha sıkı bağlanarak onu 7
yok daha hiç 4
yüksek öncelik nedeniyle *
.
Ascii sanatı için özür dilerken, AST'nin ayrıştırma özeti int *a
kabaca şöyle görünür:
Declaration
/ \
/ \
Declaration- Init-
Secifiers Declarator-
| List
| |
| ...
"int" |
Declarator
/ \
/ ...
Pointer \
| Identifier
| |
"*" |
"a"
Açıkça gösterildiği gibi, ortak ataları olduğu *
için daha sıkı bağlanırken a
, ortak bir ata bulmak Declarator
için ağacın her tarafına gitmeniz gerekir .Declaration
int
(int*) a
.
int
bu durumda. Adım 2. Herhangi bir tip dekorasyon da dahil olmak üzere bir bildirimi okuyun. *a
bu durumda. Adım 3 Sonraki karakteri okuyun. Virgül varsa, tüketin ve 2. adıma dönün. Noktalı virgül durursa. Başka bir şey sözdizimi hatası verirse. ...
int* a, b;
bir çift işaretçi yazabilir ve alabiliriz. Yaptığım nokta *
, değişkenin bağlanması ve bildirimin "temel türünü" oluşturmak için tür ile değil, değişkenle ayrıştırılmasıdır. Bu aynı zamanda, typedef int *iptr;
iptr a, b;
birkaç işaretçi oluşturmaya izin vermek için typedef'lerin kullanılmasının nedeninin bir parçası . Typedef kullanarak, olabilir bağlamak *
için int
.
Declaration-Specifier
ile "süslemeler " birleştirir Declarator
. Ancak aksi Bildirgesi belirtici değil "hareket" süslemeler yapar int a[10], b;
Bu ayrıştırır, tamamen saçma sonuçlar üretecektir int *a, b[10];
olarak int
*a
,
b[10]
;
. Bunu açıklamanın başka bir yolu yoktur.
int*a
sonunda derleyici tarafından okunuyor: " a
tür var int*
". Orijinal yorumumla kastettiğim buydu.
Çünkü aşağıdaki gibi beyanlarınız olduğunda daha mantıklı:
int *a, *b;
int* a, b;
yapılan b
bir işaretçi int
. Ama yapmadılar. Ve iyi bir sebeple. Önerilen sisteminiz altında b
, aşağıdaki beyandaki tür int* a[10], b;
nedir?
Bir satırda birden fazla işaretçi bildirmek için, int* a, * b;
daha sezgisel olarak bir tamsayıyı işaretçi olarak "a" olarak bildiren ve aynı şekilde "b" bildirirken stilleri karıştırmayan tercih ederim . Birisinin dediği gibi, zaten aynı ifadede iki farklı tip beyan etmem.
Tercih edenler int* x;
, kodlarını türün solda ve tanımlayıcının (adın) sağda olduğu kurgusal bir dünyaya zorlamaya çalışıyorlar.
"Kurgusal" diyorum çünkü:
C ve C ++ 'da, genel durumda, bildirilen tanımlayıcı tip bilgisi ile çevrelenir.
Bu çılgınca gelebilir, ama bunun gerçek olduğunu biliyorsun. İşte bazı örnekler:
int main(int argc, char *argv[])
" main
bir int
ve işaretçiler dizisini char
alıp döndüren bir işlevdir int
." Başka bir deyişle, tür bilgilerinin çoğu sağdadır. Bazı insanlar fonksiyon bildirimlerinin dikkate alınmadığını çünkü bir şekilde "özel" olduklarını düşünürler. Tamam, bir değişkeni deneyelim.
void (*fn)(int)
anlamına gelir fn
, bir işlevi alan int
ve hiçbir şey döndürmeyen bir işlevin işaretçisidir .
int a[10]
'a' değerini 10 int
saniyelik bir dizi olarak bildirir .
pixel bitmap[height][width]
.
Açıkçası, benim açımdan yapmak için sağda çok fazla tip bilgisi olan kiraz örneklerini aldım. Türün çoğunun - olmasa da - solda olduğu gibi birçok beyan var struct { int x; int y; } center
.
Bu beyan sözdizimi, K & R'nin kullanımı yansıtan beyanlara sahip olma arzusundan doğmuştur. Basit bildirimleri okumak sezgiseldir ve daha karmaşık olanları okumak, sağ-sol-sağ kuralını öğrenerek yönetilebilir (bazen spiral kuralı veya sadece sağ-sol kuralı olarak adlandırın).
C, birçok C programcısının bu stili benimsemesi ve basit bildirimler yazması kadar basittir int *p
.
C ++ 'da sözdizimi biraz daha karmaşık hale geldi (sınıflar, referanslar, şablonlar, numaralandırma sınıfları ile) ve bu karmaşıklığa bir tepki olarak, birçok bildirimde türü tanımlayıcıdan ayırmak için daha fazla çaba göreceksiniz. Başka bir deyişle, int* p
büyük bir C ++ kodu alanına göz atarsanız daha fazla stil bildirimi görebilirsiniz .
Her iki dil olarak, olabilir , her zaman sol tarafındaki tip değişken tarafından bildirimleri (1) asla aynı açıklamada birden çok değişkeni bildirerek, ve (2) yararlanarak typedef
, ironik, takma koymak s (veya takma bildirimleri, türlerin solundaki tanımlayıcılar). Örneğin:
typedef int array_of_10_ints[10];
array_of_10_ints a;
(*fn)
ilişkili işaretçiyi tutar fn
.
Zaten CP'de benzer bir soruya cevap verdim ve kimse bundan bahsetmediği için, burada da C'nin serbest biçimli bir dil olduğunu , ayrıştırıcıyı her tokenden ayırt edebilirken, hangi stili seçerseniz seçin. C'nin bu özelliği C gizleme yarışması adı verilen çok özel bir yarışmaya yol açar .
Bu konudaki argümanların çoğu açık özneldir ve "yıldız değişken adına bağlanır" hakkındaki argüman naiftir. İşte sadece fikir olmayan birkaç argüman:
Unutulmuş işaretçi türü niteleyicileri
Resmi olarak, "yıldız" ne tipe ne de değişken isme ait değildir, işaretçi adı verilen kendi gramer öğesinin bir parçasıdır . Resmi C sözdizimi (ISO 9899: 2018):
(6.7) Beyan:
Beyanname belirteçleri init-declarator-list opt;
Burada beyan-belirteçleri tipi (ve depolama) içerir ve init-Bildiricisi listesi işaretçi ve değişken adını içerir. Bu bildirim listesi sözdizimini daha da inceleyip inceleyemeyeceğimizi görüyoruz:
(6.7.6) bildirici :
işaretçi opt doğrudan bildirici
...
(6.7.6) işaretçi:
*
tip-niteleyici-listesi opt
*
tip-niteleyici-listesi opt işaretçisi
Bir bildirimin tüm bildirim olduğu durumda, bir doğrudan bildirici tanımlayıcıdır (değişken adı) ve bir işaretçi yıldızdır ve ardından işaretçinin kendisine ait isteğe bağlı bir tür niteleyici listesi gelir.
"Yıldız değişkene aittir" hakkındaki çeşitli stil argümanlarını tutarsız yapan şey, bu işaretçi türü niteleyicileri unutmuş olmalarıdır. int* const x
, int *const x
veya int*const x
?
Düşünmek int *const a, b;
, çeşitleri nelerdir a
ve b
? Artık "yıldız değişkene ait" diye belli değil. Bunun yerine, kişi const
ait olduğu yeri düşünmeye başlayacaktır .
Yıldızın işaretçi türü niteleyicisine ait olduğu, ancak bunun ötesinde olmadığı konusunda sağlam bir argüman yapabilirsiniz.
İşaretçinin tür niteleyici listesi int *a
stili kullanan kişiler için sorunlara neden olabilir . Bir işaretçi kullananlar typedef
(ki yapmamalıyız, çok kötü bir uygulama!) Ve "yıldız değişken isme aittir" diye düşünenler bu çok ince hatayı yazma eğilimindedir:
/*** bad code, don't do this ***/
typedef int *bad_idea_t;
...
void func (const bad_idea_t *foo);
Bu temiz bir şekilde derlenir. Şimdi kod const doğru yapılmış olduğunu düşünebilirsiniz. Öyle değil! Bu kod yanlışlıkla sahte bir sabit doğruluktur.
foo
Aslında türü int*const*
- en dış işaretçi verilere değil, salt okunur yapıldı. Yani bu fonksiyonun içinde yapabiliriz**foo = n;
ve arayandaki değişken değerini değiştirir.
Çünkü ifadede const bad_idea_t *foo
, *
buradaki değişken isme ait değildir! Sahte kod, bu parametre, bildirimi olarak okunacak olan const (bad_idea_t *) foo
ve olmayan şekilde (const bad_idea_t) *foo
. Yıldız, bu durumda gizli işaretçi türüne aittir - tür bir işaretçi ve sabit nitelikli bir işaretçi olarak yazılır *const
.
Ama sonra yukarıdaki örnekte sorunun kökü, işaretçileri stilin typedef
değil a'nın arkasına gizleme uygulamasıdır *
.
Tek bir satırda birden çok değişkenin bildirilmesine ilişkin
Tek bir satırda birden fazla değişkenin bildirilmesi yaygın olarak kötü uygulama olarak kabul edilmektedir 1) . CERT-C güzelce şöyle özetliyor:
DCL04-Cı. Beyan başına birden fazla değişken bildirme
Sadece İngilizce okuma, ardından sağduyu kabul eder bir deklarasyon olmalıdır bir beyanı.
Değişkenlerin işaretçi olup olmadığı önemli değildir. Her değişkeni tek bir satırda bildirmek, kodu neredeyse her durumda daha net hale getirir.
Dolayısıyla programcının kafasının karışmasına ilişkin tartışma int* a, b
kötü. Sorunun kökü, yerleşiminin değil, birden çok bildiricinin kullanılmasıdır *
. Tarzdan bağımsız olarak, bunun yerine şunu yazmalısınız:
int* a; // or int *a
int b;
Başka bir sağlam ama öznel argüman ise int* a
, türünün a
sorgulanmadanint*
ve yıldızın tür niteleyicisine ait olduğu olabilir.
Ama temelde benim sonucum, burada yayınlanan argümanların çoğunun sadece öznel ve naif olduğu. Her iki stil için gerçekten geçerli bir argüman yapamazsınız - bu gerçekten öznel kişisel tercih meselesidir.
1) CERT-C DCL04-C .
typedef int *bad_idea_t;
void func(const bad_idea_t bar);
, büyük peygamber Dan Saks'ın öğrettiği gibi " const
Anlamsal anlamı değiştirmeden her zaman mümkün olduğunca sağa yerleştirirseniz" bu tamamen durur. sorun olmak. Ayrıca const
bildirimlerinizin okunmasını daha tutarlı hale getirir . "Const kelimesinin sağındaki her şey const olan, soldaki her şey onun türüdür." Bu, bir beyandaki tüm güvenceler için geçerli olacaktır. Deneyinint const * * const x;
Düşünmek
int *x = new int();
Bu,
int *x;
*x = new int();
Aslında şu anlama gelir
int *x;
x = new int();
Bu, int *x
gösterimi bir şekilde tutarsız hale getirir .
new
bir C ++ operatörüdür. Sorusu "C ++" değil, "C" olarak etiketlendi.
typedefs
, ancak bu gereksiz karmaşıklığı artıracaktır, IMHO.