顯示具有 C語言 標籤的文章。 顯示所有文章
顯示具有 C語言 標籤的文章。 顯示所有文章

2020年8月28日 星期五

C 語言 - 正規表示法實作 ( regex.h )

regex.h (Linux 原生, Windows 再說)

    Regex.h 實作主要分 3 階段,regcom, regexec, regfree。
    // 要被批配的 buffer 跟一些參數
    int status, len, i;
    char buf[1024], data[1024];
    getdata(data);

    // 正規表示式的會要先 compile (regcomp())並儲存在 regex_t 此資料結構
    regex_t preg;

    // 設定批配模式的 flag. 
    // REG_EXTENDED ERE (Extended Regular Expression, 不用就是 BRE)
    // REG_ICASE    忽略大小寫
    // REG_NOSUB    不儲存批配後的結果
    // REG_NEWLINE  識別換行符(^$ 這類符號會成每一行的開頭跟結尾), REG_NEWLINE 效力 > eflags
    int cflags = REG_EXTENDED | REG_NEWLINE;

    // 正規表示式的批配樣板
    const char * regex = "Name: ([A-Z a-z]+)\nYear of Birth: ([^\n]*)\n([^:]*: [^\n]*)";

    // pmatch 為 struct 陣列去儲存批配後的結果
    // pmatch.rm_so 批配到的子字串在 target string 的起始 index
    // pmatch.rm_eo 批配到的子字串在 target string 的終止 index
    // nmatch 為宣告 pmatch 的陣列大小
    const size_t nmatch = 10;
    regmatch_t pmatch[nmatch];
    
    // eflags 也會對批配做改動
    // REG_NOTBOL   開頭批配符號(^)永遠批配不到
    // REG_NOTEOL   結尾批配符號($)永遠批配不到
    // REG_STARTEND 是直接使用 pmatch[0] 的 rm_so 跟 rm_eo
    //   作為字串頭尾的 index 然後批配,是為了避免 target string 
    //   中間有終止符(\0) 或 字串太長 strlen() 會 fail.
    int eflags = 0;

    // compile regex 
    if( regcomp(&preg, regex, cflags) != 0 ) {
        puts("regex compile error!\n");
        return;
    }

    // 進行批配 status = 0 代表成功
    status = regexec(&preg, data, nmatch, pmatch, eflags);
    if (status == REG_NOMATCH) {
        printf("No Match\n");
    } else if (status == 0) {
        // pmatch[0] 所指的子字串會是整串
        // pmatch[1], pmatch[2]... 會是括弧裡 sub regex,通常拿來取真正想要的值
        for (i = 0; i < nmatch && pmatch[i].rm_so >= 0; ++i) {
            len = pmatch[i].rm_eo - pmatch[i].rm_so;
            strncpy(buf, data + pmatch[i].rm_so, len);
            buf[len] = '\0';
            printf("match %d :\n%s\n\n", i+1, buf);
        }
    }

    regfree(&preg);
    return;

測試資料 跟 結果

    測試資料
Name: JunYe
Year of Birth: 1993
Gender: Male
    結果
match 1 :
Name: JunYe
Year of Birth: 1993
Gender: Male

match 2 :
JunYe

match 3 :
1993

match 4 :
Gender: Male

Basic (BRE) and extended (ERE) regular expression

    在一些特殊字元處理不一樣, 請參考 GNU的說明
參考資料 :

2020年5月24日 星期日

C/C++ - 左值、右值 ( lvalue、rvalue )

lvalue、rvalue 基本概念

左值 (lvalue) : 一個佔據某個特定記憶體的值。
右值 (rvalue) : 一個 expression 結束後就消失的值。
基本上這兩個定義包含了全部的值,非左即右,非右即左。
    int var = 4;    // var 參數佔據記憶體 = lvalue
    4 = var;        // 4 不佔據記憶體 = rvalue
    (var + 1) = 4;  // var + 1 也不佔據記憶體 = rvalue
    以上 2 跟 3 行會報錯,lvalue required as left operand of assignment。表示這個賦值程式碼錯在給不存在的記憶體空間賦值。所以只要是 lvalue 就能賦予值,即使是 function 也一樣,下面的 function 引用一個左值 ( globalvar ),所以可以賦予值。
    int globalvar = 20;

    int &foo() {
        return globalvar;
    }

    int main() {
        foo() = 10;
        std::cout << globalvar; // 10
        return 0;
    }

lvalue、rvalue 轉換


    左值能直接的轉換成右值,但右值卻不行,這時就會需要 ' * ' 。
    int a = 1;
    int b = a + 1;  // 這裡的 a 被直接視為右值
    int arr[] = {a, b};
    int *p = arr;
    *(p + 1) = 10;  // 利用 * 將右值 (p + 1) 傳換成左值


參考資料 :

2020年5月23日 星期六

C/C++ - Const

const 基本用途


    告知編譯器這個值為「常數」無法被修改。
  • 一般作用
        const int index = 5;
        index = 8; // error: assignment of read-only variable 'index'
  • 作用於 *
        // const pointer
        char * const constptr = initptr;
        *constptr = 1;              // 改變其 value OK
        constptr = otherptr;        // 改變其  addr Error
    
        // const value
        const char * constval = "const value";
        *constval = "nomal value";  // 改變其 value Error
        constval = otherptr;        // 改變其  addr OK
    
        // const pointer & const value
        const char * const consteverything = "ulti - const";

const 常見用途 : const&

在 C++ 可能常常看到 void function (type_Name const&);
"type_Name const&" 是可以寫成 "const type_Name &"
    int i = 10;
    const int &r = i;   // reference to const
    ++i;                // i 仍然可以修改
    ++r;                // r 不行 (expression must be a modifiable lvalue)
    通常追求效率的 C++ 工程師,會選擇 pass by reference 來防止程式做不必要的 Copy,這時 const 通常就會出現,來確保原始 value 不會被亂改。且 const 的出現可以使 call function 時,使用 rvalue。
    std::vector mySort(std::vector const &str)
    {
        std::vector r(str);
        std::sort(r.begin(), r.end());
        return r;
    }
    mySort({"abc", "def", "xyz"});

相關文章 :
C/C++ - 左值、右值 ( lvalue、rvalue )
讀書心得 - C++ Primer (5th Edition) - Chapter 2 (2)

參考資料 :

C/C++ - Function Pointer 應用 CallBack Function

Function Pointer 應用 CallBack Function


    有了 Function Pointer 就意味著,Callback Function 能被實現。    
Callback Function : 
A 跟 B 講 "看到 C 就打電話給我"
打電話給 A = Callback Function
    main.cpp ( A )
    int 
    main () {
        // 從 0 數到 10000000,中間請回報進度 % 數
        zeroTo(1000000, ShowPercentage);
        return 0;
    }
    zeroTo ( B ) 每數一個數字 ( 看到 C )
    void 
    zeroTo (size_t targetNumber, CallBackFuncPtr func) 
    {
        for (size_t i = 0; i < targetNumber; i++){
            func((i * 100) / targetNumber);
        }
    }
    ShowPercentage ( 打電話給 A )
    typedef void (*CallBackFuncPtr)(size_t);
    void ShowPercentage(size_t percentage)
    {
        std::cout << "\rProcessing :\t" << percentage << "%";
    }


相關文章 : 

2020年5月10日 星期日

C 語言 - CLI ( Command Line Interface ) 設計 (1)

CLI 命令列介面


    在設計 CLI 的程式時,最好的方法是遵從 IEEE Std 1003 ( POSIX ) 對 program 的 command-line options 之規範。所以用 getopt 去做 parse command-line 是最簡單的,有一點要注意的是 GNU 提供的 getopt 支援 " -- ",這個 POSIX 沒有規範。

常見的 Usage 設計

    Usage : 
        my_program tcp <host> <port> [--timeout=<seconds>]
        my_program serial <port> [--baud=9600] [--timeout=<seconds>]
        my_program (-h | --help)
    參數
    my_program   : program name
    tcp   : commands
    -h, --help   : options
    <host>   : arguments

    符號
    [ ]   : optional
    ( )   :  required
     |   :  mutually exclusive
    ...   :  repeating elements

Options 設計


    在設計長命名的 option ( " -- " ),最好需要有一個相對短命名的 option ( " - " ),example : -h, --help。

    每隻程式最好都要有 --help 跟 --version


參考資料 :

2020年5月3日 星期日

C 語言 - warning: left shift count >= width of type

warning: left shift count >= width of type

    一般來說,就是 shift 的 bit 大於資料型態的 bit 數。但有時使用 unsigned long 仍然會出錯,因為 unsigned long 會依照系統的不同,有時是 32 bit 有時是 64 bit,所以這時用 unsigned long long 較為安全 ( 保證 64 bit )。


參考資料 :
https://stackoverflow.com/questions/4201301/warning-left-shift-count-width-of-type

C 語言 - struct array 初始化 ( Initializing array of structures )

初始化 struct array

    struct student
    {
        char* name;
        int grade;
        int id;
    };

1. 依照當初宣告的順序

    struct student myStudents[] = {
        {"Henry", 3, 1},
        {"Marry", 3, 2}
    };

1. 依照當初宣告的名子

    struct student myStudents[] = {
        {.id = 1, .grade = 3, .name = "Henry"},
        {.id = 2, .grade = 3, .name = "Marry"}
    };

Output

  printf("ID : %d-%d\tName : %s\n", myStudents[0].grade, myStudents[0].id, myStudents[0].name);
  printf("ID : %d-%d\tName : %s\n", myStudents[1].grade, myStudents[1].id, myStudents[1].name);

  // ID : 3-1        Name : Henry
  // ID : 3-2        Name : Marry


參考資料 :
https://stackoverflow.com/questions/18921559/initializing-array-of-structures

2020年4月19日 星期日

C 語言 - function pointer 函數指標

function pointer 概念


    基本上有 data 的 pointer 就會有 function 的 pointer。
  • 正確的 ( 回傳 int 的 function )
  •     int (*pfi)();
  • 錯誤的 ( 回傳 int pointer 的 function )
  •     int *pfi();

function pointer 宣告


    利用 typedef,然後一樣用 & 取 function pointer
    extern int f1();
    
    typedef int (*funcptr)();
    funcptr pfi;
    pfi = &f1;
    實際上你可以省略 &,因為 function 若不呼叫,他就只能回傳 pointer ( 原文 : When you mention the name of a function but are not calling it, there's nothing else you could possibly be trying to do except generate a pointer to it. )
    pfi = f1;

function pointer 呼叫

  • 正確的 ( 回傳 int 的 function )
  •     int (*pfi)(arg1, arg2);
  • 錯誤的 ( 回傳 int pointer 的 function )
  •     int *pfi(arg1, arg2);
  • 省略的 ( 原理同上,function pointer 除了 Call function 只能 assign 另一 pointer 或比較其值,所以 Compiler 會將 pfi 視為 (*pfi)。) ( 原文 : There's nothing you can do with a function pointer except assign it to another function pointer, compare it to another function pointer, or call the function that it points to. )
  •     int pfi(arg1, arg2);

下一篇 : 

C 語言 - size_t 及各個常見類型

    typedef unsigned int size_t // 通常定義在 stddef.h

    int8     : -128 ~ 127
    int16    : -32768 ~ 32767
    int32    : -2147483648 ~ 2147483647
    int64    : -9223372036854775808 ~ 9223372036854775807
    
    uint8    : 0 ~ 255
    uint16   : 0 ~ 65535
    uint32   : 0 ~ 4294967295
    uint64   : 0 ~ 18446744073709551615

    uint8_t  : 0xFF
    uint16_t : 0xFFFF
    uint32_t : 0xFFFFFFFF
    uint64_t : 0xFFFFFFFFFFFFFFFF

C 語言 - malloc、free 與 calloc

malloc 跟 free


    一般來說要將函式結果回傳是不能用 pointer,因為一旦函式結束 stack 的空間就會被釋出,所以 pointer 會指向的資料是危險的。這時就會先動態地宣告一個位址給 pointer,但這類的記憶體會被存放在 heap 而非 stack,所以開發者必須自行釋放。
  • pointer.c
  •     void
        showMalloc(int pointerValue)
        {
            int *p = malloc(sizeof(int));
    
            printf("Address : %p\n", p);
            printf("Value   : %d\n", *p);
    
            *p = pointerValue;
    
            printf("Address : %p\n", p);
            printf("Value   : %d\n", *p);
            
            free(p);
        }
  • output
  •     Address : 00682B90
        Value   : 6826496
        Address : 00682B90
        Value   : 100

calloc


    會幫你初始值的 malloc
  • pointer.c
  •     void
        showMalloc(int pointerValue)
        {
            int *p = calloc(1, sizeof(int));
    
            printf("Address : %p\n", p);
            printf("Value   : %d\n", *p);
    
            *p = pointerValue;
    
            printf("Address : %p\n", p);
            printf("Value   : %d\n", *p);
    
            free(p);
        }
  • output
  •     Address : 00722B90
        Value   : 0
        Address : 00722B90
        Value   : 100

參考資料 :
https://openhome.cc/Gossip/CGossip/MallocFree.html

C 語言 - #ifndef

#ifndef 用途

  • 在 .h 檔確保只會被編譯一次
  •     #ifndef HELLO_H // 有些人會定義成 _HELLO_H_
        #define HELLO_H // 但目的就是不會被重複編譯
    
        #include <stdlib.h>
    
        int helloIntro(char** str);
        int main() __attribute__((weak));
    
        #endif

C 語言 - 編譯多個含有 main function 的 C code

編譯多個含有 main function 的 C code


    理論上是不行,GCC 無法自行繞過某某函式去編譯,通常是給條件去讓編譯器來達到 "只有一個你想要的 main function" 編譯目的。有人會問為什麼會有多個 main function,我想多半是因為想 Debug。

1. 利用 #ifdef

  • other.c
  •     #ifdef DEBUG
        int main ()
        {
            return 0;
        }
        #endif
  • gcc
  •     gcc -DEBUG other.c -o other.x

2. 利用 __attribute__((weak))

  • other.h ( __attribute__((weak)) 只能在宣告 function 時作 )
  •     int main() __attribute__((weak));
  • other.c
  •     int main ()
        {
            return 0;
        }

參考資料 :
https://stackoverflow.com/questions/35510670/compile-c-code-without-its-main-function

2020年4月14日 星期二

C 語言 - sprintf / snprintf

sprintf (char *s, const char *format, ...)

    C 語言並沒有其他語言方便的 container 去作字串轉換,所以 sprintf 就顯得強大許多。原理就是 printf 但輸入到字串。但會有 overflow 的 issue 產生。
    int main()
    {
        char str[5];
        sprintf(str, "ABC");
        printf("%s\n", str); // output : ABC
        return 0;
    }
    int main()
    {
        char str[5];
        sprintf(str, "ABCDEF");
        printf("%s\n", str); // output : ABCDEF
        return 0;
    }

snprintf (char *s, size_t maxlen, const char *format, ...)

    snprintf 多了一個參數去限制寫入長度(包含'\0'),安全許多。另外我是用 gcc 編譯的,snprintf 會回傳寫入字串的長度 (不包含'\0'),所以當回傳值大於等於 maxlen,則代表字串被切了。
    int main()
    {
        int ret;
        char str[5];
        ret = snprintf(str, 5, "ABC");
        printf("%s\n", str); // output : ABC
        printf("%d\n", ret); // output : 3
        return 0;
    }/code>
    int main()
    {
        int ret;
        char str[5];
        ret = snprintf(str, 5, "ABCDEF");
        printf("%s\n", str); // output : ABCD
        printf("%d\n", ret); // output : 6
        return 0;
    }


參考資料:
https://blog.xuite.net/tzeng015/twblog/113272245-snprintf
https://linux.die.net/man/3/snprintf

2020年4月13日 星期一

C 語言 - Linux System Log

Linux - System Log

    Linux 當事件發生會產生 log,通常會存在 /var/log 底下。優先度如下。
  •     debug
  •     info
  •     notice
  •     warning
  •     err
  •     crit
  •     alert
  •     emerg/panic

C 實作 - void syslog(int priority, const char *format, ...)

    #include<stdio.h>
    #include<stdlib.h>
    #include<syslog.h> 
    syslog(LOG_INFO, "hello %s", "woring");
    syslog(LOG_ERR,  "hello %s", "test");
底下為參數 int priority
LOG_USER : A miscellaneous user process
LOG_MAIL : Mail
LOG_DAEMON : A miscellaneous system daemon
LOG_AUTH : Security (authorization)
LOG_SYSLOG : Syslog
LOG_LPR : Central printer
LOG_NEWS : Network news (e.g. Usenet)
LOG_UUCP : UUCP
LOG_CRON : Cron and At
LOG_AUTHPRIV : Private security (authorization)
LOG_FTP : Ftp server
LOG_LOCAL(0~7) : Locally defined

參考資料 :
https://ithelp.ithome.com.tw/articles/10159084
http://yu-minspace.blogspot.com/2007/09/linux-syslogd-openlogsyslogdsyslog.html

2020年4月12日 星期日

C 語言 - __attribute__((weak))

使用的情境

    假設我們不知道外部是否會提供 foo() 這個 function,若沒有則用自己的 foo()。
    extern int func(void);
    int __attribute__((weak)) func(void)
    {
        return 0;
    }

備註 - 不會在動態函式庫 (.so) 中生效

    string.so
    void real_func()
    {
        printf("strong func\n");
    }
    weak.so
    void real_func() __attribute__((weak))
    {
        printf("weak func\n");
    }
    main.c
    extern void real_func();
    void main()
    {
        real_func();
    }
結果 :
    // 動態連結無效,只看順序
    gcc main.c -lstrong -lweak // 輸出 : strong func
    gcc main.c -lweak -lstrong // 輸出 : weak func

    // 正常 .o 檔
    gcc main.c weak.o strong.o // 輸出 : strong func
    gcc main.c strong.o weak.o // 輸出 : strong func


參考資料 :
https://www.itread01.com/content/1541531526.html

C 語言 - unsigned char

char 與 unsigned char 的區別

    單看兩個變數類型是沒有差別的, char -128 ~ 127, unsigned char 0 ~ 255,範圍大小一樣,並沒差別。唯一差別是在賦值的時候

賦值的時候 char 與 unsigned char 的區別

    (signed) char 有符號 bit ,這會導致你在賦予值時,(signed) char 會依照情況去賦予值。
    void unsignedcharTest(unsigned char value)
    {
        char c = value;
        unsigned char uc = value;
        int char_to_int = c;
        int unchar_to_int = uc;
        unsigned int char_to_unint = c;
        unsigned int unchar_to_unint = uc;
        printf("%%c: %c, %c\n", c, uc);
        printf("%%X: %X, %X\n", c, uc);
        printf("%%d: %d, %d\n", char_to_int, unchar_to_int);
        printf("%%u: %u, %u\n", char_to_unint, unchar_to_unint);
    }

    int main()
    {
        unsignedcharTest(0x7F); // 0111 1111 符號 bit 為 0 不會擴展
        unsignedcharTest(0x80); // 1000 0000 符號 bit 為 1 會擴展,補 1  
        return 0;
    }
    結果 :
    %c: , 
    %X: 7F, 7F
    %d: 127, 127
    %u: 127, 127
    %c: , 
    %X: FFFFFF80, 80
    %d: -128, 128
    %u: 4294967168, 128

參考資料 :
https://www.itread01.com/content/1547130625.html

C 語言 - static

static - Introduction

     靜態變數(Static Variable)在電腦編程領域指在程式執行前系統就為之靜態分配(也即在執行時中不再改變分配情況)儲存空間的一類變數。與之相對應的是在執行時只暫時存在的自動變數(即局部變數)與以動態分配方式取得儲存空間的一些物件,其中自動變數的儲存空間在呼叫棧上分配與釋放。

static + 變數

  
    (1) 宣告在 function 內 ( 記憶體模式 )
    這是比較經典的 Case,因為 Static 參數不會隨著 function 結束而消逝,所以可以用來計算 function 被呼叫的次數。
    #include "hello.h"

    int count(){
        static int count = 0;
        ++count;
        return count;
    }
    (2) 宣告在 function 外 ( 連結模式 ) 
    假設 a.h 跟 b.h 都設了 void foo() 這個函式,這樣你在 main.c 同時 include 時就會編譯錯誤。你有可能會說那我把 void foo() 個別放在 a.c 跟 b.c 就行,但編譯依然會錯誤。因為在連結 .o 檔時編譯器依然會發現。這時 static 就可避免此情況,有點類似 private 的概念,只有自己看得到就不會產生 ambiguous 的問題。


參考資料 :
https://zh.wikipedia.org/wiki/
https://medium.com/@alan81920/

Popular Posts