AIdrifter
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # [AIdrifter CS 浮生筆錄](https://hackmd.io/s/rypeUnYSb) <br> C 初次試煉 https://forum.xda-developers.com/showpost.php?p=78112850&postcount=6023 ![](https://i.imgur.com/0Wv7Ihs.png) # C ## Function Pointer Type ```C typedef int(*OP)(int,int); int add(int a , int b) { return a+b; } int main() { OP fun_ptr= add; fun_ptr(3,1); } ``` ## Declare ```C #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <string.h> #include <unistd.h> int add(int num) { return ++num; } int sub(int num) { return --num; } int square(int num) { return num*num; } int main() { int Array1D[10]; int Array2D[3][10]; int *a[10] = {Array1D,Array1D+1,Array1D+2}; // 一個有10個指標的陣列,該指標是指向一個整數型的 int (*a1)[10] = Array2D; // 一個指向有10個整數型陣列的指標 int (*a2)(int) = add; // 一個指向函數的指標,該函數有一個整數型參數並返回一個整數 int (*a3[10])(int); // 一個有10個指標的陣列,該指標指向一個函數,該函數有一個整數型參數並返回一個整數 a3[0] = add; a3[1] = sub; a3[2] = square; printf("(3+1) + (4-1) + (4*4) = %d \n",add(3) + sub(4) + square(4)); int non_const_num = 40; int non_const_num1 = 50; const int num = 10; const int num1 = 20; const int num2 = 30; const int *foo = &num; // 一個 pointer,指向 const int 變數。 int const *foo1 = &num; // 一個 pointer,指向 const int 變數。 foo = &num1; foo = &num2; int* const foo2 = &non_const_num; // 一個 const pointer,指向 int 變數。 // foo2 = &non_const_num1; /* assignment of read-only variable ‘foo2’*/ int const *const foo3 = &num2; // 一個 const pointer,指向 const int 變數。 // foo3 = &num2; /* assignment of read-only variable ‘foo3’ */ return 0; } ``` ![](https://i.imgur.com/NnJDlxj.png) ## Const const 通常表示**只可讀取不可寫入的變數**,常用來宣告常數。使用const有以下好處: * 提升程式碼可讀性 * 使編譯器保護那些不希望被改變的參數 * 給優化器一些附加的資訊 編譯器處理方式 : define 在==預處理階段==展開;const 在==編譯階段==使用。 類型和安全檢查 : const 會在編譯階段會執行類型檢查,define 則不會。 存儲方式 : define 直接展開不會分配記憶體`gcc -E`,const 則會在記憶體中分配。 --- ## inline inline 可以將修飾的函式設為行內函式,即像巨集(#define)一樣==將該函式展開編譯,用來加速執行速度。== ## inline 和 #define 的差別在於: * inline 函數只對參數進行一次計算,避免了部分巨集易產生的錯誤。 * inline 函數的參數類型被檢查,並進行必要的型態轉換。 * 巨集定義盡量不使用於複雜的函數 * **用 inline 後編譯器不一定會實作,僅為建議**。 --- ## Volatile * 被 `volatile` 修飾的變數代表它可能會被不預期的更新,因此告知 * 編譯器不對它涉及的地方做最佳化 * 每次操作它的時候都讀取該變數實體位址上最新的值,而不是讀取暫存器的值。 ## 運用 * Interrupt service routines (中斷處理程式) ```C int etx_rcvd = FALSE; // initialize etx void main() { ... /* As far as the compiler is concerned, * the expression !ext_rcvd is always true, * and, therefore, you can never exit the while loop. * Naturally, the blame will be placed on a "lousy optimizer." */ while (!ext_rcvd) { // Wait } ... } interrupt void rx_isr(void) { ... if (ETX == rx_char) { etx_rcvd = TRUE; } ... } ``` * Multi-threaded applications (多線程應用程式) * Despite the presence of queues, pipes, and other scheduler-aware communications mechanisms in real-time operating systems, it is still fairly common for two tasks to exchange information via a shared memory location (that is, a global). When you add a pre-emptive scheduler to your code, your compiler still has no idea what a context switch is or when one might occur. Thus, another task modifying a shared global is conceptually identical to the problem of interrupt service routines discussed previously. So all shared global variables should be declared volatile. For example: * 在即時操作系統中(RTOS),除去佇列(queues)、管道(pipes)以及其他調度相關的通訊結構,在兩個任務之間採用共用的記憶體空間(shared memory location)(就是全局共用, global)實現資料的交換仍然是相當常見的方法。當你將一個優先權調度器應用于你的代碼時,編譯器仍然不知道某一程式段分支選擇的實際工作方式以及什麼時候某一分支情況會發生。這是因為,另外一個任務修改一個共用的總體變數在概念上通常和前面中斷處理程式中提到的情形是一樣的。所以,(這種情況下)所有共用的總體變數都要被聲明為volatile。例如: ```C /* * This code will likely fail once the compiler's optimizer is enabled. * Declaring cntr to be volatile is * the proper way to solve the problem. * / int cntr; // Sould set volatile void task1(void) { cntr = 0; while (cntr == 0) { sleep(1); } } void task2(void) { cntr++; sleep(10); } ``` * 設備的硬體暫存器(如狀態暫存器) * consider an 8-bit status register at address 0x1234. It is required that you poll the status register until it becomes non-zero. The nave and incorrect implementation is as follows: * 假設我們有一個8位元的狀態暫存器映射在位址0x1234上。系統需要我們一直監測狀態暫存器的值,直到它的值不為0為止 * 錯誤的實現方法: ```C uint * ptr = (uint *) 0x1234; // Wait for register to become non-zero.等待暫存器變為非0值 while (*ptr == 0); // Do something else.作其他事情 ``` ```ASM mov ptr, #0x1234 mov a, @ptr loop bz loop ``` * 正確的實現方法: ```C uint volatile *ptr = (uint volatile *) 0x1234; ``` ```ASM mov ptr, #0x1234 loop mov a, @ptr bz loop ``` * const + volatile * 這是在 RTOS kernel 常見的一種宣告:rt_clock通常是指系統時鐘,它經常被時鐘中斷進行更新。所以它是volatile。 * 而rt_clock通常只有一個寫者(時鐘中斷),其他地方對其的使用通常都是唯讀的。 ```C extern const volatile unsigned int rt_clock; ``` ## Undefined Behavior ```C // ++i; add DWORD PTR [rbp-4], 1 mov eax, DWORD PTR [rbp-4] // i++; mov eax, DWORD PTR [rbp-4] lea edx, [rax+1] mov DWORD PTR [rbp-4], edx ``` ``` int i = 10 i = i++ + ++i; ``` 這個問題的標準答案是 ``` i = i++ + ++i; i = 10 + ++i; i = 10 + 12; i = 22 ``` > 語言規格在定義時為了編譯器實做上的彈性和效率考量,會刻意不去規定某些規格,因此如果寫出來的程式依賴或著錯用某些沒有在規格內所規定的特性時,我們就稱之為Undefined behavior, > reference: [萬惡的未定義行為](http://blog.ez2learn.com/2008/09/27/evil-undefined-behavior/) ## Pointer to Pointer ![](https://i.imgur.com/KLWYgb6.png) ## Pointer Operation - 指標位址不能相加,但是指標位址可以相減`offset` - 指標相加: int *a + 5 = a + sizeof(int) * 5 - 指標相減: int *a - int *b = a 和b 中間差幾個element ```C #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <string.h> #include <unistd.h> int main() { int *a, *b; a = (int *)1; b = (int *)1; printf("%d %d %d\n", a + (int)b, (int)(a + (int)b), (int)a + (int)b); // 5 5 2 a = (int *)0x5566; b = (int *)0x5570; printf("%d %d %d %d %d\n", a, b, (int)a+(int)b, a+(int)b, sizeof(int *)); // 21862,21872, 43734 , 109350 , 8 printf("%d\n", a - b); // -10/4 = -2.5 Complement is -3 printf("%d\n", ((int)b - (int)a)/sizeof(int *)); // 10/8 = 1 a = (int*)0x1000; b = a + 3; printf("%d %d %p %p \n", a, b, a, b); // 4096, 4108 0x1000, 0x100c system("pause"); return 0; } ``` ## const int* p 和 int* const q 兩者之差別: * 前指標指的內容不可變動,後指標不可變動 * C++ * void dp()const; // 我不會更改成員 const member function * const int* op() const{return &data;} // 回傳const參考 保證不會更改 --- ## Interrupt Service Routine 之錯誤 ```C __interrupt double isr(double r) //不能有參數 { double area = PI*r*r ; printf("%f\n",area) ; //改成printk return area ; // 不能有return } ``` - ISR不能有返回值(不知道給誰) - ISR不能傳遞參數(不知道誰呼叫) - `pintf()`改`printk()`` - ISR應短而高效 ## 一行程式碼 N是否為判斷2的次方 ```C boolean isPowerof2(int n) { //return n > 0 && (n & (n - 1)) == 0; return ((n&-n)==n)?true:false } ``` 二進位中,只要是 2 的次方,都剛好會是像 100 (4)或者 1000 (8)這種 1 開頭,尾巴都是 0 的狀況 ``` 8 的二進位 1000 7 的二進位 0111 1000 & 0111 ----- 0000 = 0 (return true) ``` Reference: http://davidhsu666.com/410/ispowerby2 ## Fun Array ```C int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int *p = &(a + 1)[3]; printf("%d\n", *p); ``` 輸出 5 因為a+1指向a的第二個元素,[3]表示再向後移動3個元素 `a+1`是跳一個int的大小(a想成指標) `&a+1`是跳一整個array的大小 ## What's wrong on the code. - Wrong version ```C void GetMemory(char *p) { p=(char *)malloc(100); } void test() { char *str=NULL; GetMemory(str); strcpy(str,"hello world"); printf(str); } ``` - Correct version ``` int GetMemory(char **p, int num) { *p=(char *)malloc(num); if(*p==NULL) { return -1; // memory allocated failed } return 0; } void test() { char *str=NULL; GetMemory(&str,100); strcpy(str,"hello world"); puts(str); } ``` - Wrong version ```C swap( int* p1,int* p2 ) { int *p; *p = *p1; *p1 = *p2; *p2 = *p; } ``` - dereference *p 會core dump - swap 需要return value; ```C // stupid way swap( int* p1,int* p2 ) { int p; p = *p1; *p1 = *p2; *p2 = p; } // bit wise swap( int* p1,int* p2 ) { *p1 = *p1^*p2; *p2 = *p1^*p2; *p1 = *p1^*p2; } ``` ## C Operator Precedence ![](https://i.imgur.com/Yl7Q30M.png) - `++` > `*` - 元素位移 ```C int main() { char s[] = "0113256"; char *p = s; printf("%c", *p++); // s[1] 1 printf("%c", *(p++)); // s[2] 1 printf("%c", (*p)++); // s[2] 2 printf("%c", *++p); // s[3] 3 printf("%c", *(++p)); // s[4] 2 printf("%c", ++*p); // s[4] 3 printf("%c", ++(*p)); // s[4] 4 puts(p); // 0123456 return 0; } ``` ## Bitwise ### Arithmetic Right Shift - **無號數補0 有號數補1** ```C #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stdbool.h> #include <string.h> #include <unistd.h> uint32_t count_bit(uint32_t x) { uint32_t i = 0, count = 0; for (; i<sizeof(x)*8; i++) if((x>>i)&1) { printf("1"); count++; } else printf("0"); puts(""); return count; } uint32_t reverse_uint32_t(uint32_t x) { } int main() { int signed_X = 0xffffffff; unsigned int X = 0xffffffff; printf(" x = %u\n", X); count_bit(X); count_bit(X>>3); count_bit(X<<3); printf(" signed = %d \n", X); count_bit(signed_X); count_bit(signed_X>>3); count_bit(signed_X<<3); return 0; } ``` ```shell aidrifter@aidrifter-VM$ ./a.out x = 4294967295 11111111111111111111111111111111 11111111111111111111111111111000 00011111111111111111111111111111 signed = -1 11111111111111111111111111111111 11111111111111111111111111111111 00011111111111111111111111111111 ``` ### x 的補數-x 與 ~x - `-x = ~x + 1` ```shell 001000 // 8 111000 // -8 = -32 + 16 + 8 001001 // 9 110110 // -9 : -x = ~x + 1 110111 // (-2^5) + ((2^5 - 1) - 2^3) 111111 // -1 111011 // -1 + 4 100101 // -32 + 4 + 1 ``` ### Integer++ -- - `-x = ~x + 1` = `-x = ~x - (-1)` ```C // 注意:比直接加一和減一還要慢。 int add_one(int x) { return -~x; // ++x // x: 1001001 // ~x: 0110110 // -~x: 1001010 } int sub_one(int x) { return ~-x; // --x // x: 1001001 // -x: 0110111 // ~-x: 1001000 } ``` ### unsinged to signed ```C int negative(int x) { return ~x + 1; // -x; } int negative(int x) { return (x ^ -1) + 1; // -x; } ``` ### even or odd ```C bool is_odd(int x) { return x & 1; // x % 2; } ``` ### Absolute value ```C int absolute(int x) { return ((x>0)?x:-x); } int abs(int x) { // x < 0 ? -x : x; // x >> 31 = 111...111 (如果x是負數) or 000...000 (如果x是正數) // x ^ (x>>31) => 如果 x 為負數則將 x 的 0轉1, 1轉0, 如果 x 為正數, 則保持x 不變. // (x ^ (x >> 31)) - (x >> 31) => 如果 x 為正數則 x-0=x , 如果 x 為負數則 ~x-(-1) = -x return (x ^ (x >> 31)) - (x >> 31); } ``` ### 最低位的位元 ```X int lowest_bit_1(int x) { return x & -x; } ``` ### is 2 exponation ? ```C bool is_power_of_2(int x) { return (x & -x) == x; } ``` ### is it square ? - 1+3+5+7+....+(2*n-1)=n^2 ```C bool square(int x) { int i , sum =0 ; for (i=1;i<=2*n-1;i+=2) sum += i; return (sum==x*x)?true:false; } ``` ### swap ```C void swap(int *a, int *b) { *a = *a^*b *a = *a^*b *a = *a^*b } ``` ### How to compute number of 1 ```C uint32_t count_bit(uint32_t x) { uint32_t i = 0, count = 0; for (; i<sizeof(x)*8; i++) // or n!=0 if((x>>i)&1) count++; return count; } int count_bits2(unsigned int n) { int i=0; for ( ; n != 0; n >>= 1) if (n & 1) ++i; return i; } ``` ### reverse integer ```C uint32_t reverse_uint32_t(uint32_t x) { int i; int ret = 0; for (i=0; i < sizeof(x)*8; i++) ret |= ((x&(1<<i))>>i)<<(sizeof(x)*8-i-1); // better way // ret |= (x>>i&1)<<(sizeof(x)*8-i-1); return ret; } ``` Reference: C bitwise operation http://puremonkey2010.blogspot.com/2011/05/c-bitwise-operation.html ### Dangling pointer ```C { char *cp = NULL; /* ... */ { char c; cp = &c; } /* c falls out of scope */ /* cp is now a dangling pointer */ } ``` # OS ## processes vs threads ## process Reference: linux C 語言編程 https://www.byvoid.com/zht/blog/linux-c-1 Linux Processes – Process IDs, fork, execv, wait, waitpid C Functions https://www.thegeekstuff.com/2012/03/c-process-control-functions/ AIdrifter CS浮生筆錄: Multi thread https://hackmd.io/McOLfJwIRXWnJLKmiEgfww ## thread You need to add ` gcc pthread.c -lpthread` when compiler time to solve `undefined reference to "pthread_create"` ```C #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <sys/types.h> #define _GNU_SOURCE #include <unistd.h> #include <sys/syscall.h> #include <sys/types.h> #include <signal.h> #define gettid() syscall(SYS_gettid) void *print_message_function( void *ptr ); int main() { pthread_t thread1, thread2; const char *message1 = "Thread 1"; const char *message2 = "Thread 2"; int iret1, iret2; /* Create independent threads each of which will execute function */ iret1 = pthread_create( &thread1, NULL, print_message_function, (void*) message1); if(iret1) { fprintf(stderr,"Error - pthread_create() return code: %d\n",iret1); exit(EXIT_FAILURE); } iret2 = pthread_create( &thread2, NULL, print_message_function, (void*) message2); if(iret2) { fprintf(stderr,"Error - pthread_create() return code: %d\n",iret2); exit(EXIT_FAILURE); } printf("pthread_create() for thread 1 returns: %d\n",iret1); printf("pthread_create() for thread 2 returns: %d\n",iret2); /* Wait till threads are complete before main continues. Unless we */ /* wait we run the risk of executing an exit which will terminate */ /* the process and all threads before the threads have completed. */ pthread_join( thread1, NULL); pthread_join( thread2, NULL); exit(EXIT_SUCCESS); } void *print_message_function( void *ptr ) { char *message; message = (char *) ptr; printf("%s tid : %u pid : %u\n", message, gettid(), getpid()); sleep(10); } ``` ```shell aidrifter@ubuntu$ ./a.out pthread_create() for thread 1 returns: 0 pthread_create() for thread 2 returns: 0 Thread 1 tid : 51509 pid : 51508 Thread 2 tid : 51510 pid : 51508 ``` ### Deadlock 每一種資源都有一定的instances,像是可能有五個disk,不止一個的I/O devices,每一個 process 要利用資源都有以下三種階段 要求資源 使用資源 釋放資源 DeadLock Character - Mutual exclusion(互斥) - Hold & wait(持有並等待) (Partial Allocation) - No preemption(不可強取豪奪) - Circular waiting(循環等待) ### mutex - pthread - ### fork fork() Purpose is to create a new process, which becomes the child process of the caller Both processes will execute the next instruction following the fork() system call Two identical copies of the computer's address space,code, and stack are created one for parent and child. ### Memory layout ```C #define _BSD_SOURCE #include <stdio.h> #include <stdlib.h> char globBuf[65536]; /* Uninitialized data segment */ int primes[] = { 2, 3, 5, 7 }; /* Initialized data segment */ static int square(int x) /* Allocated in frame for square() */ { int result; /* Allocated in frame for square() */ result = x * x; return result; /* Return value passed via register */ } static void doCalc(int val) /* Allocated in frame for doCalc() */ { printf("The square of %d is %d\n", val, square(val)); if (val < 1000) { int t; /* Allocated in frame for doCalc() */ t = val * val * val; printf("The cube of %d is %d\n", val, t); } } int main(int argc, char *argv[]) /* Allocated in frame for main() */ { static int key = 9973; /* Initialized data segment */ static char mbuf[10240000]; /* Uninitialized data segment */ char *p; /* Allocated in frame for main() */ p = malloc(1024); /* Points to memory in heap segment */ doCalc(key); exit(EXIT_SUCCESS); } ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully