發表文章

目前顯示的是 2月, 2018的文章

[C] 每天來點字串用法 (5) - strcat()、strncat()

圖片
好的,不知道又過了幾天(廢),終於要來到第 5 篇了。 strcat()、strncat():串接字串   所屬標頭檔: <string.h>   函式宣告: char *strcat( char *dest, const char *src ); char *strncat( char *dest, const char *src, size_t count );   看到這熟悉的命名,該不會跟 strcpy()、strncpy() 那組函式很像吧?沒錯,所以按照上次的慣例,我們先來看看 strcat()。   strcat() 有兩個參數,分別是 dest 和 src,而這個函式的功用是將 src 接到 dest 後面,再回傳 dest 指向的字串。那你可能會問:那原本 dest 的 '\0' 字元會跑去哪呢?答案是會被 src 的第一個字元(也就是 src[0])所取代,並在最後面補上一個 '\0' 來當做新字串的結束字元。   看到名字多了一個 n 的函式,你可能會猜,是不是這個 strcat() 也會造成緩衝區溢位的問題呢?沒錯,所以接下來要介紹比較推薦的函式:strncat()。   如果有看過之前那一篇的話,應該都已經知道這個函式要怎麼用了,他會多接受一個整數,作為控制最多串接的字元數。不過這裡的機制跟 strcpy() 有點不一樣:           1) 無論如何都會在最後放一個 '\0',而這個 '\0' 並不受 count 的限制。 也就是說,真正串接字元數的最大值 其實是 count + 1 。   讓我們來看看他們的使用範例: #include <stdio.h> #include <string.h> int main(){ // strcat char s1[8] = "hi ", s2[8] = "sky"; strcat(s1, s2); printf("%s\n\n", s1); // strncat char s3[8], s4[8]; scanf("%s%s&q

[C] 每天來點字串用法 (4) - strchr()、strrchr()、strstr()

不知道這次有沒有拖到,blogger 自帶的時間搞得我好混亂啊ˊˇˋ 進入正題,今天要講的是: strchr()、strrchr():字串中搜尋字元   所屬標頭檔: <string.h>   函式宣告: char *strchr( const char *str, int ch ); char *strrchr( const char *str, int ch );   先傳入一個字串 str,再傳入一個字元 ch(雖然宣告裡它的型態是 int,但 char 本來就是用 ASCII 碼存的,所以傳入後會被當成整數來使用),如果有在 str 裡找到 ch,回傳第一個 ch 的所在位址;如果找不到,回傳 NULL。   而這兩個函式的差別在於:strchr()是從前面開始找 ch,而 strrchr() 則是從後面開始找。   這兩個函式還滿簡單的,比較需要注意的地方是:由於回傳的是第一個 ch 出現的地方,所以如果要繼續找第二、三...個的話,要從「回傳的位址 +1」開始。   另外,如果你想找 '\0' 這個字元的話也是可以的,反正一個字串一定會有一個 '\0' 嘛。   我們可以看看下面的範例:給定一個 DNA 序列,把其中的 'T' 全部換成 'U',使其成為合法的 RNA 序列。(轉錄什麼的不重要啦 XD ) #include <stdio.h> #include <string.h> int main(){ char s1[16]; scanf("%s", s1); // 可以輸入 ATAGCTACTG 看看 char *ptr = s1; while (ptr != NULL){ ptr = strchr(s1, 'T'); if (ptr){ *ptr = 'U'; // 取得或修改 ptr 指到的字元 } } printf("%s\n", s1); // 輸出 AUAGCUACUG return 0; }   這裡的

[C] 每天來點字串用法 (3) - from const char* to char*

這次沒有拖到欸d(`・∀・)b 今天帶來的不是函式,而是一個小技巧: 由 const char * 轉成 char *   有時候可能在將字串傳入函式時,會出現以下錯誤: error: invalid conversion from 'const char*' to 'char*' note: expected 'char *' but argument is of type 'const char *'   這是因為 const char* 沒辦法自動轉換成 char*,那麼基本上是沒救了啦,不過有一些方法可以繞過編譯器檢查或達到與轉換型別相同的效果:           1) 直接修改函式參數的型態定義,但原本函式庫裡的宣告根本不能改。           2) 用 const_cast<char*>(cptr),這個雖然可以強制轉換,但若透過轉換後的指標更改常數的值,將會是 undefined behavior。           3) 使用上一篇提到的 strcpy(),但小心 緩衝區覆蓋 ,或是使用到不知道指到什麼的指標。   下面這裡提供 2) 和 3) 的使用範例: #include <iostream> #include <cstring> #include <string> using namespace std; int main(){ string str("hello world"); cout << "(before) str: " << str << endl; char s[16]; char *ptr1, *ptr2 = s; // ptr1 = str.c_str(); error: c_str() 回傳 const char* ptr1 = const_cast<char*>(str.c_str()); ptr1[2] = 'r'; // undefined behavior cout << &

[演算法] [C++ / Python] 當 DFS 遇上排列

圖片
之前 說過,DFS 是樹的一種走訪方式,而我們也可以將他應用在「排列」上。 剪刀、石頭、布!-全部排列   現在有三個人:甲、乙、丙在猜拳,已知他們會出完全不同的拳,而你想知道依照甲、乙、丙的順序,他們出拳的排列有哪幾種的話,就可以用 DFS 來算喔!   ...蛤?用 DFS?這不是樹的走訪嗎?管他的,先上程式碼! C++ string gesture[3] = {"剪刀", "石頭", "布"}; bool visited[3] = {false}; string arrangement[3]; void dfs(int layer){ if (layer == 3){ for (int i = 0; i < 3; i++){ cout << arrangement[i] << "\t"; } cout << endl; return; } for (int i = 0; i < 3; i++){ if (visited[i]){ continue; } visited[i] = true; arrangement[layer] = gesture[i]; dfs(layer + 1); visited[i] = false; } } Python gesture = ['剪刀', '石頭', '布'] visited = [False] * 3 arrangement = [''] * 3 def dfs(layer): if layer == 3: print(*arrangement, sep='\t\t') return for i in range(3): if visited[i]:

[C] 每天來點字串用法 (2) - strcpy()、strncpy()

圖片
結果隔了四天(不要相信 blogger 自帶的時間(?))才更新qwq,前幾天根本忘得一乾二淨XD 進入正文吧,今天要介紹的是: strcpy()、strncpy():字串複製   所屬標頭檔: <string.h>   函式宣告: char *strcpy( char *dest, const char *src ); char *strncpy( char *dest, const char *src, size_t count );   先說 strcpy(),將來源字串 (src) 複製到 目的地 (dest),並回傳 dest 指向的字串,要注意的有以下兩點:           1) 第一個參數是目的地 (dest),第二個是來源 (src)           2) 會有 緩衝區溢位 (buffer overflow)   的問題   來看看何謂 緩衝區溢位:假設有一程式進行了如下宣告: int i = 5; char s[8] = "Hi 1234";   那麼這些變數的記憶體配置可能如下:   如果今天我們進行了如下操作: strcpy(s, "hello sky");   那麼記憶體裡的內容就會變成如下:   於是這時 i 的值就會變成 121,我們可以用以下程式來驗證: #include <stdio.h> #include <string.h> int main(void){ int i = 5; char s[8] = "Hi 1234"; printf("address of i: %p\naddress of s: %p\n", &i, s); // i: 0028ff1c, s: 0028ff14 strcpy(s, "hello sky"); printf("value of s: %s\nvalue of i: %d\n", s, i); // s: hello sky, : 121 return 0; }   為了解決這樣的問題,我們可以改用 strncp

[C] 每天來點字串用法 (1) - strlen()

第一天就來個比較簡單又實用的函式好了,不然我怕我堅持不下去。(誤 strlen():字串長度   所屬標頭檔: <string.h>   函式宣告: size_t strlen( const char *str );   傳入一個字串 str,回傳這個字串的長度,而 '\0' 不計入長度(但 '\n' 計入)。其中的回傳值型態 size_t 通常是無號整數型別。   以下這個程式示範了 strlen() 的用法: #include <stdio.h> int main(){ char s1[16], s2[16]; char *s3 = s2; char s4[] = "happy new year\n"; scanf("%s", s1); printf("s1: %d\n", strlen(s1)); scanf("%s", s3); printf("s3: %d\n", strlen(s3)); printf("s4: %d\n", strlen(s4)); return 0; }   嗯,這個 #include 的配色好怪喔 (X   藉由 s4 可以得知,空格和換行字元都計入長度。 之後應該每兩天(說好的每天呢)會更新一篇文章,而每五篇我會把它統合在一篇內,以免我的 blogger 太雜亂。 參考資料:   1)  cppreference - strlen   2) 我的腦袋

[C] 每天來點字串用法 (基本篇)

有感於 C 的字元陣列實在有夠難(只是你廢),所以決定每天搞懂一點關於它的性質或用法之類的,希望我能夠持之以恆。 基本使用   首先,C 的字串指的其實是字元陣列,而字元陣列的使用通常有以下幾種: char s1[128] = "hello world"; char s2[] = "hello world";   另外,許多函式都會使用字元指標來操作字串: char *s3 = s1; // 相當於 char *s3 = &s1[0]   而對於字元指標的宣告,C 有一個特別的地方,他可以直接讓字元指標類的字串有初始值: char *s4 = "hello world";   像 s4 這種情況,是由編譯器在 唯讀 記憶體中放入 "hello world" 這個字元陣列,再讓 s4 這個指標指向它。因為是唯讀的 記憶體 ,所以我們不能更改這個字串的內容,但可以更改 s4 指向的位址。   接著讓我們看看字串的輸出: char s1[128] = "hello world"; printf("%s\n", s1); // hello world char s2[] = "hello world"; printf("%s\n", s2); // hello world char *s3 = s1; printf("%s\n", s3); // hello world char *s4 = "hello world"; printf("%s\n", s4); // hello world 關於 '\0' 字元   說到 C 的字串,絕對不能不提到 '\0' 這個字元,不要看它有兩個字('\' 和 '0'),其實它是一個單一的字元,就像換行字元 '\n' 一樣。   他的功用是:標記出字串的結束。看到我們上面輸出中的 s1 字串,我們明明就宣告了 128 個空間,但為什麼輸出結果只有 11 個字元?剩下