Mungkin ada banyak keuntungan dari kode tersebut, tetapi sayangnya Standar C tidak ditulis untuk memfasilitasinya. Kompiler secara historis menawarkan jaminan perilaku yang efektif di luar apa yang disyaratkan Standar yang memungkinkan untuk menulis kode seperti itu jauh lebih bersih daripada yang mungkin dalam Standar C, tetapi kompiler baru-baru ini mulai mencabut jaminan tersebut atas nama optimasi.
Yang paling penting, banyak kompiler C secara historis dijamin (dengan desain jika bukan dokumentasi) bahwa jika dua tipe struktur mengandung urutan awal yang sama, sebuah pointer ke kedua tipe dapat digunakan untuk mengakses anggota dari urutan umum tersebut, bahkan jika jenisnya tidak terkait, dan lebih lanjut bahwa untuk tujuan membangun urutan awal yang sama semua petunjuk ke struktur adalah setara. Kode yang memanfaatkan perilaku seperti itu bisa jauh lebih bersih dan lebih aman dari pada kode yang tidak, tetapi sayangnya meskipun Standar mensyaratkan bahwa struktur yang berbagi urutan awal yang sama harus ditata dengan cara yang sama, ia melarang kode untuk benar-benar menggunakan pointer dari satu jenis untuk mengakses urutan awal yang lain.
Akibatnya, jika Anda ingin menulis kode berorientasi objek dalam C, Anda harus memutuskan (dan harus membuat keputusan ini sejak awal) untuk melompati banyak rintangan untuk mematuhi aturan tipe pointer C dan bersiaplah untuk memiliki kompiler modern menghasilkan kode nonsensikal jika seseorang tergelincir, bahkan jika kompiler lama akan menghasilkan kode yang berfungsi sebagaimana dimaksud, atau mendokumentasikan persyaratan bahwa kode hanya akan dapat digunakan dengan kompiler yang dikonfigurasi untuk mendukung perilaku pointer gaya lama (misalnya menggunakan sebuah "-fno-strict-aliasing") Beberapa orang menganggap "-fno-strict-aliasing" sebagai kejahatan, tetapi saya menyarankan agar lebih membantu jika menganggap "-fno-strict-aliasing" C sebagai bahasa yang menawarkan kekuatan semantik yang lebih besar untuk beberapa tujuan daripada "standar" C,tetapi dengan mengorbankan optimasi yang mungkin penting untuk beberapa tujuan lain.
Sebagai contoh, pada kompiler tradisional, kompiler historis akan menginterpretasikan kode berikut:
struct pair { int i1,i2; };
struct trio { int i1,i2,i3; };
void hey(struct pair *p, struct trio *t)
{
p->i1++;
t->i1^=1;
p->i1--;
t->i1^=1;
}
sebagai melakukan langkah-langkah berikut secara berurutan: meningkatkan anggota pertama *p
, melengkapi bit terendah dari anggota pertama *t
, lalu mengurangi anggota pertama *p
, dan melengkapi bit terendah dari anggota pertama *t
. Kompiler modern akan mengatur ulang urutan operasi dengan cara kode mana yang akan lebih efisien jika p
dan t
mengidentifikasi objek yang berbeda, tetapi akan mengubah perilaku jika tidak.
Contoh ini tentu saja sengaja dibuat, dan dalam kode praktek yang menggunakan pointer dari satu jenis untuk mengakses anggota yang merupakan bagian dari urutan awal umum dari jenis lain biasanya akan bekerja, tetapi sayangnya karena tidak ada cara untuk mengetahui kapan kode tersebut mungkin gagal sama sekali tidak mungkin menggunakannya dengan aman kecuali dengan menonaktifkan analisis aliasing berbasis tipe.
Contoh agak kurang dibuat-buat akan terjadi jika seseorang ingin menulis fungsi untuk melakukan sesuatu seperti bertukar dua pointer ke tipe sewenang-wenang. Dalam sebagian besar kompiler "1990-an C", itu dapat dicapai melalui:
void swap_pointers(void **p1, void **p2)
{
void *temp = *p1;
*p1 = *p2;
*p2 = temp;
}
Namun, dalam Standar C, seseorang harus menggunakan:
#include "string.h"
#include "stdlib.h"
void swap_pointers2(void **p1, void **p2)
{
void **temp = malloc(sizeof (void*));
memcpy(temp, p1, sizeof (void*));
memcpy(p1, p2, sizeof (void*));
memcpy(p2, temp, sizeof (void*));
free(temp);
}
Jika *p2
disimpan dalam penyimpanan yang dialokasikan, dan penunjuk sementara tidak disimpan dalam penyimpanan yang dialokasikan, jenis efektif *p2
penunjuk akan menjadi jenis penunjuk sementara, dan kode yang mencoba untuk digunakan *p2
sebagai jenis apa pun yang tidak cocok dengan penunjuk-sementara tipe akan memanggil Perilaku Tidak Terdefinisi. Sudah pasti sangat tidak mungkin bahwa kompiler akan memperhatikan hal seperti itu, tetapi karena filosofi kompiler modern mengharuskan pemrogram menghindari Perilaku Tidak Terdefinisi dengan cara apa pun, saya tidak dapat memikirkan cara aman lainnya untuk menulis kode di atas tanpa menggunakan penyimpanan yang dialokasikan .