2018q3 Homework1

contributed by < eating861227 >

你的提問呢?回顧你過去開發的程式,難道沒想到概念上有衝突或者激發出更多想法嗎?

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
jserv

本週學習目標

指標篇

函數指標

宣告方式
回值型態 (*指標名稱)(傳遞參數);

  • 函式名稱本身就是指向該空間位址的參考名稱,當呼叫函式名稱時,程式就會去執行該函式名稱所指向的記憶體空間中之指令。

於是練習了一下:

#include <stdio.h>

void add() { printf("hello\n"); }

int main() {
    int (*op)() = 0;
    printf("op:%d\n", op);

    op = add;
    printf("&add:%d\n", &add);
    printf("add:%d\n", add);
    printf("op:%d\n", op);
    printf("&op:%d\n", &op);

    op();
    add();

    return 0 ;
}

執行結果如下:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

避免用圖片表示文字訊息,請善用 HackMD 支援的語法來顯示文字輸出

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
jserv

嘗試過後發現函數指標和其他類型的指標的概念是相同的,不過對於"add",我就不懂了,為什麼"add"和"&add"是一樣的?

先找出 C99/C11 規格書裡頭對 function pointer 相關的描述,詳閱後再來提問

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
jserv


  • 指標不能有完整的算數,所以只能加減,不能乘除
  • C 語言型態很重要!
    int  A;
    int* ptrA = &A;
    int* ptrB = ptrA
    ptrA++;        //合法
    ptrA = ptrA+1; //合法
    ptrB = ptrA*1; //GG 
    ptrB = ptrA + ((int)ptrA*1) //強制轉型
  • Incomplete Type 只能宣告不能當物件,所以可以宣告一個pointer type
    struct object x;  // GG
    struct object *x  // OK
  • 幫宣告的程式或解釋:$ sudo apt-get install cdecl

void* 探討

  • 任何函式若沒有特別標注返回型態,一律變成 int (伴隨著 0 作為返回值),但這導致無從驗證 function prototype 和實際使用的狀況
  • void* 指標本身記憶體大小是確定的,但指向的東西是不確定
 int main()
 {
     int* x = 0 ;
     void* y = 0 ;
     int a = *x ;
     int b = *y ; //不合法 要強制轉型
     int c = *(int*)y;//y才有明確空間,dereference後也有明確空間
     int e = *(double*)y;//牽涉到alignment
 }

Alignment (對齊)

在x86或者ARM處理器上,C語言存資料並非隨便從任一位置開始存,是需要對齊的 ! ( self-aliged ) 這樣的目的是希望可以快速查訪。所以如果存取空間是 2 bytes ,則需起始於可以被 2 整除的地址,其他以此類推。

基本類型的儲存空間

1 byte : char , unsigned char , signed char
2 bytes : (unsigned) short
4 bytes : (unsigned) int , (unsigned) long , float , 32位元的指標
8 bytes : double , long double , 64位元的指標

所以宣告順序是個學問囉~

如果我宣告以下這些變數

 char *p;
 char c;
 int x;

實際上內存的分布為:

 char *p;      /* 4 or 8 bytes */
 char c;       /* 1 byte */
 char pad[3];  /* 3 bytes */ 這是個無用的空間,又稱為slop
 int x;        /* 4 bytes */

在64位元中,如果改成這樣,就不會有 slop 的情形了

 char *p;     /* 8 bytes */
 long x;      /* 8 bytes */
 char c;      /* 1 byte

Struct 的對齊

Struct 的物件會依照最寬的成員對齊,編譯器這樣做是為了保證所有成員是 self-aligned

例如:

struct foo1 {
    char c;
    struct foo7 *p;
    short x;
};

實際上:

struct foo1 {
    char c;         /* 1 byte */
    char pad1[7];   /* 7 bytes */
    struct foo7 *p; /* 8 bytes */
    short x;        /* 2 bytes */
    char pad2[6];   /* 6 bytes */
};

要怎麼解決 ? 按大小重排:

struct foo2 {
    struct foo2 *p;
    short x;
    char c;
};

那麼就只需要做隨尾填充