İşaretçi karşılaştırması C'de nasıl çalışır? Aynı diziye işaret etmeyen işaretçileri karşılaştırmak uygun mudur?


33

K&R (C Programlama Dili 2. Baskı) bölüm 5'de aşağıdakileri okudum:

İlk olarak, işaretçiler belirli koşullar altında karşılaştırılabilir. Eğer pve qardından ilişkiler gibi nokta aynı dizinin üyelerine ==, !=, <, >=düzgün, vb işleri.

Bu da sadece aynı diziyi gösteren işaretçilerin karşılaştırılabileceği anlamına geliyor.

Ancak bu kodu denediğimde

    char t = 't';
    char *pt = &t;
    char x = 'x';
    char *px = &x;

    printf("%d\n", pt > px);

1 ekrana basılır.

Her şeyden önce, ben, çünkü bazı türü veya hata veya tanımsız olsun düşündüm ptve px(benim anlayış en azından) aynı dizide göstermiyorsa.

Ayrıca olduğu pt > px, her iki işaretçileri yığını saklanan değişkenler yönlendirdiği için, ve bellek adresi böylece yığın aşağı büyür tdaha büyüktür x? Hangisi pt > pxdoğrudur?

Malloc getirildiğinde daha çok kafam karışıyor. Bölüm 8.7'deki K&R'de de şunlar yazılı:

Bununla birlikte, geri dönen farklı bloklara işaretçilerin sbrkanlamlı bir şekilde karşılaştırılabileceği hala bir varsayımdır . Bu, yalnızca bir dizi içinde işaretçi karşılaştırmalarına izin veren standart tarafından garanti edilmez. Bu nedenle, bu sürümü mallocyalnızca genel işaretçi karşılaştırmasının anlamlı olduğu makineler arasında taşınabilir.

Öbek üzerinde hatalı yere işaret eden işaretçileri yığın değişkenlerine işaret eden işaretçilerle karşılaştırarak hiçbir sorun yaşamadım.

Örneğin, aşağıdaki kod 1yazdırılarak sorunsuz çalıştı :

    char t = 't';
    char *pt = &t;
    char *px = malloc(10);
    strcpy(px, pt);
    printf("%d\n", pt > px);

Derleyicimle yaptığım deneylerime dayanarak, herhangi bir işaretçinin, tek tek nerede işaret ettiklerine bakılmaksızın, diğer herhangi bir işaretçiyle karşılaştırılabileceğini düşünmeye yönlendirildim. Dahası, sanırım iki işaretçi arasındaki işaretçi aritmetiği iyi, tek tek nerede işaret ettikleri önemli değil, çünkü aritmetik sadece işaretçiler deposunun bellek adreslerini kullanıyor.

Yine de, K & R'de okuduğum şeyle kafam karıştı.

Sormamın nedeni profilim. aslında bir sınav sorusu yaptı. Aşağıdaki kodu verdi:

struct A {
    char *p0;
    char *p1;
};

int main(int argc, char **argv) {
    char a = 0;
    char *b = "W";
    char c[] = [ 'L', 'O', 'L', 0 ];

   struct A p[3];
    p[0].p0 = &a;
    p[1].p0 = b;
    p[2].p0 = c;

   for(int i = 0; i < 3; i++) {
        p[i].p1 = malloc(10);
        strcpy(p[i].p1, p[i].p0);
    }
}

Bunlar neyi değerlendirir?

  1. p[0].p0 < p[0].p1
  2. p[1].p0 < p[1].p1
  3. p[2].p0 < p[2].p1

Cevap 0, 1ve 0.

(Profesörüm, sınavda soruların Ubuntu Linux 16.04, 64 bit sürüm programlama ortamı ile ilgili sorumluluk reddi içeriyor)

(editörün notu: SO daha fazla etikete izin verdiyse, bu son bölüm , ve belki de garanti eder. Soru / sınıfın noktası taşınabilir C yerine özellikle düşük seviyeli işletim sistemi uygulama ayrıntıları olsaydı.)


17
Belki ne olduğunu karıştırıyorsun geçerli içinde Cne ile güvenli bölgesi C. Bununla birlikte, iki türün aynı türle karşılaştırılması her zaman yapılabilir (örneğin eşitlik kontrolü), işaretçi aritmetiği kullanılarak ve karşılaştırılır >ve <yalnızca belirli bir dizi (veya bellek bloğu) içinde kullanıldığında güvenlidir .
Adrian Mole

13
Bir kenara olarak, gereken değil K & R C öğreniyor. Başlangıç ​​olarak, dil o zamandan beri birçok değişiklik geçirdi. Ve dürüst olmak gerekirse, oradaki örnek kod, okunabilirlikten ziyade tezahürün değerlendirildiği bir zamandan beri vardı.
paxdiablo

5
Hayır, çalışacağı garanti edilmez. Bölünmüş bellek modellerine sahip makinelerde pratikte başarısız olabilir. Bkz . C'nin C ++ 'dan std :: less eşdeğeri var mı? Çoğu modern makinede, UB'ye rağmen işe yarayacak.
Peter Cordes

6
@Adam: Kapat, ama bu aslında UB (OP'nin kullandığı derleyici, GCC, tanımlamayı seçmez. Olabilir). Ancak UB "kesinlikle patlar" anlamına gelmez; UB için olası davranışlardan biri beklediğiniz şekilde çalışıyor !! UB'yi bu kadar kötü yapan şey budur; bir hata ayıklama derlemesinde çalışabilir ve optimizasyon etkinken başarısız olabilir veya tam tersi veya çevredeki koda bağlı olarak kesilebilir. Diğer işaretçileri karşılaştırmak yine de size bir cevap verecektir, ancak dil bu cevabın ne anlama geleceğini tanımlamaz (herhangi bir şey varsa). Hayır, kilitlenmeye izin verilir. Gerçekten UB.
Peter Cordes

3
@Adam: Ah evet, yorumumun ilk kısmı, seninkini yanlış okudum. Ancak diğer işaretçileri karşılaştırmanın size hala bir cevap vereceğini iddia ediyorsunuz . Bu doğru değil. Bu tam bir UB değil, belirtilmemiş bir sonuç olacaktır . UB çok daha kötüdür ve yürütme bu girdilerle söz konusu ifadeye ulaşırsa programınızın segfault veya SIGILL olabileceği anlamına gelir (bundan önce veya sonra gerçekleşen herhangi bir noktada). (Yalnızca x86-64 üzerinde, eğer derleme zamanında UB görünürse, ancak genel olarak herhangi bir şey olabilirse).
Peter Cordes

Yanıtlar:


33

Göre C11 standart , ilişkisel operatörler <, <=, >ve >=sadece aynı dizi veya yapı nesnenin elemanlarına işaretçileri kullanılabilmektedir. Bu bölüm 6.5.8p5'te açıklanmıştır:

İki işaretçi karşılaştırıldığında, sonuç, işaretlenen nesnelerin adres alanındaki göreli konumlara bağlıdır. Nesne türlerine iki işaretçi her ikisi de aynı nesneyi gösteriyorsa veya her ikisi de aynı dizi nesnesinin son öğesinden bir tanesini gösteriyorsa, eşittir. İşaret edilen nesneler aynı toplu nesnenin üyesiyse, daha sonra bildirilen yapı üyelerine işaretçiler, yapıda daha önce bildirilen üyelerle işaretçilerden daha büyük, ve daha büyük alt simge değerlerine sahip dizi öğelerine işaretçiler, aynı dizinin öğelerine işaretçilerden daha büyük karşılaştırır alt simge değerleri ile. Aynı birlik nesnesinin üyelerine yönelik tüm işaretçiler eşittir.

Bu gereksinimi karşılamayan herhangi bir karşılaştırma, tanımlanamayan davranışı gerektirdiğini , yani (diğer şeylerin yanı sıra) sonuçların tekrarlanabilir olmasına bağlı olamayacağınızı unutmayın.

Özel durumunuzda, hem iki yerel değişkenin adresleri arasında hem de yerel ve dinamik bir adresin adresi arasındaki karşılaştırma için işlem "işliyor" gibi göründü, ancak sonuç kodunuzda alakasız bir değişiklik yaparak değişebilir hatta aynı kodu farklı optimizasyon ayarlarıyla derlemek. Tanımsız davranış ile, kod sırf olabilir bir hata çökmesine veya üretmek anlamına gelmez olacak .

Örnek olarak, 8086 gerçek modda çalışan bir x86 işlemcinin, 20 bit adres oluşturmak için 16 bitlik segment ve 16 bitlik ofset kullanan segmentli bellek modeli vardır. Bu durumda, bir adres tam olarak bir tam sayıya dönüşmez.

Eşitlik operatörleri ==ve !=bununla birlikte bu kısıtlamaya sahip değildir. Uyumlu tiplere veya NULL işaretleyicilere herhangi iki işaretçi arasında kullanılabilirler. Dolayısıyla , örneklerinizde ==veya !=her ikisinde de kullanmak geçerli C kodu üretecektir.

Ancak, ile ==ve hatta !=bazı beklenmedik ama yine de iyi tanımlanmış sonuçlar elde edebilirsiniz. Bkz . İlgisiz işaretçilerin eşitlik karşılaştırması doğru olabilir mi? bununla ilgili daha fazla ayrıntı için.

Profesörünüz tarafından verilen sınav sorusu ile ilgili olarak, bir takım kusurlu varsayımlar yapar:

  • Bir adres ile bir tam sayı değeri arasında 1'e 1 yazışma olan düz bellek modeli vardır.
  • Dönüştürülen işaretçi değerlerinin bir tamsayı tipine sığması.
  • Uygulamanın, tanımlanmamış davranışın verdiği özgürlüğü kullanmadan karşılaştırma yaparken işaretçileri tamsayı olarak ele alması.
  • Bir yığının kullanılması ve yerel değişkenlerin orada depolanması.
  • Ayrılan hafızayı almak için bir yığın kullanılır.
  • Yığın (ve dolayısıyla yerel değişkenler) yığından (ve dolayısıyla ayrılan nesnelerden) daha yüksek bir adreste görünür.
  • Bu dize sabitleri, daha düşük bir adreste ve öbekte görünür.

Bu kodu bir mimaride ve / veya bu varsayımları karşılamayan bir derleyicide çalıştırırsanız, çok farklı sonuçlar elde edebilirsiniz.

Ayrıca, her iki örnek de aradıkları zaman tanımlanmamış davranış gösterirler strcpy, çünkü sağ işlenen (bazı durumlarda) boş bir sonlandırılmış dizgeyi değil, tek bir karakteri işaret eder, bu da işlevin verilen değişkenin sınırlarını aşmasıyla sonuçlanır.


3
@Shisui Buna rağmen, yine de sonuçlara güvenmemelisiniz. Derleyiciler, optimizasyon söz konusu olduğunda çok agresif olabilir ve tanımlanmamış davranışı bir fırsat olarak kullanacaktır. Farklı bir derleyici ve / veya farklı optimizasyon ayarları kullanmak farklı çıktılar üretebilir.
dbush

2
@Shisui: Genel olarak x86-64 gibi düz bellek modeline sahip makinelerde çalışır. Bu tür sistemler için bazı derleyiciler dokümanlarındaki davranışı tanımlayabilir. Ancak değilse, derleme zamanı görünür UB nedeniyle "çılgın" davranış meydana gelebilir. (Uygulamada kimsenin bunu istemediğini sanmıyorum, bu yüzden ana akım derleyicilerin aradığı ve "kırmaya çalışın" bir şey değil.)
Peter Cordes

1
Bir derleyici, bir yürütme yolunun sonuç ve yerel değişken (otomatik depolama, yani yığın) <arasında yol açacağını görürse, yürütme mallocyolunun asla alınmadığını varsayabilir ve tüm işlevi bir ud2talimata derleyebilir (yasadışı bir şekilde yükseltir) - çekirdeğin işleme SIGILL sağlayarak işleyeceği kurulum istisnası). GCC / clang bunu uygulamada, voidişlevsizliğin sonundan düşmek gibi diğer UB türleri için yapar . godbolt.org şimdi görünüyor, ama kopyala / yapıştırmayı deneyin int foo(){int x=2;}ve eksikliğini not edinret
Peter Cordes

4
@Shisui: TL: DR: x86-64 Linux üzerinde iyi çalışmasına rağmen taşınabilir C değil. Bununla birlikte, karşılaştırmanın sonuçları hakkında varsayımlar yapmak sadece çılgınca. Ana iş parçacığında değilseniz, iş parçanızdan mallocdaha fazla bellek almak için aynı mekanizmayı kullanan iş parçacığı yığınınız dinamik olarak ayrılır , bu nedenle yerel değişkenlerinizin (iş parçacığı yığını) mallocdinamik olarak tahsis edildiğini varsaymak için bir neden yoktur. depolama.
Peter Cordes

2
@PeterCordes: Gerekli olan, davranışın çeşitli yönlerini "isteğe bağlı olarak tanımlanmış" olarak tanımaktır, öyle ki uygulamalar onları boş zamanlarında tanımlayabilir ya da tanımlamayabilir, ancak bunu yapmazlarsa test edilebilir bir şekilde (örn. Önceden tanımlanmış makro) göstermelidir. Ayrıca, bir optimizasyonun etkilerinin "Tanımsız Davranış" olarak gözlemlenebileceği herhangi bir durumu karakterize etmek yerine, optimize edicilerin, belirli davranış yönlerini "gözlemlenebilir değil" olarak kabul edebileceklerini söylemek çok daha yararlı olacaktır. böyle yap. Örneğin, verilen int x,y;bir uygulama ...
Supercat

12

İşaretçileri aynı türdeki iki farklı diziyle karşılaştırarak birincil sorun, dizilerin kendilerinin belirli bir göreli konumlandırmaya yerleştirilmeleri gerekmemesidir - biri diğerinden önce ve sonra sonlanabilir.

Her şeyden önce, pt bir px aynı diziye (en azından benim anlayışımla) işaret etmediği için tanımsız veya bazı tür veya hata alacağımı düşündüm.

Hayır, sonuç, uygulamaya ve diğer öngörülemeyen faktörlere bağlıdır.

Ayrıca her iki işaretçi yığında depolanan değişkenleri işaret ettiğinden ve yığının büyüdüğünden pt> px olur, bu nedenle t'nin bellek adresi x? Bu yüzden pt> px doğrudur?

Mutlaka bir yığın yoktur . Var olduğunda, büyümeye gerek yoktur. Büyüyebilir. Tuhaf bir şekilde bitişik olmayabilir.

Dahası, sanırım iki işaretçi arasındaki işaretçi aritmetiği iyi, tek tek nerede işaret ettikleri önemli değil, çünkü aritmetik sadece işaretçiler deposunun bellek adreslerini kullanıyor.

En atalım C şartname ilişkisel operatörler (kullandığınız yani karşılaştırma operatörleri) anlatılır sayfa 85, §6.5.8. Bunun doğrudan !=veya ==karşılaştırma için geçerli olmadığını unutmayın .

İki işaretçi karşılaştırıldığında, sonuç, işaretlenen nesnelerin adres alanındaki göreli konumlara bağlıdır. ... İşaret edilen nesneler aynı toplu nesnenin üyesiyse, ... daha büyük alt simge değerlerine sahip dizi öğelerine işaretçiler, alt öğelerin daha düşük alt simge değerlerine sahip aynı dizinin öğelerine işaretçilerden daha büyük karşılaştırır.

Diğer tüm durumlarda, davranış tanımsızdır.

Son cümle önemlidir. Yer kazanmak için ilgisiz bazı vakaları keserken, bizim için önemli olan bir durum var: aynı yapı / toplu nesne 1'in parçası olmayan iki dizi ve işaretçileri bu iki diziyle karşılaştırıyoruz. Bu tanımsız bir davranıştır .

Derleyiciniz, işaretçileri sayısal olarak karşılaştıran bir çeşit CMP (karşılaştırma) makinesi talimatı eklediğinde ve burada şanslıysanız, UB oldukça tehlikeli bir canavar. Kelimenin tam anlamıyla her şey olabilir - derleyiciniz görünür yan etkiler dahil tüm işlevi optimize edebilir. Burun şeytanlarını doğurabilir.

1 Aynı yapının bir parçası olan iki farklı diziye işaretçiler karşılaştırılabilir, çünkü bu, iki dizinin aynı toplu nesnenin (yapı) bir parçası olduğu madde kapsamındadır.


1
İle Daha da önemlisi, tve xaynı işlevi tanımlanmakta, bir derleyici hedefleme x86-64 bu işlev için yığın çerçevede yerli ortaya koymak nasıl hakkında bir şey varsaymak sıfır neden yok. Aşağı doğru büyüyen yığının, bir işlevdeki değişkenlerin bildirim sırası ile ilgisi yoktur. Ayrı işlevlerde bile, biri diğerinin içine girebiliyorsa, "çocuk" işlevinin yerlileri yine de ebeveynlerle karışabilir.
Peter Cordes

1
derleyiciniz görünür yan etkiler de dahil olmak üzere tüm işlevi optimize edebilir. Bir abartı değil: diğer UB türleri için (bir voidişlevsizliğin sonundan düşmek gibi ) g ++ ve clang ++ bunu gerçekten pratikte yaparlar : godbolt.org/z/g5vesB onlar yürütme yolunun UB'ye yol açtığı için alınmadığını varsayalım ve bu tür temel blokları yasadışı bir talimatla derleyin. Ya da hiç bir talimat olmadan, bu fonksiyon çağrıldıysa sessizce bir sonraki sıraya geçme. (Bazı nedenlerden dolayı gccbunu yapmaz g++).
Peter Cordes

6

Sonra ne sordu

p[0].p0 < p[0].p1
p[1].p0 < p[1].p1
p[2].p0 < p[2].p1

Değerlendirin. Cevap 0, 1 ve 0'dır.

Bu sorular aşağıdakileri azaltır:

  1. Öbek yığının üstünde mi altında mı?
  2. Öbek, programın dize hazır bilgi bölümünün üstünde veya altındadır.
  3. [1] ile aynı.

Ve her üçünün de cevabı "uygulama tanımlı" dır. Prof'inizin soruları sahte; geleneksel unix düzenine dayandırdılar:

<empty>
text
rodata
rwdata
bss
< empty, used for heap >
...
stack
kernel

ancak birkaç modern birlik (ve alternatif sistemler) bu geleneklere uymaz. Soruyu "1992'den itibaren" önermedikçe; eval hakkında -1 verdiğinizden emin olun.


3
Uygulama tanımlanmadı, tanımlanmadı! Bu şekilde düşünün, birincisi uygulamalar arasında değişiklik gösterebilir, ancak uygulamalar davranışların nasıl kararlaştırıldığını belgelemelidir. İkincisi, davranışın herhangi bir şekilde değişebileceği ve uygulamanın size çömelme söylemek zorunda olmadığı anlamına gelir :-)
paxdiablo

1
@paxdiablo: Standardın yazarları tarafından Gerekçe'ye göre, "Tanımsız davranış ... aynı zamanda, uygun dil uzantısının genişletilmesi gereken alanları da tanımlar: uygulayıcı, resmi olarak tanımlanmamış davranışın bir tanımını sağlayarak dili artırabilir." Gerekçe ayrıca "Programcıya, taşınabilir olmamaları için mükemmel derecede faydalı olan C programlarını, dolayısıyla zarfı kesinlikle mükemmel bir şekilde göz ardı etmeyi göze çarpmayan, aynı zamanda oldukça taşınabilir olan güçlü C programları oluşturma şansı vermek." Ticari derleyici yazarları bunu anlar, ancak diğer derleyici yazarları anlamaz.
supercat

Başka bir tanımlanmış uygulama yönü daha vardır; işaretçi karşılaştırması imzalanır , bu nedenle makineye / os / derleyiciye bağlı olarak bazı adresler negatif olarak yorumlanabilir. Örneğin, yığını 0xc << 28'e yerleştiren 32 bitlik bir makine, otomatik değişkenleri yığın veya rodatadan daha küçük bir adreste gösterebilir.
mevets

1
@mevets: Standart, karşılaştırmalarda işaretçilerin imzalanmasının gözlemlenebileceği herhangi bir durum belirtiyor mu? Bir 16 bit platform 32768 bayttan daha büyük nesnelere izin veriyorsa ve arr[]böyle bir nesne ise, Standardın, imzalı bir işaretçi karşılaştırması aksini rapor ederse bile arr+32768daha büyük karşılaştırma yapmasını zorunlu arrkılacaktır.
supercat

Bilmiyorum; C standardı, ötenazi için dua eden Dante'nin dokuzuncu dairesinde yörüngede. OP özellikle K&R ve bir sınav sorusuna atıfta bulundu. #UB, tembel bir çalışma grubundan gelen enkazdır.
mevets

1

Hemen hemen tüm modern platformlarda, işaretçiler ve tamsayıların izomorfik bir sıralama ilişkisi vardır ve ayrık nesnelere işaretçiler serpiştirilmez. Çoğu derleyici, optimizasyonlar devre dışı bırakıldığında bu siparişi programcılara maruz bırakır, ancak Standart, böyle bir sıralamaya sahip platformlar ile herhangi bir uygulamanın programcıya böyle bir sıralamayı açacak platformlarda bile göstermesini gerektirmeyen ve gerektirmeyenler arasında bir ayrım yapmaz. Tanımla. Sonuç olarak, bazı derleyici yazarları, kodun asla farklı nesnelerle işaretçilerdeki ilişkisel işleçleri karşılaştırmayacağı varsayımına dayanarak çeşitli türde optimizasyonlar ve "optimizasyonlar" gerçekleştirir.

Yayınlanan Gerekçe göre, Standard yazarları uygulamaları onlar Standard "Tanımsız Davranış" olarak karakterize durumlarda nasıl davranacağını belirterek dili kapsadığı düşünülmektedir (yani Standart hayır getirmesi halinde gereksinimleri yararlı ve pratik olacaktır bunu yaparken) , ancak bazı derleyici yazarları, programların hiçbir zaman Standartların gerektirdiğinin ötesinde herhangi bir şeyden faydalanmaya çalışmadığını, programların platformların destekleyebileceği davranışları yararlı bir şekilde kullanmasına izin vermekten ziyade hiçbir ek ücret ödemeyeceğini varsayar.

İşaretçi karşılaştırmalarıyla garip bir şey yapan herhangi bir ticari olarak tasarlanmış derleyicinin farkında değilim, ancak derleyiciler arka uçları için ticari olmayan LLVM'ye ilerledikçe, davranışları daha önce belirtilmiş olan saçma sapan kodu işleme olasılıkları giderek artmaktadır platformları için derleyiciler. Bu tür davranış ilişkisel operatörlerle sınırlı değildir, eşitliği / eşitsizliği bile etkileyebilir. Örneğin, Standart bir nesneye bir işaretçi ile hemen önceki bir nesneyle "sadece geçmiş" bir işaretçi arasındaki bir karşılaştırmanın eşitle karşılaştırmasını belirtmesine rağmen, programlar böyle yaparsa gns ve LLVM tabanlı derleyiciler saçma kod üretmeye eğilimlidir karşılaştırmaları.

Eşitlik karşılaştırmasının gcc ve clang'da saçma sapan davrandığı bir duruma örnek olarak şunları göz önünde bulundurun:

extern int x[],y[];
int test(int i)
{
    int *p = y+i;
    y[0] = 4;
    if (p == x+10)
        *p = 1;
    return y[0];
}

Hem clang hem de gcc xon öğe olsa bile, her zaman 4 döndürecek kod üretecektir , yhemen takip eder ve ikarşılaştırmanın doğru olması ve p[0]1 değeriyle yazılmasıyla sonuçlanır. Bence olan şey, optimizasyonun bir geçişinin yeniden yazmasıdır. işlevi sanki *p = 1;yerini aldı x[10] = 1;. Derleyici *(x+10)eşdeğer olarak yorumlanırsa , ikinci kod eşdeğerdir *(y+i), ancak maalesef bir aşağı yönde optimizasyon aşaması, bir erişimin x[10]yalnızca xen az 11 öğeye sahip olması durumunda tanımlanacağını ve bu erişimin etkilenmesini imkansız hale getireceğini kabul eder y.

Derleyiciler, Standard tarafından açıklanan işaretçi eşitlik senaryosu ile bu "yaratıcı" yı elde edebilirlerse, Standardın gereksinimleri getirmediği durumlarda daha da yaratıcı olmaktan kaçınmalarına güvenmem.


0

Çok basit: İşaretçilerin karşılaştırılması, nesnelerin bellek konumlarının, bildirdiğinizle aynı sırada olduğu asla garanti edilmediğinden, mantıklı değildir. İstisna dizilerdir. & dizi [0] & dizi [1] 'den daha düşük. Bu K&R'nin işaret ettiği nokta. Uygulamada, yapı üyesi adresleri de bunları deneyimlerime göre beyan ettiğiniz sıradadır. Hiçbir garanti .... Başka bir istisna bir işaretçi eşit için karşılaştırırsanız. Bir işaretçi diğerine eşit olduğunda, aynı nesneye işaret ettiğini bilirsiniz. Her ne ise. Bana sorarsan kötü sınav sorusu. Ubuntu Linux 16.04'e bağlı olarak, bir sınav sorusu için 64 bit sürüm programlama ortamı? Gerçekten mi ?


Teknik olarak, diziler değil gerçekten beyan olmadığı için bir istisna arr[0], arr[1]ayrı ayrı, vb. Sen beyan arrbireysel dizi öğelerinin sipariş bu soruya anlatılana farklı bir konudur bu nedenle bir bütün olarak.
paxdiablo

1
Yapı elemanlarının, memcpybir yapının bitişik bir bölümünü kopyalamak ve içindeki tüm elemanları etkilemek ve başka hiçbir şeyi etkilemek için kullanabileceğini garanti eden sırayla garanti edilir . Standart, yapılarla veya malloc()tahsis edilmiş depolama ile ne tür işaretçi aritmetiği yapılabileceğine dair terminoloji konusunda özensizdir . offsetofMakro bir eğer bir gibi bir yapı bayt ile işaretçi Aritmetik aynı tür yerine gereksiz olacaktır char[]ancak Standart açıkça bir yapının bayt (veya kullanılabilir) söylemez bir dizi nesnesi.
supercat

-4

Ne Kışkırtıcı Bir Soru!

Bu konudaki yanıtların ve yorumların el yazısı taraması bile , görünüşte basit ve doğrudan sorgunun ne kadar duygusal olduğunu ortaya çıkaracaktır .

Şaşırtıcı olmamalı.

Tartışmasız, yanlış anlamalar etrafında kavram ve kullanım ait işaretçiler bir baskın temsil nedenini ciddi başarısızlıkları genelde programlama içinde.

Bu gerçeğin tanınması, özellikle işaretçilerin tamamen ortaya koyduğu zorluklardan kaçınmak ve tercihen bunlardan kaçınmak için tasarlanmış dillerin çoğunda açıkça görülmektedir . C ++ ve C, Java ve ilişkilerinin diğer türevlerini, Python ve diğer senaryoları düşünün - sadece daha belirgin ve yaygın olanlar olarak ve sorunla başa çıkma şiddeti bakımından az çok sıralı.

Altında yatan ilkelerin daha derin bir anlayış geliştirmek, bu nedenle olmalıdır ilgili hiç o Aspire serisi her bireyin mükemmellik özellikle sistemler düzeyinde - programlama içinde .

Bunun öğretmeninizin göstermek istediği şey olduğunu hayal ediyorum.

Ve C'nin doğası onu bu keşif için uygun bir araç yapar. Yürütme ortamının daha derin soyutlanmasına dayanan dillerden - belki de daha kolay anlaşılabilir olsa da - derlemeden daha az açıktır.

Programlayıcının niyetinin makinelerin anlayabileceği talimatlara deterministik olarak çevrilmesini kolaylaştırmak için tasarlanan C, sistem düzeyinde bir dildir. Üst düzey olarak sınıflandırılırken, gerçekten bir 'orta' kategoriye girer; fakat böyle bir şey olmadığından, 'sistem' ataması yeterli olmak zorundadır.

Bu özellik, aygıt sürücüleri , işletim sistemi kodu ve katıştırılmış uygulamalar için tercih edilen bir dil haline getirilmesinden büyük ölçüde sorumludur . Ayrıca, optimum verimliliğin en önemli olduğu uygulamalarda haklı olarak tercih edilen bir alternatif ; burada bu, hayatta kalma ve yok olma arasındaki fark anlamına gelir ve bu nedenle bir lüksün aksine bir zorunluluktur . Bu gibi durumlarda, taşınabilirliğin cazip rahatlığı tüm cazibesini kaybeder ve en az ortak paydanın parlaklık eksikliği performansını seçmek düşünülemez derecede zararlı bir seçenek haline gelir .

Ne C yapar - ve bunun bazı türevleri - çok özel, tam o verir kullanıcılarının tam kontrolü - onlar ne arzu olduğunda - olmadan empoze ilgili sorumlulukları onlar değil zaman onların üzerine. Bununla birlikte, hiç olmamasından daha sunuyor izolasyonları ince gelen makinenin doğru kullanımı beklerken niçin talep titiz anlama kavramının işaretçiler .

Özünde, sorunuzun cevabı, şüphelerinizin doğrulanmasında, oldukça basit ve tatmin edici derecede tatlıdır. Bununla birlikte, bu ifadedeki her konsepte gerekli önemi vermek şartıyla :

  • İşaretçileri, inceleyerek karşılaştırma ve manipüle eylemleri her zaman ve her vardır mutlaka sonucu elde edilen sonuçlar alan değerler geçerliliğine bağlıdır iken, geçerli ve böylece gerek yok ol.

Eski hem kaçınılmaz güvenli ve potansiyel olarak uygun ikincisi kutu sadece hiç olmak ederken, uygun o edildiğinde kurulmuş olarak güvenli . Şaşırtıcı bir şekilde - bazıları için - ikincisinin geçerliliğini belirlemek öncekine bağlıdır ve talep eder.

Tabii ki, karışıklığın bir kısmı, bir işaretçi ilkesi içinde doğal olarak mevcut olan özyinelemenin etkisinden ve içeriği adresten ayırmada karşılaşılan zorluklardan kaynaklanır.

Oldukça doğru bir şekilde tahmin ettiniz ,

Herhangi bir işaretçinin, tek tek nerede işaret ettiklerine bakılmaksızın, diğer işaretçilerle karşılaştırılabileceğini düşünüyorum. Dahası, sanırım iki işaretçi arasındaki işaretçi aritmetiği iyi, tek tek nerede işaret ettikleri önemli değil, çünkü aritmetik sadece işaretçiler deposunun bellek adreslerini kullanıyor.

Ve bazı katkıda bulunanlar doğruladı: işaretçiler sadece rakamlardır. Bazen karmaşık sayılara daha yakın , ama yine de sayılardan daha fazla olmayan bir şey.

Bu çekişmenin burada alındığı eğlenceli acrimony, insan doğası hakkında programlamadan daha fazlasını ortaya koyuyor, ancak not ve ayrıntıya değer. Belki daha sonra yapacağız ...

Bir yorum ipucu vermeye başladığında; tüm bu karışıklık ve şaşkınlık , güvenli olandan geçerli olanı ayırt etme ihtiyacından kaynaklanır , ancak bu aşırı basitleştirmedir. Ayrıca neyin işlevsel ve neyin güvenilir olduğunu , neyin pratik olduğunu ve neyin uygun olabileceğini ve ayrıca daha da ayırt etmeliyiz : belirli bir durumda neyin uygun olduğunu daha genel anlamda neyin uygun olabileceğinden ayırmalıyız . Lafı olmaz; arasındaki fark uygun ve uygunluk .

Bu amaçla doğru, öncelikle gerek takdir kesin bir şey işaretçi olduğunu .

  • Konsept üzerinde sağlam bir kavrayış gösterdiniz ve diğerleri gibi bu illüstrasyonları da patlayıcı bir şekilde basit bulabilirler, ancak burada belirgin olan karışıklık seviyesi, açıklamada bu kadar basitlik gerektirir .

Birçoğunun işaret ettiği gibi: işaretçi terimi , sadece bir dizin olan şey için özel bir isimdir ve bu nedenle diğer herhangi bir sayıdan başka bir şey değildir .

Bu , tüm çağdaş anaakım bilgisayarların yalnızca sayılarla ve sayılarla çalışması gereken ikili makineler olduğu gerçeği göz önüne alındığında zaten açıkça görülmelidir . Kuantum bilgisayar olabilir bunu değiştirmek, ama bu pek olası olduğunu ve yaş gelmedi.

Teknik olarak, belirttiğiniz gibi, işaretçiler daha doğru adreslerdir ; doğal olarak, onları bir sokaktaki evlerin veya arsaların 'adresleri' ile ilişkilendirmenin ödüllendirici benzetmesini doğal olarak tanıtan açık bir içgörü.

  • Bir de düz bellek modeli: Tüm sistem belleği tek bir doğrusal sırayla düzenlenir: aynı yolda şehir yalan tüm evler ve her ev benzersiz, tek başına kendi numarası ile tanımlanır. Nefis basit.

  • In segmentli düzenleri: numaralı yolların bir hiyerarşik örgüt kompozit adresi gereklidir böylece numaralı evlerin yukarıda tanıtıldı.

    • Bazı uygulamalar hala daha kıvrımlıdır ve farklı 'yolların' bütünlüğünün bitişik bir sıralamayı toplamasına gerek yoktur , ancak bunların hiçbiri altta yatan şeyle ilgili hiçbir şeyi değiştirmez.
    • Mutlaka bu tür hiyerarşik bağlantıların hepsini düz bir organizasyona ayırabiliriz. Organizasyon ne kadar karmaşıksa, bunu yapmak için o kadar fazla çember atlamak zorunda kalacağız, ancak bu mümkün olmalıdır . Aslında bu, x86'daki 'gerçek mod' için de geçerlidir.
    • Aksi takdirde, konumlara bağlantıların eşleştirilmesi iki yönlü olmazdı , çünkü güvenilir yürütme - sistem düzeyinde - olması GEREKİR .
      • Birden çok adresi gerekir değil tekil hafıza yerlere haritasına ve
      • tekil adresler asla birden fazla bellek konumuna eşlenmemelidir.

Bizi , bilmeceyi böylesine büyüleyici derecede karmaşık bir arapsaçı haline getiren daha fazla büküme getirin . Yukarıda, işaretçileri önermek uygun olduğunu vardır basitlik ve açıklık amacıyla, adresleri. Tabii ki, bu doğru değil . Bir işaretçi olduğu olmayan bir adres; bir işaretçi olduğu bir referans bir adrese bu içerir bir adres . Zarf sporları gibi ev için bir referans. Bunu düşünmek, kavramda yer alan özyineleme önerisiyle ne anlama geldiğini anlamanıza neden olabilir. Yine; çok fazla sözümüz var ve adreslere yapılan referansların adresleri hakkında konuşuyoruzve böyle, yakında beynin çoğunu geçersiz bir op kodu istisnasında durdurur . Ve çoğunlukla, amaç bağlamdan kolayca elde edilir, bu yüzden sokağa dönelim.

Bu hayali kentimizdeki posta işçileri, 'gerçek' dünyada bulduklarımıza çok benziyorlar. Geçersiz bir adres hakkında konuştuğunuzda veya sorguladığınızda hiç kimsenin felç geçirmesi olası değildir , ancak bu bilgiler üzerinde hareket etmelerini istediğinizde sonuncusu duracaktır .

Tekil caddemizde sadece 20 ev olduğunu varsayalım. Ayrıca, yanlış yönlendirilmiş veya disleksik bir ruhun çok önemli bir mektubu 71 numaraya yönlendirdiğini varsayalım. Şimdi, taşıyıcı Frank'e böyle bir adres olup olmadığını sorabiliriz ve basitçe ve sakin bir şekilde rapor verir: hayır . Hatta onu o takdirde bu konum yalan olur ne kadar sokakta dışında tahmin etmek bekleyebilirsiniz yaptım kabaca 2.5 kat daha fazla ucundan daha: mevcuttur. Bunların hiçbiri ona öfkeye neden olmaz. Ancak, biz ona sormak olsaydı teslim bu mektubu, ya da pick up o yerden bir öğe onun hakkında oldukça dürüst olması muhtemeldir hoşnutsuzluk ve ret uymak.

İşaretçiler yalnızca adreslerdir ve adresler yalnızca sayılardır.

Aşağıdakilerin çıktısını doğrulayın:

void foo( void *p ) {
   printf(“%p\t%zu\t%d\n”, p, (size_t)p, p == (size_t)p);
}

Geçerli veya değil, istediğiniz kadar işaretçi olarak çağırın. Lütfen do sizin platformda başarısız veya eğer bulgularını yayınlamak (çağdaş) derleyici şikayet ediyor.

İşaretçileri çünkü Şimdi, olan sadece sayılar, bunları karşılaştırmak için kaçınılmaz geçerlidir. Bir anlamda öğretmeninizin gösterdiği şey tam olarak budur. Aşağıdaki ifadelerin tümü mükemmel bir şekilde geçerlidir - ve düzgün! - C ve derlendiğinde , hiçbir işaretçinin başlatılmasına gerek olmadığından ve içerdikleri değerler tanımlanmamış olsa bile, sorun yaşamadan çalışacaktır :

  • Biz sadece hesaplıyoruz result açıkça uğruna netlik ve baskı o kadar zorlamak aksi gereksiz, ölü kod ne olacağını hesaplamak için derleyici.
void foo( size_t *a, size_t *b ) {
   size_t result;
   result = (size_t)a;
   printf(“%zu\n”, result);
   result = a == b;
   printf(“%zu\n”, result);
   result = a < b;
   printf(“%zu\n”, result);
   result = a - b;
   printf(“%zu\n”, result);
}

Elbette , test noktasında a veya b tanımsız olduğunda (okuma: doğru şekilde başlatılmamışsa ) program yanlış biçimlendirilmiştir , ancak bu tartışmamızın bu kısmı ile tamamen alakasızdır . Bu parçacıklar, aşağıdaki ifadeler gibi, 'standart' ile - herhangi bir işaretçinin IN- geçersizliğine bakılmaksızın, kusursuz bir şekilde derlenmesi ve çalışması için garanti edilir .

Sorunlar yalnızca geçersiz bir işaretçi kaydı silindiğinde ortaya çıkar . Frank'ten geçersiz, var olmayan adresi almasını veya teslim etmesini istediğimizde.

Herhangi bir keyfi işaretçi verildiğinde:

int *p;

Bu ifadenin derlenmesi ve çalıştırılması gerekir:

printf(“%p”, p);

... olması gerektiği gibi:

size_t foo( int *p ) { return (size_t)p; }

... ikisini takip tezat, hala kolaylıkla derlemek, ancak başarısız yürütme sürece işaretçi olan geçerli - hangi biz burada sadece bu anlama mevcut uygulama erişim izni edildiği bir adresi başvuruyor :

printf(“%p”, *p);
size_t foo( int *p ) { return *p; }

Değişim ne kadar incedir? İşaretçi değeri arasındaki fark olarak fark yatıyor - olan adres ve içindekiler değeri: evi bu numaradan. İşaretçi kayıttan kaldırılana kadar hiçbir sorun ortaya çıkmaz ; bağlandığı adrese erişilmeye çalışılana kadar. Paketi yolun ötesine taşımaya veya almaya çalışırken ...

Ek olarak, aynı prensip mutlaka yukarıda belirtilen içeren daha karmaşık örnekler için de geçerlidir ihtiyacı için kurmak gerekli geçerliliğini:

int* validate( int *p, int *head, int *tail ) { 
    return p >= head && p <= tail ? p : NULL; 
}

İlişkisel karşılaştırma ve aritmetik, denkliği test etmek için aynı yarar sağlar ve prensipte eşdeğer olarak geçerlidir. Ancak , bu tür hesaplama sonuçları ne delalet ve hassas sorun, dahil alıntılarla tarafından ele - farklı tamamen meselesidir.

C'de bir dizi bitişik bir arabellek, kesintisiz doğrusal bellek dizileri dizisidir. Böyle bir tekil seri içindeki konumlara referans veren işaretçiler için uygulanan karşılaştırma ve aritmetik , doğal olarak ve hem birbirleriyle hem de bu 'dizi'ye (temel olarak tanımlanır) göre açıkça anlamlıdır. Aynısı malloc, veya yoluyla tahsis edilen her blok için de geçerlidir sbrk. Çünkü bu ilişkilerin örtük , derleyici aralarında geçerli ilişkiler kurmak yapabiliyor ve bu nedenle olabilir emin hesaplamalar beklenen yanıtları sunmasını.

Farklı bloklara veya dizilere gönderme yapan işaretçiler üzerinde benzer jimnastik yapmak , böyle doğal ve belirgin bir fayda sunmaz . Ne kadar fazla olursa olsun, bir anda hangi ilişki olursa olsun, bunun değişme olasılığı yüksek, tersine çevrilebilir bir yeniden tahsis ile geçersiz kılınabilir. Bu gibi durumlarda derleyici, önceki durumda sahip olduğu güveni tesis etmek için gerekli bilgileri elde edemez.

Sen , ancak, programcı olarak, olabilecek bu tür bilgiye sahip! Ve bazı durumlarda bundan faydalanmakla yükümlüdür.

Orada ARE hangi şartlarda, bu nedenle, ÇİFT BU tamamen GEÇERLİ ve mükemmel ÖZELLİK.

Aslında, yani tam olarak neyi mallocmimarileri büyük çoğunluğu üzerinde - kendisi zaman geri bloklarını birleştirme denemek için geldiğinde içten ilgisi var. Aynısı, işletim sistemi ayırıcısı için de geçerlidir sbrk; arkadaki gibi ; eğer daha açıkçası , sık , üzerinde daha fazla birbirinden farklı varlıklar, daha eleştirel - ve de bu platformlarda alakalı mallocolmayabilir. Ve bunların kaç edilir değil C ile yazılmış?

Bir projenin geçerliliği, güvenliği ve başarısı, kaçınılmaz olarak, üzerine inşa edildiği ve uygulandığı içgörü düzeyinin sonucudur.

Sunduğunuz alıntılarda, Kernighan ve Ritchie birbiriyle yakından ilgili, ancak yine de ayrı bir konuyu ele alıyor. Onlar edilir tanımlayan sınırlamaları içinde dili , ve en azından potansiyel olarak hatalı yapıları tespit ederek sizi korumak için derleyici yetenekleri yararlanmak nasıl açıklayan. Programlama görevinizde size yardımcı olmak için mekanizmanın kullanabileceği - tasarlandığı - uzunlukları açıklarlar. Derleyici kul olunuz, sen olan usta. Ancak bilge bir usta, çeşitli hizmetkarlarının yeteneklerine yakından aşina olan bir ustadır.

Bu bağlamda, tanımlanmamış davranış potansiyel tehlikeyi ve zarar olasılığını göstermeye hizmet eder; bildiğimiz gibi yakın, geri dönüşü olmayan bir kıyamet veya dünyanın sonu anlamına gelmemelidir. Bu sadece anlamına gelir biz 'derleyici anlam' - - bu şey ne olabilir hakkında herhangi bir kestirimde bulunmak veya temsil etmek mümkün değildir ve bu nedenle biz maddenin ellerimizi yıkamak için seçin. Bu tesisin kullanımından veya yanlış kullanımından kaynaklanabilecek herhangi bir talihsizlikten sorumlu tutulamayacağız .

Aslında şunu söylüyor: 'Bu noktanın ötesinde kovboy : tek başınasınız ...'

Profesörünüz size daha ince nüansları göstermeye çalışıyor .

Örneklerini hazırlarken ne kadar özen gösterdiklerine dikkat edin ; ve nasıl kırılgan o hala olduğunu. Adresini alarak aiçinde,

p[0].p0 = &a;

derleyici, değişkenin bir kayıt defterine yerleştirilmesinden ziyade, gerçek depolama alanının tahsis edilmesine zorlanır. Ancak bu otomatik bir değişkendir, programcının atanmış olduğu yer üzerinde herhangi bir kontrolü yoktur ve bu nedenle onu takip edecek şey hakkında geçerli bir tahmin yapamaz. Hangi neden olmalıdır beklendiği gibi çalışmaya kodu için sıfıra eşit ayarlanmalıdır.a

Sadece bu satırı değiştirmek:

char a = 0;

buna:

char a = 1;  // or ANY other value than 0

programın davranışının tanımlanmamasına neden olur . En azından, ilk cevap şimdi 1 olacak; ama sorun çok daha sinsi.

Şimdi kod felakete davet ediyor.

Hala mükemmel derecede geçerli ve hatta standarda uygun olsa da , şimdi yanlış biçimlendirilmiştir ve derlenmesine rağmen, çeşitli zeminlerde uygulamada başarısız olabilir. Şimdilik orada birden problemler - hiçbiri hangi derleyici olduğunu edebilmek için tanımak.

strcpyadresinde başlayacak ave bayttan sonra baytı tüketmek ve boş bir değerle karşılaşıncaya kadar aktarmak için bunun ötesine geçecektir .

p1İşaretçi tam bir blok başlatıldı 10 bayt.

  • Eğer abir blok sonunda yerleştirilecek olur ve süreç, hemen ertesi okuma ne şu erişimi yok - p0 [1] - Bir segfault ortaya çıkartacaktır. Bu senaryo x86 mimarisinde olası değildir , ancak mümkündür.

  • Adresi dışındaki alan ise a ise erişilebilir, hiçbir okuma hatası ortaya çıkar, ama yine de programın talihsizliğinden kaydedilmez.

  • Eğer sıfır bayt olur adresinde başlayan on içinde gerçekleşmesi a, bu olabilir o zaman için hala hayatta strcpydurur ve en azından bir yazma ihlali acı olmaz.

  • Eğer yanlış okumak için hatalı değilse , ancak bu 10 aralıkta sıfır bayt oluşmazsa, strcpydevam edecek ve tahsis edilen bloğun ötesine yazmaya çalışacaktır malloc.

    • Eğer bu alan sürece ait değilse, segfault derhal tetiklenmelidir.

    • Hala daha felaket - ve ince --- durum aşağıdaki blok ortaya çıkar edilir , daha sonra hata için işleme ait olamaz algılanabilir, sinyal kaldırdı ve bu yüzden olabilir hala 'iş' için 'görünür' , aslında edilecek ise üzerine yazarak (bazı işletim ortamlarında) diğer verileri, sizin allocator yönetim yapılarını, hatta kod.

Bu yüzden işaretçi ilgili hatalar yüzden olabilir sert etmek izlemek . Bu satırların, bir başkasının yazdığı ve sizi araştırmaya yönlendirilen, karmaşık olarak ilgili binlerce satırın derinliklerine gömüldüğünü düşünün.

Yine , program olmalı o kalır için hala derlemek tamamen geçerli ve standart uyumluluğunu C

Bu tür hatalar, hiçbir standart ve hiçbir derleyici istenmeyenlere karşı koruyamaz. Sanırım tam olarak size öğretmek istedikleri şey bu.

Paranoid insanlar sürekli etmeye değiştirmek doğayı bu sorunlu olasılıklar imha ve böylece bizi kendimizden kurtarmak için C; ama bu çok saçma . Bu, gücü takip etmeyi ve makinenin daha doğrudan ve kapsamlı kontrolünün bize sunduğu özgürlüğü elde ettiğimizde kabul etmek zorunda olduğumuz sorumluluktur . Performansta mükemmelliği geliştirenler ve takipçiler asla daha az bir şey kabul etmezler.

Taşınabilirlik ve genelliği , temsil ettiği bir temelde ayrı göz ve bir bütün olduğunu standart adresine arar:

Bu belge, programlama dilinde C ifade edilen programların formunu belirtir ve yorumlanmasını sağlar. Amacı , C dil programlarının çeşitli bilgisayar sistemlerinde taşınabilirliğini , güvenilirliğini, sürdürülebilirliğini ve verimli yürütülmesini teşvik etmektir .

Hangisi tutmak için mükemmel uygun olmasının nedeni ise farklı gelen tanım ve teknik şartname dilin kendisinin. Birçok aksine inanmak gibi genelliği olan zıt için istisnai ve de örnek .

Sonuç olarak:

  • İşaretçileri incelemek ve manipüle etmek her zaman geçerli ve genellikle verimlidir . Sonuçların yorumlanması anlamlı olabilir veya olmayabilir, ancak işaretçi kayıttan kaldırılana kadar felaket asla davet edilmez ; bağlantı verilen adrese erişilmeye çalışılana kadar .

Bu doğru olmasaydı, bildiğimiz gibi programlama - ve onu sevmek - mümkün olmazdı.


3
Bu cevap maalesef doğal olarak geçersiz. Tanımlanmamış davranışlar hakkında hiçbir şey yapamazsınız. Karşılaştırmanın makine düzeyinde yapılması gerekmez.
Antti Haapala

6
Ghii, aslında hayır. C11 Ek J ve 6.5.8'e bakarsanız, karşılaştırma eyleminin kendisi UB'dir. Silme işlemi ayrı bir konudur.
paxdiablo

6
Hayır, UB bir işaretçi kaydının kaldırılmasından önce bile zararlı olabilir. Bir derleyici, görünür davranışları açıkça değiştirmesine rağmen, UB ile bir işlevi tek bir NOP'a tamamen optimize etmekte serbesttir.
nanofarad

2
@Ghii, Ek J (bahsettiğim bit) tanımsız davranış olan şeylerin listesidir , bu yüzden bunun argümanınızı nasıl desteklediğinden emin değilim :-) 6.5.8 karşılaştırmayı açıkça UB olarak çağırır. Supercat'e yaptığınız yorum için, bir işaretçi yazdırdığınızda karşılaştırma yapılmaz, böylece muhtemelen çökmemesi için haklısınızdır . Ama OP'nin sorduğu şey bu değildi. 3.4.3aynı zamanda bakmanız gereken bir bölümdür: UB'yi "bu standardın hiçbir şart getirmediği" davranış olarak tanımlar .
paxdiablo

3
@GhiiVelte, size işaret edilmesine rağmen, açıkça yanlış olan şeyleri belirtmeye devam edersiniz. Evet, yayınladığınız snippet derlenmelidir, ancak sorunsuz bir şekilde çalıştırdığı konusundaki görüşünüz yanlıştır. Aslında standart (özellikle bu durumda) kelimesini okumanızı öneririm. C11 6.5.6/9"Kelimesi" bir gereksinimi belirtir. "İki işaretçi çıkarıldığında, her ikisi de aynı dizi nesnesinin öğelerini veya sonuncuyu geçecek şekilde msgstr "% s: dizi nesnesi öğesi".
paxdiablo

-5

İşaretçiler, bilgisayardaki her şey gibi tamsayıdır. Kesinlikle bunları ile karşılaştırabilir <ve >bir programın çökmesine neden olmadan sonuç üretebilirsiniz. Bununla birlikte, standart, bu sonuçların dizi karşılaştırmaları dışında herhangi bir anlamı olduğunu garanti etmez .

Yığın tahsisli değişkenler örneğinizde, derleyici bu değişkenleri kayıtlara veya yığın bellek adreslerine tahsis edebilir ve istediği sırayla seçebilir. Gibi Karşılaştırmalar <ve >bu nedenle derleyici veya mimarileri arasında tutarlı olmayacaktır. Ancak, bu kadar kısıtlı değildir ==ve !=işaretçi eşitliğini karşılaştırmak geçerli ve kullanışlı bir işlemdir.


2
Kelime yığını C11 standardında tam olarak sıfır kez görünür . Tanımsız davranış, her şeyin olabileceği anlamına gelir (program çökmesi dahil).
paxdiablo

1
@paxdiablo Öyle mi dedim?
nickelpro

2
Yığına ayrılan değişkenlerden bahsettiniz. Standartta yığın yok, bu sadece bir uygulama detayı. Bu cevapla ilgili en ciddi sorun, işaretçileri çökme şansı olmadan karşılaştırabileceğiniz tartışmadır - bu sadece yanlıştır.
paxdiablo

1
@ nickelpro: Eğer gcc ve clang'daki optimize edicilerle uyumlu bir kod yazmak istiyorsa, birçok aptal çemberden atlamak gerekir. Her iki iyileştirici de, Standardın onları haklı çıkarmak için bükülebileceği herhangi bir yol olduğunda (ve bazen de yokken) işaretçiler tarafından erişilecek şeyler hakkında çıkarımlar yapma fırsatlarını agresif bir şekilde arayacaktır. Verilen int x[10],y[10],*p;, kod değerlendirir y[0], daha sonra geçici olarak değiştirmeden değerlendirir p>(x+5)ve yazar ve sonunda tekrar değerlendirir , ...*ppy[0]
supercat

1
Nickelpro, katılmayı kabul etmeyi kabul et ama cevabın hala temelde yanlış. Ben kullanan kişilerin bu şekilde yaklaşımın benzetmek (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')yerine isalpha()çünkü neyi süreksiz bu karakterleri olurdu aklı başında uygulama? Sonuç olarak, bildiğiniz hiçbir uygulamanın bir sorunu olmasa bile , taşınabilirliğe değer veriyorsanız standardı olabildiğince kodlamanız gerekir. "Standartlar maven" etiketini takdir ediyorum, bunun için teşekkürler. Özgeçmişimi koyabilirim :-)
paxdiablo
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.