C語言字符串函數(shù)及指針
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <math.h>
void get_n_char(char s[], int n) // 從輸入中獲取n個(gè)字符存儲(chǔ)到字符數(shù)組中,形參char s[]和char* s等價(jià)
{
???? for (int i = 0; i < n; i++)
???? {
???? ???? s[i] = getchar();
???? }
???? // 獲得n個(gè)字符,存儲(chǔ)在s[0]至s[n-1]
???? s[n] = '\0'; // 在存放的字符后面放入空字符\0,使字符數(shù)組成為字符串,所以實(shí)際使用了n+1個(gè)元素位置,程序員需要確保字符數(shù)組有足夠的空間存放這些元素,避免指針溢出,比如傳入n為sizeof(s)-1或更小
}
void get_n_char2(char* s, int n) // 從輸入中最多獲取n個(gè)字符存儲(chǔ)到字符數(shù)組中,遇到空白字符即停
{
???? while (n-- > 0)
???? {
???????? *s = getchar();
???????? if (isspace(*s++)) //先運(yùn)算s++再運(yùn)算*,s++將s的值代入表達(dá)式參與運(yùn)算,之后將s的值遞增,所以*s++的值等于*s,即判斷新讀取的字符是否為空白字符,無論true/false,s都會(huì)指向下一個(gè)元素地址/向后移動(dòng)一個(gè)char長度(1byte)
???????? break;
???? }
???? *s = '\0'; // 從輸入讀取到的最后一個(gè)字符存放在了s-1位置
}
void reverse_str(char* s) // 反序字符串
{
???? size_t len = strlen(s); // strlen()返回字符串的長度,類型為size_t無符號(hào)整數(shù),在string.h頭文件中定義,根據(jù)編譯器/實(shí)現(xiàn)不同,實(shí)際類型可能是unsigned long
???? char temp;
???? for (size_t i = 0; i < len/2; i++)
???? {
???????? temp = s[i]; // s[i]和*(s+i)等價(jià)
???????? s[i] = s[len - 1 - i];
???????? s[len - 1 - i] = temp;
???? }
}
void clear_space(void)
{
???? char s[40] = ""; //將字符數(shù)組初始化為空字符串,相當(dāng)于{'\0'},和數(shù)組一樣,如果初始化元素不足則剩余的39個(gè)字符都初始化為0,所以s中所有元素都是'\0'
???? char* ptr;
???? char* ptr_n;
???? const char* info = "enter empty line to quit:"; //用雙引號(hào)括起來的字符串常量/字符串字面量,在內(nèi)存中唯一,不要修改常量,所以用const修飾
???? puts(info); //puts(字符串)將字符串輸出到屏幕,并在最后額外添加一個(gè)換行符,如果字符串末尾有換行符,則結(jié)果會(huì)換行兩次
???? //gets(s); 已被C11標(biāo)準(zhǔn)廢除的函數(shù),從輸入讀取一行內(nèi)容存儲(chǔ)到字符數(shù)組中,由于實(shí)參是數(shù)組的首元素地址,函數(shù)中無法解決讀取輸入長度超過數(shù)組長度的問題,指針溢出會(huì)造成程序錯(cuò)誤
???? ptr = fgets(s, 40, stdin); // fgets(字符數(shù)組,最大存儲(chǔ)長度,讀取的源) fgets會(huì)讀取一行,將一行中所有字符包含換行符都存到字符數(shù)組中,如果讀取的內(nèi)容總長小于最大存儲(chǔ)長度,會(huì)將包含末尾的\n全部存儲(chǔ)并停止讀取,如果讀取的內(nèi)容總長超過最大存儲(chǔ)長度,則存滿最大長度-1,最后一個(gè)位置放\0,讀取的源可以是文件,string.h頭文件中定義stdin為用戶鍵盤輸入,函數(shù)返回字符數(shù)組的首元素地址,如果讀取到EOF返回NULL,而用戶輸入時(shí)必須以回車結(jié)束輸入才能將緩存?zhèn)鹘o程序,所以每行末尾一定是\n
???? while (ptr != NULL&&*ptr!='\n') // NULL為空指針,該指針不會(huì)指向任何有用數(shù)據(jù),不等于空指針就意味著fgets()函數(shù)讀取到了內(nèi)容并存儲(chǔ)到s中,NULL的值為0,所以測試條件可以簡寫為while(ptr&&*ptr!='\n')只有當(dāng)ptr指向了字符即第一個(gè)條件為true才會(huì)執(zhí)行&&后面的內(nèi)容,所以*ptr調(diào)用安全
???? {
???????? ptr_n = strchr(s, '\n'); // strchr(字符串,字符)在字符串中從前往后查找字符,返回找到的第一個(gè)的地址,如果沒找到返回NULL
???????? if (!ptr_n) // 與ptr_n == NULL等價(jià),等于NULL意味著字符串中沒有換行符,即用戶輸入內(nèi)容超過39長度,輸入隊(duì)列中有殘留
???????????? while (getchar() != '\n')
???????????? ???? continue; // 清空一行剩余內(nèi)容,if語句內(nèi)只有一個(gè)while語句所以省略大括號(hào),while語句內(nèi)只有一個(gè)continue所以省略大括號(hào)
???????? while (*ptr) //*ptr!='\0'簡寫為*ptr,字符'\0'的值為0,\0是八進(jìn)制的0,空字符'\0'是ASCII字符集的首個(gè)字符(索引0)
???????? {
???????????? if (*ptr==' ')
???????????? {
???????????????? strcpy(ptr, ptr + 1); // strcpy(目標(biāo)字符數(shù)組,源字符串),將源字符串拷貝到目標(biāo)字符數(shù)組指定的位置,因?yàn)閷?shí)參是地址,所以通過調(diào)整第一個(gè)參數(shù)的地址來決定將字符串拷貝到數(shù)組中的什么位置,函數(shù)會(huì)將源字符串的所有內(nèi)容包含空字符\0全部拷貝,不檢查是否超過數(shù)組長度,需要程序員確保使用安全
???????????????? continue; //將空格后面的內(nèi)容前移一位來刪除空格,空格刪除后移過來的這個(gè)字符仍然需要判斷是否為空格,所以continue再次判斷
???????????? }
???????????? ptr++;
???????? }
???????? fputs(s, stdout); // 與fgets配對(duì)使用,fputs(字符串,輸出的目的地),將字符串輸出到指定文件,string.h頭文件中定義stdout為屏幕/控制臺(tái),C將文件和stdin/stdout視為同一類別,fputs()不會(huì)在打印完之后額外添加換行符,所以如果字符串中沒有換行符,打印之后光標(biāo)仍然留在這一行的末尾
???????? puts(info);
???????? ptr = fgets(s, 40, stdin);
???????? //再次給s賦值后,如果讀取一行長度超過39會(huì)將之前的字符串完全覆蓋,所以不會(huì)檢測到之前的換行符,如果沒超過39,新賦值的字符串的末尾一定是\n\0,所以檢測到的一定是新的字符串的\n且一定會(huì)檢測到\n,兩種情況都不會(huì)出錯(cuò),所以不需要在每次使用之前將數(shù)組重置
???? }
}
void show_string_array(void);
int get_strings(char s_ar[][40], int len);
int get_choice(void);
void show_string_array2(char* ptr_ar[], int len);
void sort_ascii(char* ptr_ar[], int len);
void sort_length(char* ptr_ar[], int len);
void sort_first_word(char* ptr_ar[], int len);
void print_by_argv(int argc, char* argv[]);
void function_about_string(void);
int main(int argc, char* argv[]) //C規(guī)定main函數(shù)可以不接收參數(shù)void,也可以接收兩個(gè)參數(shù),第一個(gè)參數(shù)argc記錄命令行執(zhí)行程序時(shí)傳入的參數(shù)總數(shù),第二個(gè)參數(shù)*argv[]指針數(shù)組記錄每個(gè)參數(shù)字符串的地址,比如C>./program.exe see you later ,argv[0]指針元素指向"C:\program.exe",argv[1]指針元素指向"see",argv[2]指針元素指向"you",argv[3]指針元素指向"later",參數(shù)包含要執(zhí)行的文件,所以argc==4,在命令行中使用雙引號(hào)引起來的部分會(huì)被視為一個(gè)字符串,無論內(nèi)部是否空格,且這個(gè)參數(shù)不會(huì)保留兩側(cè)的雙引號(hào)
{
???? function_about_string();
???? double d1,d2;
???? printf("argument total : %d\n", argc);
???? for (int i = argc-1; i >= 0; i--) //反序打印參數(shù)列表,從最后一個(gè)argc-1打印到0
???? {
???? ????puts(*(argv + i)); //*(argv+i)與argv[i]等價(jià)
???? }
???? if (argc>2)
???? {
???????? if (isdigit(argv[1][0])||(argv[1][0]=='-'&&isdigit(argv[1][1]))) //成為參數(shù)說明該字符串一定不為空字符串"",也就是說該字符串至少有一個(gè)字符加一個(gè)\0,所以argv[1][1]不越界,條件為true表明參數(shù)1的開頭可以轉(zhuǎn)換為數(shù)字
???????? {
???????????? if (isdigit(argv[2][0]) || (argv[2][0] == '-' && isdigit(argv[2][1]))) //判斷程序名后的第一個(gè)參數(shù)和第二個(gè)參數(shù)是否能轉(zhuǎn)換為數(shù)字
???????????? {
???????????????? d1 = atof(argv[1]); //atof(string)將字符串轉(zhuǎn)換為double,如果開頭是數(shù)字后面不是則轉(zhuǎn)換開頭部分,如果開頭也不是數(shù)字則結(jié)果根據(jù)編譯器實(shí)現(xiàn)決定,C標(biāo)準(zhǔn)未規(guī)定這種情況的結(jié)果
???????????????? d2 = atof(argv[2]);
???????????????? printf("%.2f^%.2f = %.2f", d1, d2, pow(d1, d2)); //計(jì)算冪,第一個(gè)參數(shù)的第二個(gè)參數(shù)次冪
???????????? }
???????? }
????
???? }
???? print_by_argv(argc, argv); //將命令行參數(shù)傳給函數(shù),根據(jù)命令行參數(shù)執(zhí)行打印操作
???? return 0;
}
void show_string_array(void)
{
???? // 讀入最多10個(gè)字符串并提供打印和排序功能
???? char s_ar[10][40]; //存放10個(gè)字符串的數(shù)組
???? char* ptr_ar[10]; //存放10個(gè)指針的數(shù)組,每個(gè)指針用來指向字符串?dāng)?shù)組的一個(gè)字符串元素
???? int len;
???? int choice = 0;
???? char info[80];
???? puts("請(qǐng)輸入最多10行內(nèi)容(Ctrl+Z停止輸入):");
???? len = get_strings(s_ar, 10);
???? while (len&&choice!=5) //條件len等價(jià)于len!=0
???? {
???????? for (int i = 0; i < len; i++)
???????? {
???????? ????ptr_ar[i] = s_ar[i]; //初始化指針數(shù)組,指針數(shù)組從前往后順序指向字符串?dāng)?shù)組
???????? }
???????? sprintf(info, "接收到%d行內(nèi)容,可進(jìn)行如下操作", len); //和printf類似,sprintf第二個(gè)參數(shù)為格式化字符串,之后的參數(shù)為待打印項(xiàng),第一個(gè)參數(shù)為字符數(shù)組,函數(shù)將待打印項(xiàng)代入到格式化字符串中,將拼好的字符串存儲(chǔ)到字符數(shù)組中,不打印
???????? puts(info);
???????? fputs("1.打印原列表\n2.以ASCII順序打印字符串\n3.按長度遞增順序打印\n4.按字符串中第一個(gè)單詞長度打印字符串\n5.退出\n請(qǐng)輸入數(shù)字:", stdout);
???????? switch (choice = get_choice())
???????? {
???????????? case 2:sort_ascii(ptr_ar, len);
???????????? break;
???????????? case 3:sort_length(ptr_ar, len);
???????????? break;
???????????? case 4:sort_first_word(ptr_ar, len);
???????????? break;
???????????? case 5:
???????????? continue;
???????? }
???????? show_string_array2(ptr_ar, len);
???? }
}
int get_strings(char s_ar[][40], int len)
{
???? int i;
???? char* ptr_n;
???? for (i = 0; i < len; i++)
???? {
???????? if (fgets(s_ar[i], 40, stdin)) //fgets讀取到內(nèi)容會(huì)返回第一個(gè)參數(shù)即字符數(shù)組的地址,讀取到EOF返回NULL
???????? {
???????????? if (ptr_n = strstr(s_ar[i], "\n")) //strstr(字符串1,字符串2),在字符串1中從前往后查找字符串2,返回找到的第一個(gè)的首字符的地址,沒找到返回NULL
???????????? ????*ptr_n = '\0';
???????????? else
???????????? ????while (getchar() != '\n')
???????????? ????????continue;
???????? }
???????? else? ? ?//讀取到EOF的情況,退出循環(huán),否則連續(xù)讀取十行字符串
???????? ????break;
???? }
???? return i;
}
int get_choice(void)
{
???? char s[10];
???? int choice = 0;
???? int i;
???? char* end;
???? while (1)
???? {
???????? if (fgets(s, 10, stdin))
???????? {
???????????? if (!strpbrk(s, "\n")) // strpbrk(字符串1,字符串2),在字符串1中從前往后查找任何一個(gè)字符串2的字符,返回最先找到的字符的地址,字符串2的空字符不會(huì)被查找
???????????????? while (getchar() != '\n')
???????????????????? continue;
???????????? for (i = 0; i<9&&isspace(s[i]); i++)
???????????? ????continue; // 跳過空格
???????????? //choice = atoi(&s[i]);? // atoi(字符串)函數(shù)將字符串轉(zhuǎn)換為數(shù)字,alpha to int,如果字符串以數(shù)字開頭,函數(shù)會(huì)將開頭的整數(shù)部分轉(zhuǎn)換為int,忽略掉后面非整數(shù)字符的部分(如果有的話),如果字符串以非整數(shù)字符開頭,C標(biāo)準(zhǔn)并未規(guī)定返回的結(jié)果,所以如果不確定能夠轉(zhuǎn)換成功就不要使用這個(gè)函數(shù),同類的函數(shù)有double atof(),long atol()
???????????? choice = (int)strtol(&s[i], &end, 10); // strtol(字符串,指針的地址常量,進(jìn)制)將字符串str轉(zhuǎn)換為long,第三個(gè)參數(shù)是進(jìn)制,可指定從2進(jìn)制到36進(jìn)制(使用字母a-z),第二個(gè)參數(shù)用于指向轉(zhuǎn)換成功的字符部分之后的那個(gè)字符,形參char** EndPtr為指向指針的指針,比如轉(zhuǎn)換"12zx",十進(jìn)制,轉(zhuǎn)換結(jié)果是12,第二個(gè)參數(shù)使其指向的指針變量的值指向數(shù)字12后的字符'z'的地址,即*EndPtr = &"12zx"[2],該函數(shù)想讓主調(diào)函數(shù)的一個(gè)指針指向轉(zhuǎn)換結(jié)束的字符的地址,即想修改主調(diào)函數(shù)的指針變量的值,而想要修改主調(diào)函數(shù)的變量,需要傳入變量的地址,所以主調(diào)函數(shù)的實(shí)參為指針的地址即&end,而函數(shù)用于接收這個(gè)地址的指針變量就需要是指向指針地址的指針,所以是** EndPtr,&EndPtr是形參EndPtr的地址,EndPtr是實(shí)參&end地址,是主調(diào)函數(shù)的指針變量end的地址常量,程序通過該地址來使用end指針,類型是指針的地址,*EndPtr相當(dāng)于*&end即end指針的值,是指針地址常量&end上存儲(chǔ)的值,程序通過地址&end使用指針end,在地址&end上存儲(chǔ)char字符的地址,所以*EndPtr或者說*&end的值是char字符的地址,是地址,在本編譯器中地址是8字節(jié),char是1字節(jié),通過*EndPtr=&"12zx"[2]使主調(diào)函數(shù)的指針變量end指向'z'的地址,這樣*end就等于'z',EndPtr的值是地址類型是&end,end的值是地址類型是&字符,兩個(gè)地址類型都是8字節(jié),但EndPtr(的值)指向指針(8字節(jié)),而end(的值)指向字符(1字節(jié))
???????????? //這里如果傳入的是&s[9]即字符串末尾的\0的地址也能正常執(zhí)行,函數(shù)判斷\0不是數(shù)字,將end指向\0,返回0
???????????? //同類函數(shù)double strtod(),unsigned long strtoul()
???????????? if (&s[i] == end || choice < 1 || choice>5) //如果end指向s[i]說明開頭的字符就不是long十進(jìn)制數(shù)字,所以一個(gè)字符都沒轉(zhuǎn)換,end指向了開頭的字符,這種情況strtol返回0,所以可以只判斷后兩個(gè)條件
???????????? ????puts("請(qǐng)輸入數(shù)字1-5");
???????????? else
???????????? ????return choice;
???????? }
???????? fputs("1.打印原列表\n2.以ASCII順序打印字符串\n3.按長度遞增順序打印\n4.按字符串中第一個(gè)單詞長度打印字符串\n5.退出\n請(qǐng)輸入數(shù)字:", stdout);
???? }
}
void show_string_array2(char* ptr_ar[], int len)
{
???? while (len-- > 0)
???? puts(*ptr_ar++); //ptr_ar是指針數(shù)組首元素/指針的地址,該地址上存儲(chǔ)的是字符串首元素的地址,puts()接收字符串的地址,puts(ptr_ar)錯(cuò)誤,因?yàn)閜tr_ar是指向字符串地址的指針的地址,是指針的地址,puts()接收字符串的地址,接收的是指向字符串地址的指針的值,所以應(yīng)寫為puts(ptr_ar[i])即*(ptr_ar+i),遞增的形式為*ptr_ar++
}
void sort_ascii(char* ptr_ar[], int len)
{
???? if (len < 2) //如果字符串?dāng)?shù)組只有一個(gè)元素則無需排序
???? ????return;
???? char* temp;
???? int changed = 1;
???? for (int i = 0; changed &&i < len-1; i++) //冒泡排序
???? {
???????? changed = 0;
???????? for (int j = len-1; j > i; j--)
???????? {
???????????? if(strcmp(ptr_ar[j-1],ptr_ar[j])>0) //strcmp(字符串1,字符串2),比較兩個(gè)字符串,從首個(gè)字符開始,比較包含\0的每一個(gè)字符,當(dāng)某一次比較時(shí)兩個(gè)字符不相同,返回字符串1的字符-字符串2的字符的差值,根據(jù)ASCII字符集,大寫字母的編碼小于小寫字母,即在小寫字母前面,所以大寫字母-小寫字母為負(fù)數(shù),而\0的編碼為0,除了\0其他字符-\0都為正數(shù),所以通過比較的結(jié)果,如果字符串1的第一個(gè)與字符串2不相同的字符在ASCII字符集中位于字符串2對(duì)應(yīng)的字符的前面,則返回負(fù)數(shù),反之返回正數(shù)
???????????? {
???????????????? temp = ptr_ar[j - 1];
???????????????? ptr_ar[j - 1] = ptr_ar[j];
???????????????? ptr_ar[j] = temp; //當(dāng)strcmp結(jié)果為正時(shí)交換兩個(gè)元素
???????????????? changed = 1; //從后往前逐個(gè)相鄰的元素比較,j比j-1位小就交換,一輪下來會(huì)確定一個(gè)最小的元素,然后下一輪確認(rèn)次最小的元素,每次發(fā)生交換就說明可能還有順序不正確的,如果一輪下來沒有發(fā)生交換說明剩下的部分已經(jīng)正確排序了,結(jié)束外層循環(huán)
???????????? }
???????? }
???? }
}
void sort_length(char* ptr_ar[], int len) //按長度遞增排序,這里使用歸并排序,將大的數(shù)組分成兩部分分別排序,之后再將排序好的兩部分合并排序,通過遞歸將數(shù)組逐漸分成每部分只包含1個(gè)元素并返回(1個(gè)元素即已排序好),從兩個(gè)只包含1個(gè)元素的部分開始逐漸往更大的范圍排序
{
???? if (len < 2) //當(dāng)細(xì)分成1個(gè)元素時(shí)返回
???? ????return;
???? int mid = len / 2; //將長度分為兩個(gè)部分
???? int left, right, i;
???? sort_length(ptr_ar, mid); //遞歸對(duì)兩個(gè)部分分別排序
???? sort_length(ptr_ar + mid,len- mid); //ptr_ar + mid與&ptr_ar[mid]等價(jià),執(zhí)行完這一句說明兩個(gè)部分已經(jīng)分別完成了排序
???? char* temp[10];
???? for ( i = left = 0,right = mid; left < mid&&right<len; ) //實(shí)現(xiàn)兩個(gè)部分的合并排序
???? {
???? ????temp[i++] = strlen(ptr_ar[left]) < strlen(ptr_ar[right]) ? ptr_ar[left++] : ptr_ar[right++]; //比較左右兩個(gè)部分的最小元素(因?yàn)楦髯耘藕眯蛄?,所以是各自最左?cè)未使用的元素),將較小的那個(gè)元素放入臨時(shí)數(shù)組中,再比較這個(gè)元素右側(cè)的元素和另一部分最小的元素哪個(gè)更小
???? }
???? while(left<mid) //上面for循環(huán)的結(jié)束條件是兩個(gè)部分中有一個(gè)部分的元素全部取完,所以此時(shí)一定有另一個(gè)部分還留有元素沒有放入臨時(shí)數(shù)組,在不知道是哪個(gè)部分的情況下,繼續(xù)用兩個(gè)循環(huán)來將剩下的元素取完
???? ????temp[i++] = ptr_ar[left++];
???? while(right<len)
???? ????temp[i++] = ptr_ar[right++];
???? for ( i = 0; i < len; i++) //將排序好的臨時(shí)數(shù)組拷貝到原數(shù)組對(duì)應(yīng)的區(qū)域
???? ????ptr_ar[i] = temp[i];
}
void sort_first_word(char* ptr_ar[], int len) //按首個(gè)單詞的長度遞增排序,這里使用隨機(jī)快速排序,每次遞歸隨機(jī)選擇一個(gè)支點(diǎn),將所有小于該支點(diǎn)的元素放到左側(cè),所有大于的元素放到右側(cè),這樣即完成了對(duì)該支點(diǎn)的排序,之后遞歸執(zhí)行左側(cè)和右側(cè)兩個(gè)部分,直到所有元素都成為支點(diǎn)即被放到正確的位置,排序完成
{
???? if (len < 2) //如果字符串?dāng)?shù)組只有一個(gè)元素則無需排序
???? ????return;
???? char *temp, *pivot,*ptr_space;
???? //錯(cuò)誤用法:char *temp, pivot,ptr_space; 聲明類型char不攜帶*,執(zhí)行時(shí)分別聲明char* temp/char pivot/char ptr_space
????//char ch, * ptr, str[20], * ptr_arr[20], str_arr[10][20]; 分別聲明字符變量ch,字符指針ptr,字符數(shù)組str,字符指針數(shù)組ptrarr,字符數(shù)組的數(shù)組strarr,聲明只共享關(guān)鍵字char
???? int ran, s1r, s2r, tlen, slen;
???? srand(time(NULL)); //設(shè)定隨機(jī)數(shù)種子,time(NULL)返回當(dāng)前時(shí)間戳,用這個(gè)必然每次都不同的時(shí)間戳設(shè)定為隨機(jī)數(shù)的種子,使每次偽隨機(jī)的值都不同
???? ran = rand() % len; //rand()函數(shù)根據(jù)種子生成偽隨機(jī)數(shù),返回int,范圍從0到RAND_MAX,模len的結(jié)果為0到len-1即有效下標(biāo)/索引的范圍,從數(shù)組中隨機(jī)選擇一個(gè)元素作為支點(diǎn)
???? temp = ptr_ar[0];
???? ptr_ar[0] = ptr_ar[ran];
???? ptr_ar[ran] = temp; //將隨機(jī)選取的元素放到首位
???? pivot = ptr_ar[0]; //將首位作為支點(diǎn)
????//錯(cuò)誤用法:tlen = (int)((strpbrk(pivot, " \t") || strchr(pivot, '\0')) - pivot); //使用strpbrk查找pivot指向的字符串中第一個(gè)空白字符的位置(沒有查\n是因?yàn)間et_strings獲得函數(shù)時(shí)將\n去掉了),因?yàn)閟trpbrk不能查找\0即便將\0寫入第二個(gè)參數(shù)中也不行,所以如果該字符串沒有空白字符,strpbrk會(huì)返回NULL,使用||或運(yùn)算符來處理(當(dāng)strpbrk返回有效地址時(shí)不執(zhí)行||右側(cè)判斷,當(dāng)返回NULL時(shí)執(zhí)行右側(cè)判斷),使用strchr函數(shù)查找\0的位置,所以兩個(gè)函數(shù)一定有至少一個(gè)會(huì)返回有效地址,設(shè)想用兩個(gè)地址差表示首個(gè)單詞的長度
????//但是
???? //C語言的邏輯運(yùn)算符的值為0或1,(strpbrk(pivot, " \t") || strchr(pivot, '\0'))的值一定為1(而不是指針),1 - 指針出錯(cuò)
???? ptr_space = strpbrk(pivot, " \t");
???? tlen = (int)(ptr_space ? (ptr_space - pivot) : strlen(pivot));
???? for ( s1r = s2r = 0; ++s2r < len; ) //s1r是左側(cè)部分(小于支點(diǎn)的部分)的最右側(cè)元素,s2r是右側(cè)部分的最右側(cè)元素,初始化時(shí)兩個(gè)部分都沒有放元素,所以都是下標(biāo)0
???? {
???????? ptr_space = strpbrk(ptr_ar[s2r], " \t");
???????? slen = (int)(ptr_space ? ptr_space - ptr_ar[s2r] : strlen(ptr_ar[s2r]));
???????? if (slen<tlen) //先擴(kuò)展s2r一位,比較新的這位和支點(diǎn),如果小于支點(diǎn)則擴(kuò)展s1r一位并將兩個(gè)擴(kuò)展的位交換
???????? {
???????????? temp = ptr_ar[++s1r];
???????????? ptr_ar[s1r] = ptr_ar[s2r];
???????????? ptr_ar[s2r] = temp; //交換之后索引s1r的位是這一輪新擴(kuò)展的位(小于支點(diǎn)的位),索引s2r的位是之前比較過大于支點(diǎn)的位
???????? }
???? }
???? ptr_ar[0] = ptr_ar[s1r];
???? ptr_ar[s1r] = pivot; //將小于支點(diǎn)部分的最右側(cè)的元素和支點(diǎn)交換,此時(shí)支點(diǎn)左側(cè)都是小于支點(diǎn)的共有s1r位,右側(cè)都是大于支點(diǎn)的共有l(wèi)en-s1r-1位,經(jīng)過循環(huán)s2r==len
???? sort_first_word(ptr_ar, s1r); //當(dāng)s1r==0時(shí)傳入的指針依然是已經(jīng)排序好的支點(diǎn),但函數(shù)先判定s1r<2返回所以安全
???? sort_first_word(ptr_ar + s1r + 1, len - s1r - 1); //當(dāng)s1r==len-1即支點(diǎn)右側(cè)沒有元素時(shí),傳入的指針為范圍外的ptr_ar[len],C規(guī)定數(shù)組外/后首個(gè)地址為有效地址,雖然不知道該地址是用來做什么的,但是ptr_ar[len]不會(huì)報(bào)錯(cuò),而函數(shù)先判斷l(xiāng)en-s1r-1<2返回所以安全
????
}
void print_by_argv(int argc, char* argv[])
{
???? puts("輸入內(nèi)容以EOF結(jié)束:");
???? char s[1024];
???? char ch;
???? int i;
???? for (i = 0; i < 1023&&(ch = getchar())!=EOF; i++)
???? {
???? ????s[i] = ch;
???? }
???? s[i] = '\0';
???? if (argc<2||strcmp(argv[1],"-p")==0) //比較字符串不能使用==運(yùn)算符,因?yàn)檫@會(huì)比較地址不會(huì)比較字符,strcmp()如果兩個(gè)字符串內(nèi)容長度相同會(huì)返回0
???? {
???? ????fputs(s, stdout);
???? }
???? else if (strcmp(argv[1], "-u") == 0) //if語句中使用||或運(yùn)算符,如果執(zhí)行else說明兩側(cè)條件都為false,即argc>=2&&argv[1]不是-p,也就是說argv[1]一定不越界,所以不需要再次判斷argc>=2,else即!(argc<2||strcmp==0),即argc>=2&&strcmp!=0,在這個(gè)范圍內(nèi)可直接判斷argv[1]是否-u-l
???? {
???????? for (char* ptr = s; *ptr; ptr++)
???????? {
???????? ????putchar(toupper(*ptr));
???????? }
???? }
???? else if (strcmp(argv[1], "-l") == 0) //執(zhí)行這個(gè)else if意味著argc>=2并且argv[1]不是-p-u
???? {
???????? for (char* ptr = s; *ptr; ptr++)
???????? {
???????? ????putchar(tolower(*ptr));
???????? }
???? }
}
void function_about_string(void)
{
???? //其他字符串相關(guān)函數(shù)
???? strcmp("asdf", "as"); //比較兩個(gè)字符串,相同返回0,不同返回首個(gè)不同的字符的ASCII差(前減后)
???? printf("%d",strncmp("asdf", "as", 2)); //比較兩個(gè)字符串的前2個(gè)字符,第三個(gè)參數(shù)指定比較前幾個(gè)字符,這兩個(gè)字符串的前兩個(gè)字符相同,所以結(jié)果為0,如果參數(shù)為3,則返回'd'-'\0'
???? char s[80];
???? const char* ptr = "this is a string";
???? char* ptr2;
???? ptr2 = strcpy(s, ptr); //strcpy(dest,src)將src拷貝到dest,包含空字符,函數(shù)不會(huì)檢查dest空間是否足夠,需要程序員確保安全使用,將strcpy(dest,src)理解為字符串的賦值表達(dá)式,dest是左值,src是右值,如果直接寫dest=src只是將字符串的地址賦值,而沒有將整個(gè)字符串傳過來,所以這個(gè)函數(shù)相當(dāng)于字符串的賦值表達(dá)式,函數(shù)返回參數(shù)1,即左值的首元素地址
???? ptr2 = strncpy(s + 5, ptr, 13); //strncpy(dest,src,len)將src的前l(fā)en個(gè)字符拷貝到dest,如果src長度小于len就等同于strcpy,如果大于等于len會(huì)使得末尾的\0沒有拷貝過來,如果在拷貝中將目標(biāo)數(shù)組原來的\0覆蓋掉則需要在末尾手動(dòng)補(bǔ)充\0,返回參數(shù)1和strcpy一樣
???? ptr2[13] = '\0';
???? puts(s); //完整打印字符串
???? puts(ptr2); //ptr2現(xiàn)在指向&s[5]地址,即從第六個(gè)元素開始向后打印
???? ptr2 = strcat(s, ptr); //將ptr拼接到s的末尾,concatenate連接,從s的第一個(gè)\0空字符開始拷貝,拷貝包含末尾的\0,函數(shù)不檢查空間是否足夠,返回參數(shù)1
???? ptr2 = strncat(s, ptr, 4); // 拼接ptr前4位到s的末尾,如果ptr長度小于第三個(gè)參數(shù),則效果等同strcat,將包含\0截止的所有字符拼接,如果長度大于等于第三個(gè)參數(shù),則拷貝指定的位數(shù),并在拷貝的內(nèi)容后面一位添加\0,所以無論哪種情況,都一定會(huì)有且只有1個(gè)\0被添加到末尾,返回參數(shù)1
???? puts(ptr2); //ptr2現(xiàn)在指向s
???? ptr2 = strchr(s, 'f'); //從前往后查找字符f,返回第一個(gè)的地址,沒找到返回NULL
???? ptr2 = strrchr(s, '\n'); //從字符串的末尾往前查找字符,函數(shù)接受的不是字符數(shù)組,所以是檢測到第一個(gè)\0位置開始往前查找,無論這個(gè)字符數(shù)組在第一個(gè)\0后面有多少個(gè)要查找的字符都無效,只要第一個(gè)\0前面找不到這個(gè)字符,就返回NULL
???? ptr2 = strpbrk(s, "poiugsfdxcvb"); //在參數(shù)1字符串中從前往后查找,判斷每一位字符是否在參數(shù)2字符中(不包含\0),返回發(fā)現(xiàn)的第一個(gè)字符地址,沒找到返回NULL
???? ptr2 = strstr(s, "thisz"); //在參數(shù)1字符串中從前往后查找參數(shù)2字符串(不包含\0),找到則返回匹配位置首字符地址,未找到返回NULL
???? strlen(s); //返回字符串長度size_t類型
???? sprintf(s, "%d:%22s", 1, ptr); //使用格式化字符串拼接各待打印項(xiàng),將拼好的字符串存儲(chǔ)到字符數(shù)組中
???? fputs(s,stdout);
???? fgets(s, 10, stdin);
???? gets_s(s, 79); //C11規(guī)定可選函數(shù),不一定適用于所有支持C11的編譯器,函數(shù)從標(biāo)準(zhǔn)輸入stdin/用戶鍵盤輸入讀取內(nèi)容拷貝到字符數(shù)組,如果讀到換行符會(huì)丟棄不儲(chǔ)存,如果讀取長度小于參數(shù)2限制的最大字符數(shù)則完成拷貝并在末尾添加\0(即在原本應(yīng)存儲(chǔ)換行符的位置存儲(chǔ)\0)返回參數(shù)1,如果讀取到第二個(gè)參數(shù)所限制的最大字符數(shù)都沒有讀到換行符則將首位賦值\0并丟棄該行剩余的內(nèi)容直至遇到換行符或文件結(jié)尾EOF并返回空指針,接著調(diào)用依賴實(shí)現(xiàn)的"處理函數(shù)"(或你選擇的其他函數(shù)),所以函數(shù)不會(huì)修改最大字符數(shù)的后一位作為\0
???? atoi("123asdf");?
???? strtol("42asdf", &ptr2, 16);
}