「C 語言與 C++ 語言的共同點是什麼?」這不只是面試題,更是理解 C++ 如何在 C 基礎上構建物件導向體系的關鍵。本文將從語法相容性、記憶體模型、編譯模型、核心機制對應四個維度,深度剖析兩者的共同基因與演進邏輯。
| 維度 | 共同點說明 | 關鍵差異/演進 |
|---|---|---|
| 基本語法與詞法 | 關鍵字、運算子、控制流、函式宣告/定義、前處理器指令幾乎完全相容 | C++ 新增關鍵字 (class, template, virtual...),更嚴格型別檢查 |
| 基礎資料型別 | int, float, double, char, bool (C99), 陣列, 指標, 列舉, 聯合體 |
C++ 引入參考型別 (&)、std::string, std::vector 等標準庫型別 |
| 記憶體模型 | 靜態/堆疊/堆積三區分佈,位元組定址,對齊規則,sizeof 語義一致 |
C++ 新增建構子/解構子自動管理生命週期,RAII 模式 |
| 編譯與連結模型 | 預處理 → 編譯 → 組譯 → 連結,目標檔格式 (ELF/COFF/Mach-O) 相同,符號表機制相同 | C++ 名稱修飾 實現函式多載、命名空間、模板實例化 |
| 呼叫慣例與 ABI | 同平台下 C/C++ 函式呼叫慣例相容,extern "C" 可互呼 |
C++ 成員函式隱含 this 指標傳遞 (通常經 ECX/RCX 或堆疊) |
| 標準庫核心子集 | <cstdio>, <cstdlib>, <cstring>, <cmath>, <climits> 等 C 標準庫在 C++ 完整保留 |
C++ 提供 <iostream>, <vector>, <algorithm> 等更高階抽象 |
| 前處理器/巨集 | 完全相同的預處理機制,條件編譯、巨集展開、檔案包含 | C++ 建議以 constexpr, 模板, 內聯函式 替代巨集 |
原始回答指出:「C++ 中的結構體和類別沒有本質區別,只不過結構體的預設權限為 public,類別預設為 private。」這是核心洞察。我們從機器碼層面驗證:
// C 風格
struct PointC { int x; int y; };
// C++ 風格
struct PointCpp { int x; int y; };
class PointClass { public: int x; int y; }; // 显式 public
// 编译后内存布局完全相同:连续 8 字节 (x:4, y:4),无隐藏成员
static_assert(sizeof(PointC) == sizeof(PointCpp) && sizeof(PointCpp) == sizeof(PointClass));
static_assert(offsetof(PointC, x) == offsetof(PointCpp, x) && offsetof(PointCpp, x) == offsetof(PointClass, x));
// C++ 类
struct Shape {
int x, y;
void move(int dx, int dy) { x += dx; y += dy; }
};
// 等价的 C 实现
struct Shape { int x, y; };
void Shape_move(Shape* this, int dx, int dy) { // 显式 this 指针
this->x += dx;
this->y += dy;
}
// 调用方式
Shape s;
s.move(1, 2); // C++ 语法糖
Shape_move(&s, 1, 2); // C 等价展开
關鍵點:C++ 非靜態成員函式編譯後就是第一個參數為 this 指標的普通函式。x86-64 System V ABI 下,this 經由 RDI 傳遞;Microsoft x64 ABI 經由 RCX。這正是原始回答提到的「在 x86 系統中使用 ecx 傳遞參數而非堆疊」。
// C++ 单继承
struct Base { int a; virtual void foo(); };
struct Derived : Base { int b; void foo() override; };
// C 等价实现(简化)
struct Base { int a; void (*foo)(struct Base*); }; // 函数指针表雏形
struct Derived { struct Base base; int b; }; // 组合 = 继承
// 虚函数表机制(单继承无虚基类时)
// Base 对象内存:[vptr][a]
// Derived 对象内存:[vptr][a][b] -- vptr 指向 Derived 的 vtable
// vtable: [&Base::foo, &Derived::foo, ...]
原始回答的代碼片段:
struct b { a xxx; ... } // b 从 a 继承 = 组合 a 作为首成员
正是這個原理。單繼承無虛基類時,派生類物件記憶體佈局 = 基類子物件 + 派生類自有成員,基類指標可直接指向派生類物件開頭(向上轉型零成本)。
public/protected/private、friend 皆為編譯器強制的靜態檢查。機器碼層面完全沒有權限概念—— 只要拿到位址,隨時可讀寫。這也是原始回答所言:「在機器碼那個層次沒有限制」。
C 的巨集 (#define) 是 C++ 模板的「文字替換」前身。C++ 模板在編譯期實例化生成具體型別程式碼,本質上是型別安全的巨集展開,且支援型別推導、特化、SFINAE、概念 等進階特性。
C 的 setjmp/longjmp 是非局部跳轉的原始形式。C++ 異常機制(try/catch/throw)在編譯器實現上通常基於零成本異常模型 (Itanium C++ ABI):正常路徑零開銷,拋出時透過堆疊展開表查找處理器,本質上是結構化的 longjmp。
C 無原生 RTTI。C++ 的 typeid、dynamic_cast 依賴虛函式表擴展的 type_info 結構,同樣是編譯器為類別自動生成的中繼資料。
extern "C" 導出純 C 介面,理解 ABI 兼容性至關重要。final、CRTP、樣板化消除虛呼叫。#pragma pack、alignas、[[no_unique_address]] 等屬性在 C/C++ 通用,關鍵於高效能資料結構、網路封包、硬體暫存器映射。this 指標、虛表指標、物件佈局,能快速定位物件生命週期、切面、繼承關係問題。C 與 C++ 的共同點遠超「語法相似」—— 它們共享相同的記憶體模型、編譯鏈結模型、呼叫慣例、ABI 基礎。C++ 可視為「在 C 的機器模型之上,增加了語義層面的抽象(類別、模板、異常、RAII),並由編譯器自動降級為 C 風格的機器碼」。深刻理解這一點,您就能在兩語言間自由穿梭,寫出既具 C 效能又具 C++ 表達力的現代系統級程式碼。