# 2018q3 Homework3 (review)
contributed by < littlepee >
- [第1週測驗題](https://hackmd.io/s/S1a9-YO_Q)
- [第2週測驗題](https://hackmd.io/s/BJA6LjbK7)
- [第3週測驗題](https://hackmd.io/s/BkyQskjK7)
## 第3週 測驗2
考慮以下程式碼,在 little-endian 硬體上,會返回 `1`,反之,在 big-endian 硬體上會返回 `0`:
```C
int main() {
union { int a; char b;
} c = { .a = 1 };
return c.b == 1;
}
```
- union
- little-endian 與 big-endian
若為 big-endian , `char b` 會取 0x0000 這個位址的值 `c.b = 0`,因此會 `return 0`
|記憶體位址|0x0000|0x0001|0x0002|0x0003|
|--------|------|------|------|------|
| value | 0x00 | 0x00 | 0x00 | 0x01 |
反之若為 little-endian , `char b` 會取 0x0000 這個位址的值 `c.b = 1`,因此會 `return 1`
|記憶體位址|0x0000|0x0001|0x0002|0x0003|
|--------|------|------|------|------|
| value | 0x01 | 0x00 | 0x00 | 0x00 |
### union
Union 內所宣告的所有變數共享同一個記憶體空間
分配的記憶體大小是以 size 最大的變數型態做為分配的依據
做個小測試,在union中宣告三個不同 size 的變數。
```clike=
int main(){
union{
long a;
int b;
char c;
} t;
printf("size of long: %d\n", sizeof(long));
printf("size of int: %d\n", sizeof(int));
printf("size of char: %d\n", sizeof(char));
printf("size of t: %d\n", sizeof(t));
}
```
結果可見,所宣告 union 的 size 和其中 size 最大的 double 相同
```shell
size of long: 8
size of int: 4
size of char: 1
size of t: 8
```
再來看一下 a b c 的記憶體位址
```clike
printf("t: %x\n", &t);
printf("a: %x\n", &t.a);
printf("b: %x\n", &t.b);
printf("c: %x\n", &t.c);
```
結果可見,所宣告的 union 以及其內部的變數,皆擁有相同的記憶體位址
```shell
t: b04d9b20
a: b04d9b20
b: b04d9b20
c: b04d9b20
```
### little-endian 與 big-endian
一個多位的整數將按照其存儲地址的最低或最高位元組排列。如果最低有效位在最高有效位的前面,則稱小端序( little-endian );反之則稱大端序( big-endian )。
若以儲存 0x12345678 為例 :
在 big-endian 中,從最高有效位開始存,因此會先存 0x12,再來是 0x34,0x56,0x78
|記憶體位址|0x0000|0x0001|0x0002|0x0003|
|--------|------|------|------|------|
| value | 0x12 | 0x34 | 0x56 | 0x78 |
在 little-endian 中,從最低有效位開始存,因此會先存 0x78,再來是 0x56,0x34,0x12
|記憶體位址|0x0000|0x0001|0x0002|0x0003|
|--------|------|------|------|------|
| value | 0x78 | 0x56 | 0x34 | 0x12 |
測試一下
```clike
int endian2(){
union {
int a;
char b[4];
} t;
t.a = 0x12345678;
printf("a ");
printf("%x ", &t.a);
printf("%x\n", t.a);
for(int i=0;i<4;i++){
printf("b[%d] ", i);
printf("%x ", &t.b[i]);
printf("%x\n", t.b[i]);
}
return 0;
}
```
結果得知,我的系統是使用 little-endian 的方式
```shell
a e7be06f0 12345678
b[0] e7be06f0 78
b[1] e7be06f1 56
b[2] e7be06f2 34
b[3] e7be06f3 12
```
### 參考資料
- [Unions 在C語言的簡單介紹](https://hamisme.blogspot.com/2012/02/unions-c.html)
- [維基百科 - 位元組順序](https://zh.wikipedia.org/wiki/%E5%AD%97%E8%8A%82%E5%BA%8F)
- [位元組順序 (Byte Order or Endianness) — big-endian vs. little-endian](https://notfalse.net/19/byte-order#i-2)
---
## 第2週 測驗2
```clike
void (*signal(int sig, void (*handler)(int))) (int);
```
逐一拆解,並試著使用 cdecl
`void (*handler)(int)`
handler 是一個 pointer to function,這個 function 帶有一個參數 int ,並 return void
```shell
cdecl> explain void (*handler)()
declare handler as pointer to function returning void
cdecl> explain void (*handler)(int)
declare handler as pointer to function (int) returning void
```
---
`void (*signal())(int)`
signal 是一個 pointer to function,需要一個參數 int ,並 return void
```shell
cdecl> explain void (*signal())(int)
declare signal as function returning pointer to function (int) returning void
```
---
`void (*signal(int sig, void (*handler)(int))) (int);`
signal 本身帶有兩個參數 `int sig` ,以及 `void (*handler)(int))` ,
而 signal 是一個 pointer to function,需要一個參數 int ,並 return void
### [github](https://github.com/Pinkii-/LI/blob/8ef070048bf38c69b923254b9caefd70dd2e81d8/satSolver/picosat-959/main.c)
```clike
resetsighandlers (void)
{
(void) signal (SIGINT, sig_int_handler);
(void) signal (SIGSEGV, sig_segv_handler);
(void) signal (SIGABRT, sig_abrt_handler);
(void) signal (SIGTERM, sig_term_handler);
#ifndef NALLSIGNALS
(void) signal (SIGKILL, sig_kill_handler);
(void) signal (SIGXCPU, sig_xcpu_handler);
(void) signal (SIGXFSZ, sig_xfsz_handler);
#endif
}
```
## 第2週 測驗7
```clike
void hex2(unsigned int x) {
do {
char c = "0123456789abcdef" [x & 0xf];
printf("char %c for %d\n", c, x);
x >>= 4;
printf("char %c for %d\n", c, x);
} while (x);
}
```
`x & 0xf` 取出 x 的最後面 4 個 bits ,使 `char c` 可從 array 中拿出對應到的 value ,並將之 printf 出來,再將 x 又移 4 個 bits ,反覆直到 x 變為 0。
若一開始 x = 182 = 32'b10110110
第一次:
若 x=32'b10110110 , x & 0xf = 32'b0110 = 6,
c = "0123456789abcdef" [6] ,獲得 "6"。
x 向右移 4 個 bits , x='b1011 。
第二次:
x=32'b1011 , x & 0xf = 32'b1011 = 11,
c = "0123456789abcdef" [11] ,獲得 "b"。
x 向右移 4 個 bits , x='b0 , 跳出 while 。
所以我們得到第一次的 6 和第二次的 b ,而 b6 為 182 的16進位結果。
可知,此 function 是將 x 的16進位結果 printf 出來,並且是從 LSB -> MSB。
而這個 function 有個限制就是要 unsigned int ,這樣在做向右位移時, x 的前 4 個 bits 才會都補 0。
若是 signed int x,當 sign bit 為 0 時,可以正常運作;但當 sign bit 為 1 時,在做向右位移時, x 的前 4 個 bits 都會補 1 ,使得 x 最後會變成 0xffffffff ,最後會一直 printf 出 "f" ,結果錯誤,且 while 迴圈無法終止。
### [glibc 原始碼](https://github.com/lattera/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/sunrpc/xcrypt.c)
將 bin 文件轉換成 hex
```clike
static const char hex[16] =
{
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
};
```
```clike
bin2hex (int len, unsigned char *binnum, char *hexnum)
{
int i;
unsigned val;
for (i = 0; i < len; i++)
{
val = binnum[i];
hexnum[i * 2] = hex[val >> 4];
hexnum[i * 2 + 1] = hex[val & 0xf];
}
hexnum[len * 2] = 0;
}
```
## 第1週 測驗1
```clike
int my_xor(int x, int y) { return (x | y) & (~ x | ~ y); }
int main() {
int x = 3, y = 5;
return my_xor(x, y);
}
```
用數位邏輯的觀念去想
and 閘可以用 "加法" 表示, or 閘可以用 "乘法" 表示。
`return ( x | y ) & ( ~ x | ~ y )`
可表示成
`
(x + y)(~x + ~y)
= x(~x) + x(~y) + y(~x) + y(~y)
= x(~y)+y(~x)
`
xor 真值表
| |x|~x|
|-|-|-|
|y|0|1|
|~y|1|0|
`x(~y)+y(~x)` 為 xor 的表示法
## 第3週 測驗1
考慮以下程式碼:
```C=
#include <stdio.h>
#include <stdint.h>
struct test {
unsigned int x : 5;
unsigned int y : 5;
unsigned int z;
};
int main() {
struct test t;
printf("Offset of z in struct test is %ld\n",
(uintptr_t) &t.z - (uintptr_t) &t);
return 0;
}
```
在 GNU/Linux x86_64 環境中執行,得到以下輸出:
```
Offset of z in struct test is 4
```
倘若將第 10 和第 11 換為以下:
```C=10
printf("Address of t.x is %p", &t.x);
```
會發生什麼事?
編譯失敗,不能將指標指向沒有對齊 1 byte 的結構體成員;
- C99規格書 [3.2] :
**alignment**
requirement that objects of a particular type be located on storage boundaries with
addresses that are particular multiples of a byte address
5+5+32 = 42 不是 8 的倍數 ( 1 byte = 8 bits)。
- C99規格書 [6.2.5.26] :
A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.39) Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.
pointer 需指向有 alignment 的記憶體位址,而 t.x 沒有 alignment ,所以 &t.x 無法指向沒有 alignment 的記憶體位址。