2016年12月11日 星期日

聯發科經典面試題

1. Explain "#error"



所以他就只是一個丟出錯誤訊息的 macro 而已。

2. Explain "struct" and "union"


在 C 中,我們可以利用 struct 來定義自己要的資料結構,例如說二維坐標中的一個點,可以用 struct Point {int x, y;};

同樣的我們也可以用 union 做類似的事情,只是差別在於, union 中的每個成員是共用同樣的記憶體。

3. Explain "volatile". Can we use "const" and "volatile" in the same variable? Can we use "volatile" in a pointer?


volatile 是告訴 compiler 不要擅自對變數作最佳化。

參考以下的 code:

int *x = FOO;
for (int i = 0; i < 100; ++i) {
    bar(i, x);
    *x = FOO;
}

其中 x 在迴圈外被初始化成 FOO (的值),先不管 FOO 是什麼,在迴圈內 compiler 發現 *x = FOO; 可以做一下最佳化讓 *x 不要給值這麼多次以增加執行效率,所以可能忽略 *x = FOO; 做的事情。

但是如果 FOO 是一個硬體的記憶體位置,每次取值可能不同的結果,那 compiler 擅自做了最佳化以後就會造成 *x 只從硬體的記憶體位置中取值取一次 (int *x = FOO;),為了避免這種事情發生,可以利用 volatile 加在 x 宣告的時候:

volatile int *x = FOO;

那 const 可以跟 volatile 一起使用嗎?當然囉,這兩個 keyword 的意思並沒有牴觸。const 的意思是 read-only 而不是不變的,volatile 的意思是他可能會變而不是你可以改他。

4. For the following C code

unsigned long v1 = 0x 00001111;
unsigned long v2 = 0x 00001202;
unsigned long v;
v = v1&(~v2);
v = v | v2;

ask: the value of v?

最後一個 v 可以展開一下:

v = v | v2
  = (v1 & (~v2)) | v2
  = ( v1 | v2 ) & ( ~v2 | v2 )

因為 ~v2 | v2 等於 1,所以展開後

v = v1 | v2 = 0x00001111 | 0x00001202 = 0x0001313

5. For the following C code

int a[5] ={1,2,3,4,5};
int *p = (int *)(&a+1);

ask: the value of *(a+1), (*p-1)?

*(a+1) 其實就是 a[1],所以就是 2;
(*p - 1) 就比較奇怪了,因為 (&a+1) 的關係所以 p 其實存的是 a[5] 的記憶體位置,
(a_pointer + a_number) = (a_pointer + (a_number * sizeof(*a_pointer)))
所以 &a + 1 = &a + 1 * sizeof(*&a) = &a + 1 * 20 = a[5] (這邊是假設 int 大小是 4 bytes)
所以理論上來說 *p 應該是 garbage,就算 - 1 以後還是 garbage ,答案應該是:不知道?


6. write a code

a. set the specific bit
b. clear the specific bit
c. inverse the specific bit (0->1; 1->0)

Macro 的解法:

a.
#define SET_BIT(x, n)      ((x) |=    (1 << (n)))
b.
#define CLEAR_BIT(x, n)    ((x) &= ~(1 << (n)))
c.
#define INVERSE_BIT(x, n)  ((x) ^=   (1 << (n)))

Function 解法:

a.
static inline void set_bit(int *x, int n) { *x |= (1 << n); }
b.
static inline void clear_bit(int *x, int n) { *x &= ~(1 << n); }
c.
static inline void inverse_bit(int *x, int n) { *x ^= (1 << n); }

7. Fill the blank

void(*(*papf)[3])(char *); // Rewrite this
typedef ___________;
pf(*papf)[3];


第三行最後是用 pf 把 *papf 當作參數傳進去,所以把中間的 (*papf)[3] 取出來變成 pf:

typedef void(*pf)(char *);



8. Write a code that check the input is a multiple of 3 or not without using division or mod


最蠢的方式當然就是寫一個迴圈一直減 3 然後看最後是不是剩下 0 ... 但是超沒效率 XD"

直覺想到一個比較好一點的方式是把全部位數相加,如果總和大於 10 的話就繼續做全部位數相加,值到剩下一位數為止,然後看是不是 0, 3, 6, 9,是的話就是 3 的倍數。但是沒辦法用 mod 所以不曉得怎麼取得每個位數 ... 所以大概只能用 sprintf 吧 XD

但實際講起來 sprintf 大概也是有用到除法或者 mod 吧 ...

目前看到有個比較可行的做法是,在 N base 的系統中測試能否被 (N+1) 整除的方式是將偶數位數的和跟奇數位數的和相減,再看是否能被 N+1整除。最常見的例子就是 11 啦,不過那是因為是在 10 base 的系統中,所以在 2 base (也就是 2 進位啦) 的系統中可以用這種方式來判斷是否為 3 的倍數。


int multiple_of_three(int num) {

    if (num < 0)
        num = -num;

    if (num == 0)
        return 1; // true
    if (num == 1 || num == 2)
        return 0; // false

    int even = 0, odd = 0;

    while (num != 0) {
        even += (num & 0x1);
        num >>= 1;
        odd += (num & 0x1);
        num >>= 1;
    }

    return multiple_of_three(even - odd);
}



9. Explain lvalue and rvalue.


lvalue 跟 rvalue 分別指的是 = (等號) 的左邊跟右邊的值,但這樣講不太精確,因為 lvalue 跟 rvalue 描述的是 expression 而不是 object:

C++03 3.10/1 says: "Every expression is either an lvalue or an rvalue."  It's important to remember that lvalueness versus rvalueness is a property of expressions, not of objects

lvalue 可以 reference, rvalue 不能 reference。


2016年12月8日 星期四

[C] strrev

找了一下 kernel 似乎沒有 strrev 所以記錄一下
http://www8.cs.umu.se/~isak/snippets/strrev.c


/*
**  STRREV.C - reverse a string in place
**
**  public domain by Bob Stout
*/
#include <string.h>

char *strrev(char *str)
{
      char *p1, *p2;

      if (! str || ! *str)
            return str;
      for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2)
      {
            *p1 ^= *p2;
            *p2 ^= *p1;
            *p1 ^= *p2;
      }
      return str;
}

#ifdef TEST

#include <string.h>

int main(int argc, char *argv[])
{
      while (--argc)
      {
            printf("\"%s\" backwards is ", *++argv);
            printf("\"%s\"\n", strrev(*argv));
      }
}

#endif /* TEST */



[C] Macro 中的 error 與 warning

其實很簡單,而且還很常見,只是我沒有意識到而已 XD

請看 Gnu C 解釋 https://gcc.gnu.org/onlinedocs/cpp/Diagnostics.html

第二個例子看完秒懂:

#if !defined(FOO) && defined(BAR)
     #error "BAR requires FOO."
#endif


這不就是我常常在 Gentoo 做 emerge 很常看到的錯誤訊息嗎 XD


CONFIG_OPENVSWITCH is not set


就只是顯示錯誤訊息而已,差別就 error 會停,warning 就只是警告然後繼續 compile。

[C] const volatile

http://stackoverflow.com/a/4597164 給了一個不錯的解釋。

以下是回答所提供的例子:

unsigned int const volatile *status_reg; // assume these are assigned to point to the 
unsigned char const volatile *recv_reg;  //   correct hardware addresses


#define UART_CHAR_READY 0x00000001

int get_next_char()
{
    while ((*status_reg & UART_CHAR_READY) == 0) {
        // do nothing but spin
    }

    return *recv_reg;
}

因為不希望這支程式改動到 status_reg 跟 recv_reg 指到的值,所以把他們設定為 const。

但並不表示其他程序不會動到 status_reg 跟 recv_reg 所指到的值,以這個例子來說就是硬體會改變那兩個記憶體位置的值。

如果 compiler 在這支程式做最佳化的話,那 while 迴圈可能就只會讀取 status_reg 一次,那就有可能面臨到無窮迴圈跳不出來的問題,所以只好把他們設定為 volatile,叫 compiler 不要偷懶,每次乖乖的去取值。


https://read01.com/oONo5Q.html 提到的一些觀念釐清:


  • const: 含意是「請做為常數使用,這個值只能讀不能寫」,而並非「這個值只是個常數而他不會變」
  • volatile: 含意是「請不要做最佳化,因為這個值可能會變」,而並非「你可以修改這個值」


所以其實 const 跟 volatile 是不矛盾的。


勉強になった。

2016年12月3日 星期六

[Gentoo] 在 Libvirt 上跑 Chromium OS

先裝 repo [1]


$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
$ export PATH=`pwd`/depot_tools:"$PATH"


然後安裝時候需要 sudo,我用 gentoo 沒在用 sudo 所以還要另外裝一下 ...

$ emerge sudo


然後下載 chromium source code

$ mkdir -p ${HOME}/chromiumos
$ cd ${HOME}/chromiumos
$ repo init -u https://chromium.googlesource.com/chromiumos/manifest.git --repo-url https://chromium.googlesource.com/external/repo.git
$ repo sync -j4


開始編譯 [2]


$ export BOARD=amd64-generic
$ cd ${HOME}/chromiumos/src/script
$ ./build_packages --board=${BOARD}
$ ./build_image --board=${BOARD} --noenable_rootfs_verification dev
$ ./image_to_vm.sh --board=${BOARD}

然後就可以看到編好的 vm 檔

$ ls -l ../build/images/amd64-generic/latest/chromiumos_qemu_image.bin
$ file ../build/images/amd64-generic/latest/chromiumos_qemu_image.bin
../build/images/amd64-generic/latest/chromiumos_qemu_image.bin: DOS/MBR boot sector; partition 1 : ID=0xee, start-CHS (0x0,0,2), end-CHS (0x3ff,255,63), startsector 1, 14995391 sectors


用 virt-install 來 import [4]

$ virt-install --virt-type kvm \
               --name chromium \
               --ram 2048 \
               --disk path=/home/mkfsn/chromiumos/src/build/images/amd64-generic/latest/chromiumos_qemu_image.bin \
               --network bridge=br0 \
               --graphics vnc,listen=192.168.0.159 \
               --noautoconsole \
               --os-type=linux \
               --import



因為 os-variant 裡面沒一個適合的,所以我就乾脆不給 XP

打完收工

$ virsh list
 Id    Name                           State
----------------------------------------------------
 4     chromium                       running



Reference
  1. https://www.chromium.org/developers/how-tos/install-depot-tools
  2. https://www.chromium.org/chromium-os/developer-guide
  3. https://www.chromium.org/chromium-os/how-tos-and-troubleshooting/running-chromeos-image-under-virtual-machines
  4. https://www.itfromallangles.com/2011/03/kvm-guests-using-virt-install-to-import-an-existing-disk-image/

2016年11月29日 星期二

[Go] Web Framework: Martini / Beego / Revel

[1] 有 Martini[4] 跟 Beego[5] 的 benchmark 比較,但還沒看怎麼測得。

從結果上來比較一下:


Martini Beego Revel
GET 135 ms 115 ms --
POST 135 ms 116 ms --
static 132 ms 109 ms --
view 403 ms 192 ms --


Beego 跟 Martini 比根本完勝,等我看完怎麼測 benchmark 之後再來測測看 Revel[6]


不過 [2] 從 Github 上的資訊來比較:



Commits Braches Contributors Features
Martini 589 3 99 Modular, Routing, some third party middlewares available
Beego 2622 2 169 MVC, modular, hot code reload, auto testing, Routing, Session, Cache, ORM
Revel 1277 14 110 MVC, modular, hot code reload, auto testing, Routing, Reverse routing, Session, Cache (RAM/redis/memcached)


三個比較起來 Beego 似乎比 Revel 還要熱門一點。
(老實說 Branch 我不曉得是要比什麼鬼 ...)


值得一提的是,[2] 最後表示從 benchmarks[3] 來看, Revel 比 Beego 效能還要好,甚至在大部分的 case 中比 plain golang app 還要快 ( 誒竟然 XDDDD


雖然還不曉得測出來的 benchmark 值不值得相信,不過看來我下一個要玩的玩具就是 Revel 了 XD


參考連結:
  1. https://github.com/coderhaoxin/koa-vs-martini-vs-beego
  2. https://www.linux.org.ru/forum/web-development/10360359
  3. https://www.techempower.com/benchmarks/
  4. https://github.com/go-martini/martini
  5. https://github.com/astaxie/beego
  6. https://github.com/revel/revel

附註:請原諒我沒去美化那兩個 table Orz ...



2016年11月28日 星期一

用 KVM 部署 Ceph

前言


大概一兩年前聽過 ceph[4],然後最近也從很多地方看到這個東西,包括公司職缺的簡介中,所以就翻了一點資料。雖然資料算滿多的,但是大略掃過以後覺得好像摸不著邊,剛好看到 ptt 上面有人寫了一個部屬的步驟 [1] (感謝大大無私分享!) ,然後我最近又才架完 KVM 不久,就試著在 VM 上玩看看,順便還可以熟悉一下 virsh( ... 熟悉一點點 XD)。

環境


  • Host
    • Gentoo kernel-4.4.26 64bit
  • Guest
    • Ubuntu-16.04 Server 64bit (x4台)
  • VMs' network
    • 192.168.0.181 ceph-admin
    • 192.168.0.182 ceph-1
    • 192.168.0.183 ceph-2
    • 192.168.0.184 ceph-3

用 Virsh 先把 ceph-admin, ceph-1, ceph-2, ceph-3 架起來:

$ virt-install --virt-type kvm \
               --name $(NAME) \
               --ram 512 \
               --cdrom=$(ROOT)/ubuntu-16.04.1-server-amd64.iso \
               --disk path=$(ROOT)$(DIR)/$(NAME).qcow2,size=16,format=qcow2 \
               --network bridge=br0 \
               --graphics vnc,listen=0.0.0.0 \
               --noautoconsole \
               --os-type=linux \
               --os-variant=ubuntu16.04

然後另外要產生給 ceph OSD (Object Storage Device,負責提供儲存資源) 的 disk:

$ qemu-img create -f raw ceph3-data.img 10G


應該是要用 virt-clone 的,可是因為我沒辦法用 NAT 所以指定 IP 之類的好像有點麻煩,所以就先用最簡單蠢的方式把這四台 VM 架起來。

不曉得為什麼我的這台 host 開的 VM 如果是用 NAT 的話就會沒辦法順利拿到沒問題的封包,意思就是拿到的封包都有問題 ... 然後用 bridge 就沒事了,怪哉。而且不是 VM Guest 而已,是連 docker container 都一樣 ...

正片開始


架好之後在四台的 /etc/hosts 加上:

192.168.0.181 ceph-admin
192.168.0.182 ceph-1
192.168.0.183 ceph-2
192.168.0.184 ceph-3


一開始先在 ceph-admin 把 ssh 用的 key 建好並複製到其他主機:

mkfsn@ceph-admin:~$ ssh-keygen
mkfsn@ceph-admin:~$ ssh-copy-id ceph-1
mkfsn@ceph-admin:~$ ssh-copy-id ceph-2
mkfsn@ceph-admin:~$ ssh-copy-id ceph-3


接著在 ceph-admin 把該裝的套件裝好:


mkfsn@ceph-admin:~$ wget -q -O- 'https://ceph.com/git/?p=ceph.git;a=blob_plain;f=keys/release.asc' | sudo apt-key add -
mkfsn@ceph-admin:~$ echo deb echo deb http://ceph.com/debian-firefly/ $(lsb_release -sc) main |  sudo tee /etc/apt/sources.list.d/ceph.list
mkfsn@ceph-admin:~$ sudo apt-get update
mkfsn@ceph-admin:~$ sudo apt-get install ceph-deploy
mkfsn@ceph-admin:~$ sudo apt-get install ceph-common

部署


建立工作目錄
mkfsn@ceph-admin:~$ mkdir ~/ceph && cd ~/ceph


在所有node上安裝ceph套件
mkfsn@ceph-admin:~/ceph$ ceph-deploy new ceph-1 ceph-2 ceph-3

撞了第一個牆:



看 error message 似乎是 ceph-1 沒裝 python 的原因,所以就把 ceph-1, ceph-2, ceph-3 裝上 python 吧:

mkfsn@ceph-1:~$ sudo apt install -y python
mkfsn@ceph-2:~$ sudo apt install -y python
mkfsn@ceph-3:~$ sudo apt install -y python


接下來繼續撞,第二個牆:



[2] 說在 ceph-1,2,3 的 /etc/sudoers 要設定讓使用者不用輸入密碼就可以執行 sudo,所以我就在 /etc/sudoers.d/ 新增一個檔案 01mkfsn:

mkfsn   ALL=(ALL) NOPASSWD:ALL

接著回到主任務:

在所有 node 上安裝 ceph 套件
mkfsn@ceph-admin:~/ceph$ ceph-deploy install ceph-1 ceph-2 ceph-3

建立 mon
mkfsn@ceph-admin:~/ceph$ ceph-deploy mon create ceph-1 ceph-2 ceph-3

將所有 node 的密鑰都複製到工作目錄
mkfsn@ceph-admin:~/ceph$ ceph-deploy gatherkeys ceph-1 ceph-2 ceph-3

增加 OSD
mkfsn@ceph-admin:~/ceph$ ceph-deploy osd prepare ceph-1:/dev/vdb ceph-2:/dev/vdb ceph-3:/dev/vdb


複製 admin 密鑰到所有 node
mkfsn@ceph-admin:~/ceph$ ceph-deploy admin ceph-1 ceph-2 ceph-3


最後檢查部署狀態,不過馬上就噴了錯誤
mkfsn@ceph-admin:~/ceph$ sudo ceph health
auth: unable to find a keyring on /etc/ceph/ceph.client.admin.keyring,/etc/ceph/ceph.keyring,/etc/ceph/keyring,/etc/ceph/keyring.bin: (2) No such file or directory




貌似是需要把 ceph.client.admin.keyring 複製到 /etc/ceph 底下才行,所以就複製過去吧!
mkfsn@ceph-admin:~/ceph$ sudo cp ceph.client.admin.keyring /etc/ceph/


掛載 rbd 內核模組
mkfsn@ceph-admin:~/ceph$ sudo modprobe rbd


建立一個 4G 的 image
mkfsn@ceph-admin:~/ceph$ sudo rbd create --size 4096 test

將 test 這個 image map 到 rbd pool,然後就撞到第四個牆
mkfsn@ceph-admin:~/ceph$ sudo rbd map test --pool rbd
rbd: sysfs write failed
RBD image feature set mismatch. You can disable features unsupported by the kernel with "rbd feature disable".
In some cases useful info is found in syslog - try "dmesg | tail" or so.
rbd: map failed: (6) No such device or address


[3] 表示有一些 feature 沒有被 kernel client 支援,所以關掉即可:

  • exclusive-lock
  • object-map
  • fast-diff
  • deep-flatten

mkfsn@ceph-admin:~/ceph$ sudo rbd feature disable test exclusive-lock object-map fast-diff deep-flatten

然後就可以順利執行了~

mkfsn@ceph-admin:~/ceph$ sudo rbd map test --pool rbd
/dev/rbd0

都看到 block device file 了,就 format mount 起來看看囉~

mkfsn@ceph-admin:~/ceph$ sudo mkfs.ext4 /dev/rbd0
mkfsn@ceph-admin:~/ceph$ sudo mkdir -p /rbd/test

最終結果:

mkfsn@ceph-admin:~/ceph$ lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sr0     11:0    1 1024M  0 rom
vda    253:0    0   16G  0 disk
|-vda1 253:1    0 15.5G  0 part /
|-vda2 253:2    0    1K  0 part
`-vda5 253:5    0  510M  0 part [SWAP]
rbd0   251:0    0    4G  0 disk /rbd/test

心得


覺得 deploy 起來真的是非常簡單易懂,照著指令打你說誰不會呢 ? XD

所以之後有空要來挖一下實作的部分,只是我對於 storage 真的很不熟啊啊啊啊 Orzzz

Reference


2016年11月24日 星期四

[Gentoo] bind9 的 dnssec 之拖了三年終於弄好?了

總之先產生 Key,然後我這邊是使用 hinet 可以支援的最高標準: RSA/SHA256,長度 1024,會需要花一點時間在執行。

$ dnssec-keygen -r /dev/random -a RSASHA256 -b 1024 -n ZONE mkx.tw
Generating key pair.........++++++ .++++++
Kmkx.tw.+008+59265

接著用 dnssec-signzone 來簽署, -S 是智慧型簽署,只要把你要簽署的檔案放到目前的資料夾中就可以了,所以我就先 ln 把我自己的 zone file 弄到目前的資料夾底下(檔名就設成 zone,以我的為例是 mkx.tw),程式會自己去讀檔案裡面的配置,然後逐一簽署:

$ ln -s /path/to/mkx.tw.fwd ./mkx.tw
$ dnssec-signzone -S -z mkx.tw
Fetching ZSK 59265/RSASHA256 from key repository.
Verifying the zone using the following algorithms: RSASHA256.
Zone fully signed:
Algorithm: RSASHA256: KSKs: 0 active, 0 stand-by, 0 revoked
                      ZSKs: 1 active, 0 stand-by, 0 revoked
mkx.tw.signed

這邊會產生兩個檔案: dsset-mkx.tw 跟 mkx.tw.signed。
然後就可以來修改 config 檔 (named.conf)

options {
    ...
    dnssec-enable yes;
    dnssec-validation yes;
    ...
}

zone "mkx.tw" IN {
    file "/path/to/mkx.tw.signed";
};

最後到 hinet 網域註冊那邊登記 DS 記錄,這時候需要打開 dsset-mkx.tw 來看一下

$ cat dsset-mkx.tw
mkx.tw.                 IN DS 59265 8 1 DF0619E7A7CD1DD2DEDB9D431637C905ACE6B299
mkx.tw.                 IN DS 59265 8 2 D14507C8639B06B1CAD8516F8E997A9D824FD07BE04B4E75D06A0754 CB737FFF

以我的例子來說,59265 就是 key tag,而 hinet 表示 DS 記錄長度需要 64,一開始不太懂所以試了幾次,結果發現原來就是第二行的後面那兩串去掉中間的空白,嗯 ... 長度是 64 沒錯啦,不過 hinet 網頁真的寫得不清不楚啊 ...

2016年11月14日 星期一

[C/C++] Prefix/Postfix Increment/Decrement Operators

Prefix/Postfix Increment/Decrement Operators


今天在研究 Sequence Point 的時候發現一個有趣的 expression:

int i = 0, j;
j = ++++++i;

這邊就不探討 sequence point 的問題了,直接來說關於這行 ++++++i 是什麼鬼東西。

R-value 跟 L-value


首先要知道關於 rvalue 跟 lvalue,而他們最簡單且籠統的定義就是:

  • rvalue: = 右邊的東西
  • lvalue: = 左邊的東西

進一步來說,lvalue 就是只你可以『給值』的東西,是有實體記憶體的,例如說一般變數:

int x;

,所以你可以給值:

x = 3;

,那 rvalue 呢?一般來說是不是 lvalue 的都叫做 rvalue。


這也太籠統了吧?!舉個例子來看看:

3

似乎很有道理,因為你應該不會寫出像這樣的 code:

3 = x;

吧?因為 3 不能給值,所以我們把他歸類為 rvalue。


當然 rvalue 跟 lvalue 並不是像我這種憨人所想得這麼簡單,否則網路上也不會這麼多文章再探討 rvalue 跟 lvalue 了,但畢竟這不是這篇文章要講的重點,所以就不詳細說明了。

++++++i;


回到這鬼玩意兒,聰明的你可能知道他應該是長這個樣子:

++ ++ ++i;

你可以再更靠近一點:


++  ++  ++i;


看不出來的話我們在更進一步表示一下:


++ ( ++ ( ++i ) );


對 C 跟 C++ 來說這鬼玩意兒就是這樣解釋的。


看懂之後那問題來了,這行 code 是正確的嗎?


首先講一下 C++,對 C++ 來說,
  • Prefix Operator (++x): 回傳的是 lvalue
  • Postfix Operator (x++): 回傳的是 rvalue

Prefix versions of the built-in operators return references and postfix versions return values. [1]

既然 ++x 是回傳 lvalue 代表我們可以繼續修改他回傳的記憶體,所以那個鬼玩意兒對 C++ 來說最後 i 的結果就是 3。


那對 C 來說呢?很抱歉,兩個都不是回傳 lvalue。


這也意味著在最裡面那個括號做完以後是不能修改的,當然就不能在對他做 ++ 囉,所以 C 的編譯器在編譯的時候就會報錯:


test.c: In function ‘main’:
test.c:3:7: error: lvalue required as increment operand
     ++++++i;
       ^


結論



又是一個搞死人的 code。


Reference

  1. http://en.cppreference.com/w/cpp/language/operator_incdec
  2. https://stackoverflow.com/questions/21351799/postfix-prefix-increment-l-value-and-r-value-in-c-and-c


[C/C++] Function 參數裡的 void

在 C/C++ 裡面,如果 function 回傳的 type 是 void 的話,代表這個 function 沒有回傳任何東西。

但是如果是在參數裡面呢?

void foo(void);

在 C 跟 C++ 中,代表的是 foo 這個 function 不接受任何參數,但是

void foo();

在 C 跟 C++ 的解釋卻不一樣。對 C 來說 foo 可以接收任意數的參數,但是 C++ 的解釋跟 foo(void) 一樣。

也就是說,以下的 code:

// test.c
void foo() {}

int main () {
    foo (1, 2, 3, 4);
    foo ("hello world");
    return 0;
}


在 C 編譯時不會有任何問題,但是在 C++ 編譯的時候會跳錯誤:

test.c: In function ‘int main()’:
test.c:6:19: error: too many arguments to function ‘void foo()’
     foo(1, 2, 3, 4);
                   ^
test.c:1:6: note: declared here
 void foo() {}
      ^
test.c:7:22: error: too many arguments to function ‘void foo()’
     foo("hello world");
                      ^
test.c:1:6: note: declared here
 void foo() {}

Reference

2016年11月10日 星期四

C: pointer arithmetic

http://stackoverflow.com/a/394777 得到了我想要的答案。

當你寫 (a_pointer + a_number) 的時候,實際上是:
(a_pointer + (a_number * sizeof(*a_pointer)))

總之先拿 code 來觀察一下,

#include <stdio .h>

int main() {

    int a[10];
    int *p = a;

    printf("a = %p\n", a);
    printf("a + 1 = %p\n", a + 1);

    printf("&a     = %p\n", &a);
    printf("&a + 1 = %p\n", &a + 1);

    printf("&p     = %p\n", &p);
    printf("&p + 1 = %p\n", &p + 1);

    return 0;
}

會得到以下的結果(記憶體當然不會一樣,不過可以觀察一下差距)

a       = 0x7fff56c39650
a + 1 = 0x7fff56c39654
// 相差了 4 bytes,也就是一個 int 大小

&a       = 0x7fff56c39650
&a + 1 = 0x7fff56c39678
// 相差了 40 bytes,也就是一個 a 的大小(sizeof(int) * 10 = 4 * 10)

&p       = 0x7fff56c39640
&p + 1 = 0x7fff56c39648
// 相差了 8 bytes,也就是一個 pointer 大小(64bit 的系統下)


2016年11月9日 星期三

SQL 七種 JOIN 方法

面試群暉的時候被問到 SQL 的 OUTER JOIN 跟 INNER JOIN 的差別,壓跟沒想到這間會問到跟 Web 有關的 ... 是我自己準備不周 Orz

不過查了一下發現其實很簡單,一張圖就可以全部弄懂了:


回到面試被問的問題,

INNER JOIN 就是最中間那張圖, A 跟 B 的交集。

而 OUTER JOIN 有三種:
  • LEFT OUTER JOIN: 就是 A
  • RIGHT OUTER JOIN: 就是 B
  • FULL OUTER JOIN: 就是 A 跟 B 的聯集

2016年11月8日 星期二

C++ Virtual Function

因為準備面試所以複習了一下實作 C++ 多型 (Polymorphism) 的 virtual function。

Virtual function 的主要目的是要在 runtime 的時候解決 switch 的問題,於是我們不需要實際寫一個 switch 去判斷現在的目標是什麼 type。

直接看 code :

#include <iostream>  

using std::cout;  
using std::cerr;  
using std::endl;  

class Otoko {  
public:  
  virtual bool can_ask_line_id() = 0;  
};  

class Ikemen: public Otoko {  
public:  
  virtual bool can_ask_line_id() {  
    return true;  
  }  
};  

class Otaku: public Otoko {  
public:  
  virtual bool can_ask_line_id() {  
    cerr << "死ね!!" << endl;  
    return false;  
  }  
};  

int main(int argc, char* argv[]) {  
  Otoko *yuujinA = new Ikemen(),  
        *yuujinB = new Otaku();  

  cout << (yuujinA->can_ask_line_id() ? "No problem" : "Impossible") << endl;  
  cout << (yuujinB->can_ask_line_id() ? "No problem" : "Impossible") << endl;  

  delete yuujinA;  
  delete yuujinB;  

  return 0;  
}


其中變數 yuujinA, yuujinB 的型別同樣都是 Otoko*,但是分別指到不同的 class (Ikemen, Otaku)。

在 runtime 的時候,我不需要去寫:

if (yuujinA 是 Ikemen) {  
} else if (yuujinA 是 Otaku) {  
} else {  
}  

當然更不用:
switch (yuujinA 的型別) {  
  case Ikemen:  
  case Otaku:  
}  

只需要在 class 裡面宣告、定義 virtual function,在 runtime 的時候便會自動呼叫對應的 virtual function。

所以儘管 yuujinA 跟 yuujinB 有同樣的型別 Otoko*,runtime 的時候呼叫的 virtual function 便會依照他們實際是指到哪個 class ,再去做對應的 function call。

而沒有加上 virtual 的 function,例如 whoami,就只會去看 yuujinA, yuujinB 宣告的時候的 type 去做對應的 function call。



<<名詞解釋>>

Otoko: 男人
Ikemen: 帥哥
Otaku: 阿宅
yuujinA: 友人A
yuujinB: 友人B