Apa itu kompilasi waktu kompilasi dalam C?

9

Ketika saya meneliti keunggulan C lebih dari C ++, saya menemukan paragraf ini:

Cara standar dalam C untuk melakukan enkapsulasi adalah dengan meneruskan mendeklarasikan struct dan hanya mengizinkan akses ke datanya melalui fungsi. Metode ini juga membuat enkapsulasi waktu kompilasi. Kompilasi waktu enkapsulasi memungkinkan kami untuk mengubah struktur data anggota tanpa kompilasi ulang kode klien (kode lain menggunakan antarmuka kami). Cara standar melakukan enkapsulasi C ++ di sisi lain (menggunakan kelas) membutuhkan kompilasi ulang kode klien saat menambahkan atau menghapus variabel anggota pribadi.

Saya mengerti bagaimana cara maju menyatakan struct dan mengakses anggotanya melalui fungsi menyembunyikan detail implementasi struct. Yang tidak saya mengerti adalah baris ini secara khusus:

Kompilasi waktu enkapsulasi memungkinkan kami untuk mengubah struktur data anggota tanpa kompilasi ulang kode klien (kode lain menggunakan antarmuka kami).

Dalam skenario apa ini berlaku?

mbl
sumber
Pada dasarnya, structini adalah kotak hitam dengan internal yang tidak diketahui. Jika klien tidak mengetahui internal, ia tidak akan pernah bisa mengaksesnya secara langsung dan Anda dapat mengubahnya sesuka hati. Ini mirip dengan enkapsulasi dalam OOP. Internal bersifat pribadi dan Anda hanya mengubah objek menggunakan metode publik.
Sulthan
Ini tidak selalu benar. Jika Anda memutuskan untuk menambah / menghapus anggota sebuah struct, Anda mengubah ukurannya. Ini membutuhkan kompilasi ulang kode klien.
DarkAtom
2
@DarkAtom Tidak benar! Jika klien tidak tahu isinya ( struktur buram ), maka ia tidak tahu ukurannya, jadi mengubah ukurannya bukan masalah.
Adrian Mole
1
@DarkAtom: Mengizinkan akses ke struktur hanya melalui fungsi mencakup alokasi hanya melalui fungsi. Perpustakaan akan menyediakan fungsi untuk mengalokasikan struktur, dan klien tidak akan pernah tahu ukurannya. Mengubah ukuran tidak perlu mengkompilasi ulang klien.
Eric Postpischil
3
Perhatikan bahwa ini secara teknis bukan merupakan "keuntungan dari C lebih dari C ++" karena Anda dapat (dan sering kali) mengimplementasikan ide yang sama dalam C ++. Lihatlah idiom "pimpl" .
user4815162342

Jawaban:

4

Kemungkinan skenario dunia nyata di mana ini akan terjadi adalah ketika pustaka database, yang ditulis pada hari-hari ketika ruang hard disk sangat terbatas, menggunakan byte tunggal untuk menyimpan bidang 'tahun' tanggal (misalnya 11-NOV-1973 akan 73untuk tahun ini). Tetapi, ketika Tahun 2000 datang, ini tidak lagi memadai, dan tahun itu kemudian disimpan sebagai bilangan bulat pendek (16-bit). Header yang relevan (sangat disederhanakan) untuk pustaka ini bisa berupa ini:

// dbEntry.h
typedef struct _dbEntry dbEntry;

dbEntry* CreateDBE(int day, int month, int year, int otherData);
void DeleteDBE(dbEntry* entry);
int GetYear(dbEntry* entry);

Dan program 'klien' adalah:

#include <stdio.h>
#include "dbEntry.h"

int main()
{
    int dataBlob = 42;
    dbEntry* test = CreateDBE(17, 11, 2019, dataBlob);
    //...
    int year = GetYear(test);
    printf("Year = %d\n", year);
    //...
    DeleteDBE(test);
    return 0;
}

Implementasi 'asli':

#include <stdlib.h>
#include "dbEntry.h"

struct _dbEntry {
    unsigned char d;
    unsigned char m;
    unsigned char y;    // Fails at Y2K!
    int dummyData;
};

dbEntry* CreateDBE(int day, int month, int year, int otherData)
{
    dbEntry* local = malloc(sizeof(dbEntry));
    local->d = (unsigned char)(day);
    local->m = (unsigned char)(month);
    local->y = (unsigned char)(year % 100);
    local->dummyData = otherData;
    return local;
}

void DeleteDBE(dbEntry* entry)
{
    free(entry);
}

int GetYear(dbEntry* entry)
{
    return (int)(entry->y);
}

Kemudian, pada pendekatan Y2K, file implementasi ini akan diubah sebagai berikut (semua yang lainnya tidak tersentuh):

struct _dbEntry {
    unsigned char d;
    unsigned char m;
    unsigned short y;   // Can now differentiate 1969 from 2069
    int dummyData;
};

dbEntry* CreateDBE(int day, int month, int year, int otherData)
{
    dbEntry* local = malloc(sizeof(dbEntry));
    local->d = (unsigned char)(day);
    local->m = (unsigned char)(month);
    local->y = (unsigned short)(year);
    local->dummyData = otherData;
    return local;
}

Ketika klien perlu diperbarui untuk menggunakan versi baru (Y2K-aman), tidak ada perubahan kode yang diperlukan. Bahkan, Anda bahkan mungkin tidak perlu mengkompilasi ulang: cukup dengan menautkan kembali ke pustaka objek yang diperbarui (jika memang seperti itu) sudah cukup.

Adrian Mole
sumber
2

Catatan: Daftar berikut ini tidak lengkap. Suntingan dipersilakan!

Skenario yang berlaku meliputi:

  • Aplikasi multi-modul di mana Anda tidak ingin melakukan kompilasi karena alasan tertentu.
  • Struktur yang digunakan di perpustakaan di mana Anda tidak ingin memaksa pengguna perpustakaan untuk mengkompilasi ulang setiap kali Anda mengubah struktur (diterbitkan).
  • Struktur yang mengandung elemen berbeda pada platform yang berbeda tempat modul ini bekerja.

Struktur yang paling dikenal dari jenis ini adalah FILE. Anda cukup menelepon fopen()dan mendapatkan pointer jika berhasil. Pointer ini kemudian diserahkan ke fungsi lain yang berfungsi pada file. Tapi Anda tidak tahu - dan Anda tidak ingin tahu - detailnya, seperti elemen dan ukuran yang terkandung.

Sibuk
sumber