Try   HackMD

2018q3 Homework1

contributed by < yichung279 >

tags: sysprog2018

主題

  • bitwise operation
  • 你所知道的 C 語言 (C基礎複習)
  • 你所不知道的 C 語言:開發工具與規格標準
  • 你所不知道的 C 語言:指標篇
  • 你所不知道的 C 語言:函式呼叫篇

bitwise operation(及測驗第一題延伸)

  • 簡介 : 逐個bit 作邏輯運算
  • 應用 :
    bit & 0 : = 0
    bit | 1 : = 1
    bit ^ 1 : toggle
    跟相反的布林作運算時,bit皆不會改變
    配合0xff、0x80、shift、~ 可取特定位置的bit(s)或快速改bit
  • && / ||:
    邏輯運算,數值只有0與~0
  • 測驗延伸:
    1.可配合卡諾圖,將真值表轉換為任何等價邏輯,並不只有xor可以轉換。
    2.nand/nor可以組合出所有邏輯運算子

你所知道的c語言(c基礎複習)

ref: https://openhome.cc/Gossip/CGossip/

Ubuntu 16.04.5 LTS / GCC 5.5編譯器(我的環境)下的data type

  • 整數
    short: 2 bytes
    int : 4 bytes
    long : 8 bytes
  • 浮點數
    float : 4 bytes
    double : 8 bytes
    long double : 16 bytes
  • 字元
    char : 1 byte

這些規範不在規格書中,再 ieee754 及其他標準,這也保持了架構的靈活性。

指標

  • & : 取址
  • * : 傳值
  • 指標運算: 前進一個資料型態的記憶體長度
  • 雙重指標: 就是指標的指標

沒有「雙重指標」這說法,請依據 C 語言規格書,只有 the pointer to the pointer
:notes: jserv

#include <stdio.h>

int main(void) {
    int var = 10;
    int *ptr = &var ;
    // ptr為var的指標,型態為int,占4bytes
    // var位址0x7ffedd04e2d2
    // ptr == 0x7ffedd04e2d2
    ptr + 1; 
    // 0x7ffedd04e2d6
    ptr + 2;
    // 0x7ffedd042da
    int **pptr = &ptr;
    // pptr為ptr的指標,型態為int,占4bytes
    *pptr;
    // *ptr == ptr == 10的指標 == 0x7ffedd04e2d2
    **pptr;
    // **pptr == 10
    return 0;
}

善用 GDB,你可以直接傾印特定記憶體區間的內容,並用 macro 來客製化輸出
:notes: jserv

你不所知道的 c 語言:開發工具和規格標準

C 語言發展:

C是為了寫 Unix 而生的語言,而且可以自己編譯自己,先從從C 語言的子集合 C0 開始, C0 產生 C1 ,以此類推,一步步擴充規範。 C0 編譯器以組合語言開發。 見: bootstraping

C0 有的關鍵字:

break   goto  
void    int     double

ISO/IEC 9899:

直播中看到的疑問:

  • Q: what is lvalue/rvalue?
  • A: 這兩個名詞來自 = 兩邊的值,規格書中6.3.2.1:

    The name ‘‘lvalue’’ comes originally from the assignment expression E1 = E2,


  • Q:為什麼會印出s的位址不是Hellow world 的ascii?
​​char str[] = 'Hellow world'//string literal
​​printf(%x, str)
  • A: 根據規格書6.5.2.1 Array subscripting,[] 是pointer to object type 的 postfix,如 pointer to int,而str其實是 an array object ( a pointer to the initial element of an array object )

我不太理解這裡的 E1 到底是什麼,翻來翻去結果是 pointer to element (所以可以做指標運算)又是 array type (所以 E1 是lvalue 時,不能做modifiable value),導致 E1++ 不合法,但 E1+1合法。
所以 E1 是 pointer ,但是 array type 讓他行為不同嗎?而其他的pointer 會有 type嗎?(但pjchiou的共筆指 E1 不是指標,所以不能 E1++,有點困惑)

array subscripting 限制了可用的 operator,請回頭看規格書 6.5.2 並且對照 gcc 編譯錯誤訊息

6.5.2 第三點指出:[] 會把 E1 轉換成不能做lvalue的pointerYichung

提及他人共筆時,應該述及 ID 或其他識別資訊。
:notes: jserv


  • Q:我不會解析這段:

    E1[E2] is identical to(*((E1)+(E2)))

  • A:由於 E1 是 pointer ,所以這裡是的指標運算。
    &E1[12] = E1 + 12,指向一塊未初始化的記憶體。

  • Q:以前計概的array在教/學什麼?
  • A:不知道

gcc 及gdb:

compiler and debugger。

  • gcc:
    gcc -o [執行檔名稱] [要編譯的檔案]
  • gdb:
    ypChien的共筆 有精美的指令整理。

你不所知道的c語言:指標篇

int foo();

int main() {
    int (*funcptr)() = foo;
}

void * 之謎:

指標操作有相關的風險,例如:用 "指向比較小 object 的 pointer" 指向 比較大的 object ,而導致不如預期的行為。
因為透過 void* 無法直接被存取的特性,我們便能保護 object。
危險:

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 →

直接不給過:
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 →

另有優點:
stack overflow 的討論中,提及'void is nothing, void* is everything',作為function parameter時,很有靈活性。

文字訊息不要用圖片來展現,一來很難搜尋 (和複製貼上),二來視覺障礙的朋友無法觀看,自然無法跟你交流討論
:notes: jserv

pointer to pointer:

透過 address ,我們可以輕易地在 A function 修改 B function 的 object ,而如果這個object是pointer時,就需要pointer to pointer 了。

Type :

  • Type 決定了object或函式回傳值的expression
  • object types : describe objects
  • function types : describe functions
  • incomplete types : 描述物件但未決定物件(在記憶體中的)大小
  • A pointer type 可以指向以下,並被稱為 referenced type
    • a function type,
    • an object type,
    • an incomplete type
  • 三位一體: Array, function, pointer types
    都稱為 derived declarator types,都是從‘導’出來的型態

row major cache

待研讀:

Uno: 跟 cache line 有關 , column major 較容易會洗掉原本的 cache 內容
ref:cenalulu的文章

array vs pointer

syntax sugar

int *p, value;     // 這樣宣告才不會搞混value的型態
int* p, value;     // value實際上是int    

物件占了一定的空間,型態決定這些空間怎麼表示。
strcpy/strcat:使用到不該使用的記憶體,就會發生問題了。
cat:concatenate。
malloc:會失敗,失敗跟配置0都會回傳null

C語言把大家當成成熟的大人宅色夫

argv有點可怕

:question:
延伸:python有argv嗎?有,sys.argv 為什麼還要有argparse?因為有人的程式很複雜 argv不夠用
但經實驗sys.argv 跟argparse的速度差了三個數量級

附上實驗方法和數據,理工人說話要精準
:notes: jserv

在我的筆電上,輸入一個環境參數,argv大約使用了0.000006s,argparser 約需要0.004s,一直想用程式做更大規模的實驗,有機會會補上完整數據。yichung279

void Foo(int i, float f);

void Bar()
{
    int num = 1;
    Foo(num, 2.0);
}

num 與 2.0 即為 引數 (Argument),或稱為 函式引數 (Argument of a Function)。
i 與 f 則為 參數 (Parameter)。

function designator ,因為不是搭配 &, sizeof 使用,所以會被轉成為 pointer to function
就算 ***fptr,再多的 * 都鞥function designator。

#include <stdio.h>

int *func1()

char *func1()
{
   char *p = "hello world";
   return p;
}

int *func2()

char *func2()
{
   char p[] = "hello world";
   return p;
}
 
int main() 
{
    printf("%s\r\n", func1());
    printf("%s\r\n", func2());
} 

"" 會使用 static area 中的記憶體,進行 string literal,[] 會把上述的結果複製至stack中。
而func2() 指向的 array 在 func2 生命週期結束時,一起被釋放了,所以 func2() return值 便指向了 null。
但fun1() 指向 static area 裡的空間,func1生命週期結束後依然存在,所以 func1() return值 依然指向字串。

詭異的事:一開始程式打錯 char 打成 int ,func1() 仍印出 "hello world"。
於是試圖做以下實驗:

int func() { long a = 0x12345678; return a; } int main(){}

gdb :
whatis func():type = int
x func(): 0x12345678
p func(): 305419896

難以理解一個 int type ,有 unsigned long 的行為。一個 object 不是以 type 決定 expression 嗎?
function call 不能以 object 觀點檢視?(待我看完函式篇

後來做到 big-endian / little-endian 的題目就懂了。-yichung279

OSX把 /n 拿掉會多印一位,GNU/linux不會多印,online compiler不會多印,為何有不同的行為,為何不是印到OS介入為止。(UB?)

int main() { char s[] = "hello world"; s[11] = 'a'; puts(s); }

你所不知道的C語言:函式呼叫篇

C 語言歷史:

  • 早期 C 語言 (1972-1973) -> K&R C (1976-1979) -> ANSI C (1983-1989) -> ISO
  • 「不允許 nested function」這件事簡化了 C 編譯器的設計

Something behind "Hello world"

main printf 本身就是 function,涉及func call
呼叫標準函式庫,涉及linking
before main
memory layout