一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

1.?字符串操作函數(shù)

 BeautymengRoom 2011-07-08

1. 字符串操作函數(shù) 請(qǐng)點(diǎn)評(píng)

程序按功能劃分可分為數(shù)值運(yùn)算、符號(hào)處理和I/O操作三類,符號(hào)處理程序占相當(dāng)大的比例,符號(hào)處理程序無處不在,編譯器、瀏覽器、Office套件等程序的主要功能都是符號(hào)處理。無論多復(fù)雜的符號(hào)處理都是由各種基本的字符串操作組成的,本節(jié)介紹如何用C語言的庫函數(shù)做字符串初始化、取長度、拷貝、連接、比較、搜索等基本操作。

1.1. 初始化字符串 請(qǐng)點(diǎn)評(píng)

#include <string.h>
void *memset(void *s, int c, size_t n);
返回值:s指向哪,返回的指針就指向哪

memset函數(shù)把s所指的內(nèi)存地址開始的n個(gè)字節(jié)都填充為c的值。通常c的值為0,把一塊內(nèi)存區(qū)清零。例如定義char buf[10];,如果它是全局變量或靜態(tài)變量,則自動(dòng)初始化為0(位于.bss段),如果它是函數(shù)的局部變量,則初值不確定,可以用memset(buf, 0, 10)清零,由malloc分配的內(nèi)存初值也是不確定的,也可以用memset清零。

1.2. 取字符串的長度 請(qǐng)點(diǎn)評(píng)

#include <string.h>
size_t strlen(const char *s);
返回值:字符串的長度

strlen函數(shù)返回s所指的字符串的長度。該函數(shù)從s所指的第一個(gè)字符開始找'\0'字符,一旦找到就返回,返回的長度不包括'\0'字符在內(nèi)。例如定義char buf[] = "hello";,則strlen(buf)的值是5,但要注意,如果定義char buf[5] = "hello";,則調(diào)用strlen(buf)是危險(xiǎn)的,會(huì)造成數(shù)組訪問越界。

1.3. 拷貝字符串 請(qǐng)點(diǎn)評(píng)

第 1 節(jié) “本章的預(yù)備知識(shí)”中介紹了strcpystrncpy函數(shù),拷貝以'\0'結(jié)尾的字符串,strncpy還帶一個(gè)參數(shù)指定最多拷貝多少個(gè)字節(jié),此外,strncpy并不保證緩沖區(qū)以'\0'結(jié)尾。現(xiàn)在介紹memcpymemmove函數(shù)。

#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n);
返回值:dest指向哪,返回的指針就指向哪

memcpy函數(shù)從src所指的內(nèi)存地址拷貝n個(gè)字節(jié)到dest所指的內(nèi)存地址,和strncpy不同,memcpy并不是遇到'\0'就結(jié)束,而是一定會(huì)拷貝完n個(gè)字節(jié)。這里的命名規(guī)律是,以str開頭的函數(shù)處理以'\0'結(jié)尾的字符串,而以mem開頭的函數(shù)則不關(guān)心'\0'字符,或者說這些函數(shù)并不把參數(shù)當(dāng)字符串看待,因此參數(shù)的指針類型是void *而非char *。

memmove也是從src所指的內(nèi)存地址拷貝n個(gè)字節(jié)到dest所指的內(nèi)存地址,雖然叫move但其實(shí)也是拷貝而非移動(dòng)。但是和memcpy有一點(diǎn)不同,memcpy的兩個(gè)參數(shù)srcdest所指的內(nèi)存區(qū)間如果重疊則無法保證正確拷貝,而memmove卻可以正確拷貝。假設(shè)定義了一個(gè)數(shù)組char buf[20] = "hello world\n";,如果想把其中的字符串往后移動(dòng)一個(gè)字節(jié)(變成"hhello world\n"),調(diào)用memcpy(buf + 1, buf, 13)是無法保證正確拷貝的:

例 25.1. 錯(cuò)誤的memcpy調(diào)用

#include <stdio.h>
#include <string.h>
int main(void)
{
char buf[20] = "hello world\n";
memcpy(buf + 1, buf, 13);
printf("%s", buf);
return 0;
}

在我的機(jī)器上運(yùn)行的結(jié)果是hhhllooworrd。如果把代碼中的memcpy改成memmove則可以保證正確拷貝。memmove可以這樣實(shí)現(xiàn):

void *memmove(void *dest, const void *src, size_t n)
{
char temp[n];
int i;
char *d = dest;
const char *s = src;
for (i = 0; i < n; i++)
temp[i] = s[i];
for (i = 0; i < n; i++)
d[i] = temp[i];
return dest;
}

借助于一個(gè)臨時(shí)緩沖區(qū)temp,即使srcdest所指的內(nèi)存區(qū)間有重疊也能正確拷貝。思考一下,如果不借助于臨時(shí)緩沖區(qū)能不能正確處理重疊內(nèi)存區(qū)間的拷貝?

memcpy如果得到的結(jié)果是hhhhhhhhhhhhhh倒不奇怪,可為什么會(huì)得到hhhllooworrd這個(gè)奇怪的結(jié)果呢?根據(jù)這個(gè)結(jié)果猜測(cè)的一種可能的實(shí)現(xiàn)是:

void *memcpy(void *dest, const void *src, size_t n)
{
char *d = dest;
const char *s = src;
int *di;
const int *si;
int r = n % 4;
while (r--)
*d++ = *s++;
di = (int *)d;
si = (const int *)s;
n /= 4;
while (n--)
*di++ = *si++;
return dest;
}

在32位的x86平臺(tái)上,每次拷貝1個(gè)字節(jié)需要一條指令,每次拷貝4個(gè)字節(jié)也只需要一條指令,為了提高拷貝的效率,我們先處理完零頭然后4個(gè)字節(jié)4個(gè)字節(jié)地拷貝。注意這個(gè)實(shí)現(xiàn)并不正確,把void *指針轉(zhuǎn)成int *指針來訪問應(yīng)該考慮對(duì)齊的問題,請(qǐng)讀者自己實(shí)現(xiàn)一個(gè)更完善的版本。

C99的restrict關(guān)鍵字 請(qǐng)點(diǎn)評(píng)

我們來看一個(gè)跟memcpy/memmove類似的問題。下面的函數(shù)將兩個(gè)數(shù)組中對(duì)應(yīng)的元素相加,結(jié)果保存在第三個(gè)數(shù)組中。

void vector_add(const double *x, const double *y, double *result)
{
int i;
for (i = 0; i < 64; ++i)
result[i] = x[i] + y[i];
}

如果這個(gè)函數(shù)要在多處理器的計(jì)算機(jī)上執(zhí)行,編譯器可以做這樣的優(yōu)化:把這一個(gè)循環(huán)拆成兩個(gè)循環(huán),一個(gè)處理器計(jì)算i值從0到31的循環(huán),另一個(gè)處理器計(jì)算i值從32到63的循環(huán),這樣兩個(gè)處理器可以同時(shí)工作,使計(jì)算時(shí)間縮短一半。但是這樣的編譯優(yōu)化能保證得出正確結(jié)果嗎?假如resultx所指的內(nèi)存區(qū)間是重疊的,result[0]其實(shí)是x[1]result[i]其實(shí)是x[i+1],這兩個(gè)處理器就不能各干各的事情了,因?yàn)榈诙€(gè)處理器的工作依賴于第一個(gè)處理器的最終計(jì)算結(jié)果,這種情況下編譯優(yōu)化的結(jié)果是錯(cuò)的。這樣看來編譯器是不敢隨便做優(yōu)化了,那么多處理器提供的并行性就無法利用,豈不可惜?為此,C99引入restrict關(guān)鍵字,如果程序員把上面的函數(shù)聲明為void vector_add(const double *restrict x, const double *restrict y, double *restrict result),就是告訴編譯器可以放心地對(duì)這個(gè)函數(shù)做優(yōu)化,程序員自己會(huì)保證這些指針?biāo)傅膬?nèi)存區(qū)間互不重疊。

由于restrict是C99引入的新關(guān)鍵字,目前Linux的Man Page還沒有更新,所以都沒有restrict關(guān)鍵字,本書的函數(shù)原型都取自Man Page,所以也都沒有restrict關(guān)鍵字。但在C99標(biāo)準(zhǔn)中庫函數(shù)的原型都在必要的地方加了restrict關(guān)鍵字,在C99中memcpy的原型是void *memcpy(void * restrict s1, const void * restrict s2, size_t n);,就是告訴調(diào)用者,這個(gè)函數(shù)的實(shí)現(xiàn)可能會(huì)做些優(yōu)化,編譯器也可能會(huì)做些優(yōu)化,傳進(jìn)來的指針不允許指向重疊的內(nèi)存區(qū)間,否則結(jié)果可能是錯(cuò)的,而memmove的原型是void *memmove(void *s1, const void *s2, size_t n);,沒有restrict關(guān)鍵字,說明傳給這個(gè)函數(shù)的指針允許指向重疊的內(nèi)存區(qū)間。在restrict關(guān)鍵字出現(xiàn)之前都是用自然語言描述哪些函數(shù)的參數(shù)不允許指向重疊的內(nèi)存區(qū)間,例如在C89標(biāo)準(zhǔn)的庫函數(shù)一章開頭提到,本章描述的所有函數(shù),除非特別說明,都不應(yīng)該接收兩個(gè)指針參數(shù)指向重疊的內(nèi)存區(qū)間,例如調(diào)用sprintf時(shí)傳進(jìn)來的格式化字符串和結(jié)果字符串的首地址相同,諸如此類的調(diào)用都是非法的。本書也遵循這一慣例,除非像memmove這樣特別說明之外,都表示“不允許”。

關(guān)于restrict關(guān)鍵字更詳細(xì)的解釋可以參考[BeganFORTRAN]

字符串的拷貝也可以用strdup(3)函數(shù),這個(gè)函數(shù)不屬于C標(biāo)準(zhǔn)庫,是POSIX標(biāo)準(zhǔn)中定義的,POSIX標(biāo)準(zhǔn)定義了UNIX系統(tǒng)的各種接口,包含C標(biāo)準(zhǔn)庫的所有函數(shù)和很多其它的系統(tǒng)函數(shù),在第 2 節(jié) “C標(biāo)準(zhǔn)I/O庫函數(shù)與Unbuffered I/O函數(shù)”將詳細(xì)介紹POSIX標(biāo)準(zhǔn)。

#include <string.h>
char *strdup(const char *s);
返回值:指向新分配的字符串

這個(gè)函數(shù)調(diào)用malloc動(dòng)態(tài)分配內(nèi)存,把字符串s拷貝到新分配的內(nèi)存中然后返回。用這個(gè)函數(shù)省去了事先為新字符串分配內(nèi)存的麻煩,但是用完之后要記得調(diào)用free釋放新字符串的內(nèi)存。

1.4. 連接字符串 請(qǐng)點(diǎn)評(píng)

#include <string.h>
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);
返回值:dest指向哪,返回的指針就指向哪

strcatsrc所指的字符串連接到dest所指的字符串后面,例如:

char d[10] = "foo";
char s[10] = "bar";
strcat(d, s);
printf("%s %s\n", d, s);

調(diào)用strcat函數(shù)后,緩沖區(qū)s的內(nèi)容沒變,緩沖區(qū)d中保存著字符串"foobar",注意原來"foo"后面的'\0'被連接上來的字符串"bar"覆蓋掉了,"bar"后面的'\0'仍保留。

strcatstrcpy有同樣的問題,調(diào)用者必須確保dest緩沖區(qū)足夠大,否則會(huì)導(dǎo)致緩沖區(qū)溢出錯(cuò)誤。strncat函數(shù)通過參數(shù)n指定一個(gè)長度,就可以避免緩沖區(qū)溢出錯(cuò)誤。注意這個(gè)參數(shù)n的含義和strncpy的參數(shù)n不同,它并不是緩沖區(qū)dest的長度,而是表示最多從src緩沖區(qū)中取n個(gè)字符(不包括結(jié)尾的'\0')連接到dest后面。如果src中前n個(gè)字符沒有出現(xiàn)'\0',則取前n個(gè)字符再加一個(gè)'\0'連接到dest后面,所以strncat總是保證dest緩沖區(qū)以'\0'結(jié)尾,這一點(diǎn)又和strncpy不同,strncpy并不保證dest緩沖區(qū)以'\0'結(jié)尾。所以,提供給strncat函數(shù)的dest緩沖區(qū)的大小至少應(yīng)該是strlen(dest)+n+1個(gè)字節(jié),才能保證不溢出。

1.5. 比較字符串 請(qǐng)點(diǎn)評(píng)

#include <string.h>
int memcmp(const void *s1, const void *s2, size_t n);
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
返回值:負(fù)值表示s1小于s2,0表示s1等于s2,正值表示s1大于s2

memcmp從前到后逐個(gè)比較緩沖區(qū)s1s2的前n個(gè)字節(jié)(不管里面有沒有'\0'),如果s1s2的前n個(gè)字節(jié)全都一樣就返回0,如果遇到不一樣的字節(jié),s1的字節(jié)比s2小就返回負(fù)值,s1的字節(jié)比s2大就返回正值。

strcmps1s2當(dāng)字符串比較,在其中一個(gè)字符串中遇到'\0'時(shí)結(jié)束,按照上面的比較準(zhǔn)則,"ABC""abc"小,"ABCD""ABC"大,"123A9""123B2"小。

strncmp的比較結(jié)束條件是:要么在其中一個(gè)字符串中遇到'\0'結(jié)束(類似于strcmp),要么比較完n個(gè)字符結(jié)束(類似于memcmp)。例如,strncmp("ABCD", "ABC", 3)的返回值是0,strncmp("ABCD", "ABC", 4)的返回值是正值。

#include <strings.h>
int strcasecmp(const char *s1, const char *s2);
int strncasecmp(const char *s1, const char *s2, size_t n);
返回值:負(fù)值表示s1小于s2,0表示s1等于s2,正值表示s1大于s2

這兩個(gè)函數(shù)和strcmp/strncmp類似,但在比較過程中忽略大小寫,大寫字母A和小寫字母a認(rèn)為是相等的。這兩個(gè)函數(shù)不屬于C標(biāo)準(zhǔn)庫,是POSIX標(biāo)準(zhǔn)中定義的。

1.6. 搜索字符串 請(qǐng)點(diǎn)評(píng)

#include <string.h>
char *strchr(const char *s, int c);
char *strrchr(const char *s, int c);
返回值:如果找到字符c,返回字符串s中指向字符c的指針,如果找不到就返回NULL

strchr在字符串s中從前到后查找字符c,找到字符c第一次出現(xiàn)的位置時(shí)就返回,返回值指向這個(gè)位置,如果找不到字符c就返回NULL。strrchrstrchr類似,但是從右向左找字符c,找到字符c第一次出現(xiàn)的位置就返回,函數(shù)名中間多了一個(gè)字母r可以理解為Reverse。

#include <string.h>
char *strstr(const char *haystack, const char *needle);
返回值:如果找到子串,返回值指向子串的開頭,如果找不到就返回NULL

strstr在一個(gè)長字符串中從前到后找一個(gè)子串(Substring),找到子串第一次出現(xiàn)的位置就返回,返回值指向子串的開頭,如果找不到就返回NULL。這兩個(gè)參數(shù)名很形象,在干草堆haystack中找一根針needle,按中文的說法叫大海撈針,顯然haystack是長字符串,needle是要找的子串。

搜索子串有一個(gè)顯而易見的算法,可以用兩層的循環(huán),外層循環(huán)把haystack中的每一個(gè)字符的位置依次假定為子串的開頭,內(nèi)層循環(huán)從這個(gè)位置開始逐個(gè)比較haystackneedle的每個(gè)字符是否相同。想想這個(gè)算法最多需要做多少次比較?其實(shí)有比這個(gè)算法高效得多的算法,有興趣的讀者可以參考[算法導(dǎo)論]

1.7. 分割字符串 請(qǐng)點(diǎn)評(píng)

很多文件格式或協(xié)議格式中會(huì)規(guī)定一些分隔符或者叫界定符(Delimiter),例如/etc/passwd文件中保存著系統(tǒng)的賬號(hào)信息:

$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
...

每條記錄占一行,也就是說記錄之間的分隔符是換行符,每條記錄又由若干個(gè)字段組成,這些字段包括用戶名、密碼、用戶id、組id、個(gè)人信息、主目錄、登錄Shell,字段之間的分隔符是:號(hào)。解析這樣的字符串需要根據(jù)分隔符把字符串分割成幾段,C標(biāo)準(zhǔn)庫提供的strtok函數(shù)可以很方便地完成分割字符串的操作。tok是Token的縮寫,分割出來的每一段字符串稱為一個(gè)Token。

#include <string.h>
char *strtok(char *str, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr);
返回值:返回指向下一個(gè)Token的指針,如果沒有下一個(gè)Token了就返回NULL

參數(shù)str是待分割的字符串,delim是分隔符,可以指定一個(gè)或多個(gè)分隔符,strtok遇到其中任何一個(gè)分隔符就會(huì)分割字符串??聪旅娴睦?。

例 25.2. strtok

#include <stdio.h>
#include <string.h>
int main(void)
{
char str[] = "root:x::0:root:/root:/bin/bash:";
char *token;
token = strtok(str, ":");
printf("%s\n", token);
while ( (token = strtok(NULL, ":")) != NULL)
printf("%s\n", token);
return 0;
}

$ ./a.out
root
x
0
root
/root
/bin/bash

結(jié)合這個(gè)例子,strtok的行為可以這樣理解:冒號(hào)是分隔符,把"root:x::0:root:/root:/bin/bash:"這個(gè)字符串分隔成"root"、"x"、"""0"、"root""/root"、"/bin/bash"、""等幾個(gè)Token,但空字符串的Token被忽略。第一次調(diào)用要把字符串首地址傳給strtok的第一個(gè)參數(shù),以后每次調(diào)用第一個(gè)參數(shù)只要傳NULL就可以了,strtok函數(shù)自己會(huì)記住上次處理到字符串的什么位置(顯然這是通過strtok函數(shù)中的一個(gè)靜態(tài)指針變量記住的)。

gdb跟蹤這個(gè)程序,會(huì)發(fā)現(xiàn)str字符串被strtok不斷修改,每次調(diào)用strtokstr中的一個(gè)分隔符改成'\0',分割出一個(gè)小字符串,并返回這個(gè)小字符串的首地址。

(gdb) start
Breakpoint 1 at 0x8048415: file main.c, line 5.
Starting program: /home/akaedu/a.out
main () at main.c:5
5	{
(gdb) n
6		char str[] = "root:x::0:root:/root:/bin/bash:";
(gdb)
9		token = strtok(str, ":");
(gdb) display str
1: str = "root:x::0:root:/root:/bin/bash:"
(gdb) n
10		printf("%s\n", token);
1: str = "root\000x::0:root:/root:/bin/bash:"
(gdb)
root
11		while ( (token = strtok(NULL, ":")) != NULL)
1: str = "root\000x::0:root:/root:/bin/bash:"
(gdb)
12			printf("%s\n", token);
1: str = "root\000x\000:0:root:/root:/bin/bash:"
(gdb)
x
11		while ( (token = strtok(NULL, ":")) != NULL)
1: str = "root\000x\000:0:root:/root:/bin/bash:"

剛才提到在strtok函數(shù)中應(yīng)該有一個(gè)靜態(tài)指針變量記住上次處理到字符串中的什么位置,所以不需要每次調(diào)用時(shí)都把字符串中的當(dāng)前處理位置傳給strtok,但是在函數(shù)中使用靜態(tài)變量是不好的,以后會(huì)講到這樣的函數(shù)是不可重入的。strtok_r函數(shù)則不存在這個(gè)問題,它的內(nèi)部沒有靜態(tài)變量,調(diào)用者需要自己分配一個(gè)指針變量來維護(hù)字符串中的當(dāng)前處理位置,每次調(diào)用時(shí)把這個(gè)指針變量的地址傳給strtok_r的第三個(gè)參數(shù),告訴strtok_r從哪里開始處理,strtok_r返回時(shí)再把新的處理位置寫回到這個(gè)指針變量中(這是一個(gè)Value-result參數(shù))。strtok_r末尾的r就表示可重入(Reentrant),這個(gè)函數(shù)不屬于C標(biāo)準(zhǔn)庫,是在POSIX標(biāo)準(zhǔn)中定義的。關(guān)于strtok_r的用法Man Page上有一個(gè)很好的例子:

例 25.3. strtok_r

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *str1, *str2, *token, *subtoken;
char *saveptr1, *saveptr2;
int j;
if (argc != 4) {
fprintf(stderr, "Usage: %s string delim subdelim\n",
argv[0]);
exit(EXIT_FAILURE);
}
for (j = 1, str1 = argv[1]; ; j++, str1 = NULL) {
token = strtok_r(str1, argv[2], &saveptr1);
if (token == NULL)
break;
printf("%d: %s\n", j, token);
for (str2 = token; ; str2 = NULL) {
subtoken = strtok_r(str2, argv[3], &saveptr2);
if (subtoken == NULL)
break;
printf(" --> %s\n", subtoken);
}
}
exit(EXIT_SUCCESS);
}

$ ./a.out 'a/bbb///cc;xxx:yyy:' ':;' '/'
1: a/bbb///cc
--> a
--> bbb
--> cc
2: xxx
--> xxx
3: yyy
--> yyy

a/bbb///cc;xxx:yyy:這個(gè)字符串有兩級(jí)分隔符,一級(jí)分隔符是:號(hào)或;號(hào),把這個(gè)字符串分割成a/bbb///cc、xxxyyy三個(gè)子串,二級(jí)分隔符是/,只有第一個(gè)子串中有二級(jí)分隔符,它被進(jìn)一步分割成abbb、cc三個(gè)子串。由于strtok_r不使用靜態(tài)變量,而是要求調(diào)用者自己保存字符串的當(dāng)前處理位置,所以這個(gè)例子可以在按一級(jí)分隔符分割整個(gè)字符串的過程中穿插著用二級(jí)分隔符分割其中的每個(gè)子串。建議讀者用gdbdisplay命令跟蹤argv[1]、saveptr1saveptr2,以理解strtok_r函數(shù)的工作方式。

Man Page的BUGS部分指出了用strtokstrtok_r函數(shù)需要注意的問題:

  • 這兩個(gè)函數(shù)要改寫字符串以達(dá)到分割的效果

  • 這兩個(gè)函數(shù)不能用于常量字符串,因?yàn)樵噲D改寫.rodata段會(huì)產(chǎn)生段錯(cuò)誤

  • 在做了分割之后,字符串中的分隔符就被'\0'覆蓋了

  • strtok函數(shù)使用了靜態(tài)變量,它不是線程安全的,必要時(shí)應(yīng)該用可重入的strtok_r函數(shù),以后再詳細(xì)介紹“可重入”和“線程安全”這兩個(gè)概念

1、出于練習(xí)的目的,strtokstrtok_r函數(shù)非常值得自己動(dòng)手實(shí)現(xiàn)一遍,在這個(gè)過程中不僅可以更深刻地理解這兩個(gè)函數(shù)的工作原理,也為以后理解“可重入”和“線程安全”這兩個(gè)重要概念打下基礎(chǔ)。

2、解析URL中的路徑和查詢字符串。動(dòng)態(tài)網(wǎng)頁的URL末尾通常帶有查詢,例如:

http://www.google.cn/search?complete=1&hl=zh-CN&ie=GB2312&q=linux&meta=
http://www.baidu.com/s?wd=linux&cl=3

比如上面第一個(gè)例子,http://www.google.cn/search是路徑部分,?號(hào)后面的complete=1&hl=zh-CN&ie=GB2312&q=linux&meta=是查詢字符串,由五個(gè)“key=value”形式的鍵值對(duì)(Key-value Pair)組成,以&隔開,有些鍵對(duì)應(yīng)的值可能是空字符串,比如這個(gè)例子中的鍵meta。

現(xiàn)在要求實(shí)現(xiàn)一個(gè)函數(shù),傳入一個(gè)帶查詢字符串的URL,首先檢查輸入格式的合法性,然后對(duì)URL進(jìn)行切分,將路徑部分和各鍵值對(duì)分別傳出,請(qǐng)仔細(xì)設(shè)計(jì)函數(shù)接口以便傳出這些字符串。如果函數(shù)中有動(dòng)態(tài)分配內(nèi)存的操作,還要另外實(shí)現(xiàn)一個(gè)釋放內(nèi)存的函數(shù)。完成之后,為自己設(shè)計(jì)的函數(shù)寫一個(gè)Man Page

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    精品久久av一二三区| 国产av一区二区三区麻豆| 欧洲亚洲精品自拍偷拍| 欧美成人久久久免费播放| 亚洲国产精品久久琪琪| 欧美日韩免费观看视频| 亚洲精品小视频在线观看| 日本不卡一本二本三区| 女生更色还是男生更色| 在线观看欧美视频一区| 日本加勒比在线播放一区| 青青久久亚洲婷婷中文网| 久久亚洲精品成人国产| 国产免费成人激情视频| 伊人网免费在线观看高清版| 日韩精品亚洲精品国产精品| 色婷婷成人精品综合一区| 欧美人与动牲交a精品| 中文字幕亚洲精品人妻| 一区二区三区在线不卡免费| 又色又爽又无遮挡的视频| 粉嫩国产一区二区三区在线| 国产欧美日本在线播放| 91一区国产中文字幕| 黑人粗大一区二区三区| 免费在线观看欧美喷水黄片 | 91人妻人人精品人人爽| 经典欧美熟女激情综合网| 精品人妻一区二区三区在线看| 久热这里只有精品九九| 日本久久中文字幕免费| 亚洲国产精品一区二区| 91欧美激情在线视频| 少妇丰满a一区二区三区| 久草精品视频精品视频精品| 国产免费一区二区不卡| 欧美一区二区三区不卡高清视| 欧美日韩校园春色激情偷拍| 亚洲精品高清国产一线久久| 高清不卡视频在线观看| 亚洲国产av一二三区|