---
tags: 研讀筆記, polymorphism, C
---
# 研讀筆記: Achieving polymorphism in C
[source](https://www.codeproject.com/Articles/739687/Achieving-polymorphism-in-C)
> 全文主要是翻譯原文,並記錄自己的閱讀。
### Introduction
在這篇文章中我們的目的是建構一個指向 Shape 物件的指標陣列,每一個 Shape 指向物件 Circle、Square、Goat 等不同物件,皆以純 C 實現。
### Airticle
先建立三個結構。
```cpp=
struct Square{
int width;
int height;
};
struct Circle{
float radius;
};
struct Triangle{
int base;
int height;
};
```
印出來看看。
```cpp=
printf("size of square is %d\n", sizeof(struct Square));
printf("size of Circle is %d\n", sizeof(struct Circle));
printf("size of Triangle is %d\n", sizeof(struct Triangle));
```
結果如下。
```cpp=
//size of cube is 8
//size of circle is 4
//size of triangle is 8
```
可以注意到這邊的結構中的成員在記憶體中很巧妙的對齊。

以下的輸出結果是 1,印出了 Square 前面 4 個 bytes 的值。
```cpp=
struct Square square;
square.width = 1;
square.height = 2;
printf("the first 4 bytes of square are %d", (*(int*)(&square)))
```
以下這個也是印出 1。
```cpp=
printf("the first 4 bytes of square are %d\n", square); //this works?
```
operand `&` 會給我們 square 這個結構一開始的記憶體位置,這個也能印出來,但是我們要把她轉成 int 的地址。
```cpp=
printf("the first 4 bytes of square are %d\n", &square);
```
這樣我們就可以印出來 int 用我們轉乘的 int 指標。
```cpp=
printf("the first 4 bytes of square are %d\n", (int*)&square);
```
這個也就是我們第一個範例做的事情,但是在我們印出來之前,可以選擇要移動指標。
```cpp=
printf("the second 4 bytes of square are %d\n", (*(int*)&square + 1));
```
接下來是 function pointers,C 對待 function pointer 就跟其他 member 一樣。底下是一個函式不用吃任何參數也不用回傳值。
```cpp=
void print_square( void ){
printf("Hello Square\n");
}
```
function pointer 宣告在結構當中。
```cpp=
struct Square{
int width;
int height;
//now for functions
void (* print)( void );
float (* area)( struct Square * this );
};
```
就像剛剛提到的,我們要建立 Square 需要手動去設定每個成員對應的值,會需要 constructor。
```cpp=
void init_square( struct Square * square, int w, int h ){
(*square).print = print_square;
(*square).width = w;
(*square).height = h;
(*square).area = calc_square_area;
}
```
所有結構都要宣告。
```cpp=
struct Square square;
struct Circle circle;
struct Triangle triangle;
init_square( &square, 2, 2 );
init_circle( &circle, 7.7 );
init_triangle( &triangle, 2, 3 );
square.print();
circle.print();
triangle.print();
printf("the area of the square is %f\n", square.area(&square));
printf("the area of the circle is %f\n", circle.area(&circle));
printf("the area of the triangle is %f\n", triangle.area(&triangle));
```
接下來,讓我們來建立結構 Shape,讓三個物件繼承他。
```cpp=
//abstract father class
struct Shape{
void (* print)( void );
float (* area)( struct Shape * this );
};
```
如果幫剛剛的 Square 改成用 Shape 呼叫 print 函式,會發生甚麼事呢?
```cpp=
struct Shape * pointer = (struct Shape *)□
(*pointer).print(); //?? what is going to happen??
```
會得到 segmentation fault。那我們再回來討論這些結構的記憶體。

在 Shape 當中的 print 函式是在最開頭的 4 bytes,但是在 Square 當中是第三個 4 bytes。
當我們把一個指向 Square 指標轉成指向 Shape 的指標,記憶體是不變的(untouched),記住,轉型通常是不會改變內部記憶體的任何東西,除非是依些 dynamic casting。
現在讓我們來解決這個問題,填滿前面 8 bytes。
```cpp=
struct Shape{
char padd[8];
void (* print)( void );
float (* area)( struct Shape * this );
};
```
最後。
```cpp=
struct Shape * shapes[3];
shapes[0] = (struct Shape *)□
shapes[1] = (struct Shape *)&circle;
shapes[2] = (struct Shape *)▵
int i;
for(i=0; i<3; ++i){
(*shapes[i]).print();
printf("%f\n\n", (*shapes[i]).area(shapes[i]));
}
```
這就是很基本的用 OOP 觀念實作多形(polymorphism)。