Sabit adrese işaretçi ve aynı adresin sabitlerini işaretlemek için işaretçi ile işlev çağrısı


14

Bir dizi veri girişi ve işaretçiler kullanarak başka bir veri dizisi çıktı bir işlev yazmak istiyorum.

Ben her ikisi de sonuç ne olduğunu merak ediyorum srcve dstaynı adres işaret çünkü derleyici const için optimize edebilirsiniz biliyorum. Tanımlanmamış bir davranış mı? (Hem C hem de C ++ ile etiketledim, çünkü cevabın aralarında farklı olup olmadığından emin değilim ve her ikisini de bilmek istiyorum.)

void f(const char *src, char *dst) {
    dst[2] = src[0];
    dst[1] = src[1];
    dst[0] = src[2];
}

int main() {
    char s[] = "123";
    f(s,s);
    printf("%s\n", s);
    return 0;
}

Yukarıdaki soruya ek olarak const, orijinal kodda silersem bu iyi tanımlanmış mı?

Yanıtlar:


17

Davranışın iyi tanımlanmış olduğu doğru olsa da - derleyicilerin demek istediğiniz anlamda "const için optimize edebileceği" doğru değildir .

Olduğunu, bir derleyici olduğu değil izin verilen bir parametre olduğunu sırf varsayalım const T* ptrtarafından, bellek işaret ptrbaşka işaretçi üzerinden değişmeyecektir. İşaretçilerin eşit olması bile gerekmez. Bu constbir yükümlülük değil, bir garantidir - sizin tarafınızdan (= işlev) bu işaretçi aracılığıyla değişiklik yapmama yükümlülüğüdür.

Bu garantiyi alabilmek için işaretçiyi restrictanahtar kelimeyle işaretlemeniz gerekir . Bu nedenle, bu iki işlevi derlerseniz:

int foo(const int* x, int* y) {
    int result = *x;
    (*y)++;
    return result + *x;
}

int bar(const int* x, int* restrict y) {
    int result = *x;
    (*y)++;
    return result + *x;
}

foo()fonksiyon iki kez gelen okumalı xederken, bar()sadece bir kez okumak gerekir:

foo:
        mov     eax, DWORD PTR [rdi]
        add     DWORD PTR [rsi], 1
        add     eax, DWORD PTR [rdi]  # second read
        ret
bar:
        mov     eax, DWORD PTR [rdi]
        add     DWORD PTR [rsi], 1
        add     eax, eax              # no second read
        ret

Bunu canlı olarak izleyin GodBolt.

restrictyalnızca C dilinde bir anahtar kelimedir (C99'dan beri); ne yazık ki, şimdiye kadar C ++ 'a dahil edilmemiştir (kötü nedenlerden dolayı C ++' a tanıtmak daha karmaşıktır). Ancak birçok derleyici bunu biraz destekliyor __restrict.

Alt satır: Derleyici derlerken "ezoterik" kullanım durumunuzu desteklemelidir f()ve bununla ilgili herhangi bir sorun yaşamazsınız.


İçin kullanım durumları ile ilgili bu gönderiye bakın restrict.


const“sizin tarafınızdan (= işlev) bu işaretçiden değişiklik yapmamanız gereken bir yükümlülük” değildir. C standardı, işlevin constbir döküm yoluyla kaldırılmasına ve ardından sonuç üzerinden nesneyi değiştirmesine izin verir . Esasen, constbir nesneyi yanlışlıkla değiştirmekten kaçınmaya yardımcı olmak için programcıya sadece tavsiye ve kolaylık sağlamaktır.
Eric Postpischil

@EricPostpischil: Bu bir zorunluluktur.
einpoklum

Çıkabileceğiniz bir yükümlülük bir yükümlülük değildir.
Eric Postpischil

2
@EricPostpischil: 1. Saçları buraya ayırıyorsun. 2. Bu doğru değil.
einpoklum

1
Bu nedenle memcpyve bağımsız değişkenlerle strcpybildirilmiş restrictolsa da memmove, yalnızca ikincisi bellek blokları arasında çakışmaya izin verir.
Barmar

5

Bu, constniteleyici ile ve niteleyici olmadan iyi tanımlanmıştır (C ++ 'da, C'de artık emin değilim) .

Aranacak ilk şey, katı takma adlandırma kuralı 1'dir . Eğer srcve dstaynı nesneye noktası:

  • C'de, uyumlu tipte olmalıdırlar ; char*ve char const*uyumlu değil.
  • C ++ 'da, benzer türlerde olmalıdırlar ; char*ve char const*benzer.

Niteleyiciyle ilgili olarak, işleviniz hangi noktaları etkili bir şekilde değiştirdiğinde , olarak nitelendirilmemesi gerektiğini constiddia dst == srcedebilirsiniz . Bu şekilde çalışmaz . İki vakanın dikkate alınması gerekir:srcsrcconstconst

  1. Bir nesne olduğu gibi tanımlandığında const, char const data[42];(doğrudan veya dolaylı olarak) değiştirilmesi, Tanımlanmamış Davranış'a yol açar.
  2. Bir constnesneye başvuru veya işaretçi olduğu gibi tanımlandığında, char const* pdata = data;alttaki nesne const2 olarak tanımlanmamışsa değiştirilebilir (bkz. 1). Yani aşağıdakiler iyi tanımlanmıştır:
int main()
{
    int result = 42;
    int const* presult = &result;
    *const_cast<int*>(presult) = 0;
    return *presult; // 0
}

1) Sıkı örtüşme kuralı nedir?
2) const_castgüvenli?


Belki OP ödevlerin yeniden sıralanması anlamına gelir?
Igor R.

char*ve char const*uyumlu değil. _Generic((char *) 0, const char *: 1, default: 0))sıfıra değerlendirir.
Eric Postpischil

“Bir constnesneye başvuru veya işaretçi tanımlandığında” ifadesi yanlış. Bir bir referans ya da gösterici zaman anlamına constulaşım kolaylığı türü tanımlanmıştır ki, (çeşitli yollarla) o değiştirilemez noktaya ayarlanır nesne anlamına gelmez. (İşaretçi bir constnesneyi gösteriyorsa, bu aslında nesnenin consttanım gereği olduğu anlamına gelir, bu nedenle nesneyi değiştirmeye çalışma davranışı tanımlanmamıştır.)
Eric Postpischil

@Eric, Ben sadece standart veya etiketli soru hakkında özelim language-lawyer. Kesinlik, değer verdiğim bir değerdir, ancak bunun daha karmaşık bir hale geldiğinin de farkındayım. Burada, basitlik ve anlaşılması kolay cümleler kullanmaya karar verdim, çünkü bunun OP'nin istediği olduğuna inanıyorum. Aksi takdirde lütfen cevap vermeyi düşünüyorsanız, onu ilk oylayanlardan biri olacağım. Her neyse, yorumunuz için teşekkür ederim.
YSC

3

Bu C'de iyi tanımlanmıştır. Katı takma kuralları, chartür için veya aynı türdeki iki işaretçi için geçerli değildir .

"Optimize etmek" ile ne demek istediğinizden emin değilim const. Derleyicim (GCC 8.3.0 x86-64) her iki durumda da aynı kodu üretir. restrictİşaretleyiciyi belirteçlere eklerseniz, oluşturulan kod biraz daha iyidir, ancak durumunuz için çalışmaz, işaretçiler aynıdır.

(C11 §6.5 7)

Bir nesnenin depolanmış değerine yalnızca aşağıdaki türlerden birine sahip bir değer değeri ifadesiyle erişilir:
- nesnenin
etkili türüyle uyumlu bir tür,
- nesnenin etkili türüyle uyumlu bir türün nitelikli bir sürümü, - nesnenin etkili türüne karşılık gelen
imzalı veya imzasız tür olan bir tür,
- nesnenin etkili türünün nitelikli bir sürümüne karşılık gelen imzalı veya imzasız tür olan bir tür - bir içeren bir toplama veya birleşim türü üyeleri arasında yukarıda belirtilen türlerin (özyinelemeli olarak, bir alt küme veya içerdiği birliğin bir üyesi dahil) veya
- bir karakter türü.

Bu durumda (olmadan restrict ), her zaman 121sonuç alırsınız .

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.