Pertanyaan Bisakah ptrdiff_t mewakili semua pengurangan pointer ke elemen objek array yang sama?


Untuk pengurangan pointer i dan j ke elemen objek array yang sama catatan di [expr.add # 5] membaca:

[catatan: Jika nilainya i − j tidak dalam kisaran nilai-nilai jenis yang dapat diwakilkan std​::​ptrdiff_­t, perilaku tidak terdefinisi. -catatan akhir ]

Tetapi diberikan [support.types.layout # 2], yang menyatakan itu (tekanan ranjau):

  1. Tipe ptrdiff_­t adalah tipe integer bertanda tangan yang ditentukan oleh implementasi itu dapat memegang perbedaan dua subskrip dalam objek array, seperti yang dijelaskan dalam [expr.add].

Apakah mungkin hasil dari i-j tidak berada di kisaran nilai representable dari ptrdiff_t?

PS: Saya minta maaf jika pertanyaan saya disebabkan oleh pemahaman bahasa Inggris yang buruk.

EDIT: Terkait: Mengapa ukuran maksimum array "terlalu besar"?


32
2018-03-20 09:27


asal


Jawaban:


Apakah mungkin hasil dari i-j tidak berada di kisaran nilai representable dari ptrdiff_t?

Ya, tapi itu tidak mungkin.

Faktanya, [support.types.layout]/2 tidak banyak bicara kecuali aturan yang tepat tentang pengurangan pointer dan ptrdiff_t didefinisikan dalam [expr.add]. Jadi mari kita lihat bagian ini.

[expr.add]/5

Ketika dua pointer ke elemen objek array yang sama dikurangi, jenis hasilnya adalah jenis integral yang ditentukan implementasi-didefinisikan; jenis ini harus jenis yang sama yang didefinisikan sebagai std​::​ptrdiff_­t dalam <cstddef> tajuk.

Pertama-tama, perhatikan bahwa kasus di mana i dan j adalah indeks subscript dari array yang berbeda tidak dipertimbangkan. Ini memungkinkan untuk mengobati i-j sebagai P-Q akan berada di mana P adalah penunjuk ke elemen larik pada subskrip i dan Q adalah penunjuk ke elemen sama larik di subskrip j. Dalam akta, kurangi dua pointer ke elemen array yang berbeda perilaku tidak terdefinisi:

[expr.add]/5

Jika ekspresi P dan Q arahkan ke, masing-masing, elemen x[i] dan x[j] dari objek array yang sama x, ekspresi P - Q memiliki nilai i−j   ; jika tidak, perilaku tidak terdefinisi.

Sebagai kesimpulan, dengan notasi yang ditentukan sebelumnya, i-j dan P-Q didefinisikan memiliki nilai yang sama, dengan yang terakhir adalah tipe std::ptrdiff_t. Tetapi tidak ada yang dikatakan tentang kemungkinan bagi tipe ini untuk memiliki nilai seperti itu. Pertanyaan ini dapat dijawab dengan bantuan std::numeric_limits; terutama, seseorang dapat mendeteksi jika sebuah array some_array aku s terlalu besar untuk std::ptrdiff_t untuk menahan semua perbedaan indeks:

static_assert(std::numeric_limits<std::ptrdiff_t>::max() > sizeof(some_array)/sizeof(some_array[0]),
    "some_array is too big, subtracting its first and one-past-the-end element indexes "
    "or pointers would lead to undefined behavior as per [expr.add]/5."
);

Sekarang, pada target biasa, ini biasanya tidak terjadi sebagai sizeof(std::ptrdiff_t) == sizeof(void*); yang berarti array harus besar untuk ptrdiff_t meluap. Tetapi tidak ada jaminannya.


5
2018-03-20 09:30



Saya pikir itu adalah bug dari susunan kata.

Aturan di [expr.add] diwariskan dari aturan yang sama untuk pengurangan pointer dalam standar C. Dalam standar C, ptrdiff_t tidak diperlukan untuk menahan perbedaan dua subskrip dalam objek array.

Aturan di [support.types.layout] berasal Masalah Bahasa Inti 1122. Ini menambahkan definisi langsung untuk std::size_t dan std::ptrdiff_t, yang seharusnya memecahkan masalah definisi melingkar. Saya tidak melihat ada alasan (setidaknya tidak disebutkan dalam dokumen resmi) untuk dibuat std::ptrdiff_t pegang perbedaan dua subskrip dalam objek larik. Saya kira itu hanya menggunakan definisi yang tidak tepat untuk memecahkan masalah definisi melingkar.

Sebagai bukti lain, [diff.library] tidak menyebutkan perbedaan antara std::ptrdiff_t di C ++ dan ptrdiff_t di C. Karena di C ptrdiff_t tidak memiliki kendala seperti itu, di C ++ std::ptrdiff_t seharusnya tidak memiliki kendala seperti itu juga.


1
2018-03-23 15:45