??前面的話??
在前面C語言的指南針——指標!指標與結構體的介紹和C語言處理批量資料的好伙伴!陣列!C語言陣列的介紹與應用兩篇文章中已經簡要介紹了陣列指標,字串庫函式和指標等內容,在這篇文章我們將繼續深入了解有關字串庫函式和指標的探索,
📒博客主頁:https://blog.csdn.net/m0_59139260?spm=1011.2124.3001.5343
🎉歡迎關注🔎點贊👍收藏??留言📝
📌本文由未見花聞原創,CSDN首發!
??堅持和努力一定能換來詩與遠方!
🙏作者水平很有限,如果發現錯誤,一定要及時告知作者哦!感謝感謝!
博主的碼云gitee,平常博主寫的程式代碼都在里面,
內容導讀
- 1.字符指標與字串
- 1.1字符指標與字串的關系
- 1.2常用的幾個字串庫函式
- 1.2.1輸出輸入字串的函式
- 1.2.2字串連接函式
- 1.2.3字串復制函式
- 1.2.4字串比較函式
- 1.2.5測字串長度的函式
- 1.2.6轉換為大小寫的函式
- 2.指標陣列與陣列指標
- 2.1指標陣列
- 2.2陣列指標
- 2.3陣列名與&陣列名
- 3.陣列傳參與指標傳參
- 3.1陣列傳參
- 3.1.1一維陣列傳參
- 3.1.2二維陣列傳參
- 3.2指標傳參
- 3.2.1一級指標傳參
- 3.2.2二級指標傳參
- 4.函式指標與函式指標陣列
- 4.1函式指標
- 4.2函式指標陣列及其在計算器的應用
- 5.回呼函式
1.字符指標與字串
字符指標即指向字符型別的指標,
char* pch = NULL;
char ch = 's';
pch = &ch;//字符指標
1.1字符指標與字串的關系
在C語言中,并沒有字串這種資料型別,但是在編程中又常常遇到字串,那在C語言中然后去表達字串呢?
有兩種方式,一是使用字符陣串列示字串,本質上就是定義一個char型別的陣列變數去儲存字串,使用字符陣串列示字串得到的是一個字串變數,它是能夠被修改的;二是使用字符指標的方式去表達字串,本質上就是使用一個char指標指向字串的第一個字符,因為在輸出字串的時候計算機是以\0為標志來列印字串的,但是要注意的是,它和使用字符陣列不同,字符陣列定義的是一個字串變數,而字符指標定義的是一個字串常量,是不能進行修改的,
#include <stdio.h>
int main()
{
char str1[40] = "Come on, China Olympic Games!";//使用字符陣列定義字串變數
char* str2 = "Come on, China Olympic Games!";//使用字符指標定義字串常量
printf("str1:%s\nstr2:%s\n", str1, str2);
return 0;
}
💡運行結果:
str1:Come on, China Olympic Games!
str2:Come on, China Olympic Games!
D:\gtee\C-learning-code-and-project\test_813\Debug\test_813.exe (行程 6960)已退出,代碼為 0,
按任意鍵關閉此視窗. . .
如果不小心對字串常量修改,會引發讀寫訪問沖突的例外,對于VS這個例外只有在除錯的時候才能看得見而在編譯的時候是不會報警告或者報錯的,萬一是在一個很大的工程中出現了這種問題,是很難察覺的,所以一般定義一個不能被修改的變數或常量的時候,一般在定義的資料型別前面加上一個const,這樣如果不小心修改了這個const修飾的變數或常量,編譯器在編譯的時候就會報錯,這樣就能更容易地發現讀寫沖突例外的情況,
💡面試題:
#include <stdio.h>
int main()
{
char str1[] = "hello str.";
char str2[] = "hello str.";
char* str3 = "hello str.";
char* str4 = "hello str.";
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if (str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
使用字符陣串列達的是字串變數,本質就是陣列,每個陣列是獨立儲存的,所以就算兩個陣列儲存相同的內容,但是兩者地址一定是不相同的,
而使用字符指標表達的是字串常量,所以內容相同,字符指標指向的就是同一個地址,
知道了這個區別,那這道面試題也能迎刃而解了,
💡運行結果:
str1 and str2 are not same
str3 and str4 are same
D:\gtee\C-learning-code-and-project\test_813\Debug\test_813.exe (行程 16488)已退出,代碼為 0,
按任意鍵關閉此視窗. . .
1.2常用的幾個字串庫函式
在前面介紹字符陣列的時候簡要提到了這幾個與字串相關的庫函式,現在我將詳細介紹這些常用的庫函式,
1.2.1輸出輸入字串的函式
puts(字符陣列);//輸出
int puts(const char* string);
gets(字符陣列);//輸入
char* gets(char* buffer);

🔎puts:函式所在庫為stdio.h;函式引數為const修飾的char*型別(使用const修飾是為了防止字串被修改);函式回傳值為int,如果函式執行失敗則會回傳EOF,

🔎gets:函式所在庫為stdio.h;函式引數為char*型別(從緩沖區讀取一行字串,buffer是緩沖區的意思);函式回傳值為char*(字串的第一個元素地址),函式不能傳入空指標,如果函式執行失敗會回傳EOF,
用puts和gets函式只能輸出或輸入一個字串,
puts作用:將一個字串(以′\0′結束的字符序列)輸出到終端,
用puts函式輸出的字串中可以包含轉義字符,
在用puts輸出時將字串結束標志′\0′轉換成′\n′,即輸出完字串后換行,
gets作用:從終端輸入一個字串到字符陣列,并且得到一個函式值,該函式值是字符陣列的起始地址,
1.2.2字串連接函式
strcat(字串1, 字串2);
char *strcat( char *strDestination, const char *strSource );

🔎strcat:函式所在庫為string.h;函式引數有兩個,第一個為char*型別(目的字串地址)第二個引數為const char*型別(被連接字串的地址);函式回傳值為char*(回傳strDestination的元素地址),
strcat函式將strSource追加到strDestination后面,
strcat作用:把兩個字符陣列中的字串連接起來,把字串2接到字串1的后面,結果放在字符陣列1中,函式呼叫后得到一個函式值——字符陣列1的地址,
字符陣列1必須足夠大,以便容納連接后的新字串,
連接前兩個字串的后面都有′\0′,連接時將字串1后面的′\0′取消,只在新串最后保留′\0′,
1.2.3字串復制函式
strcpy(字串1, 字串2);
char *strcpy( char *strDestination, const char *strSource );

🔎strcpy:函式所在庫為string.h;函式引數有兩個,第一個為char*型別(目的字串地址)第二個引數為const char*型別(被復制字串的地址);函式回傳值為char*(回傳strDestination的元素地址),
strcpy函式將strSource復制給strDestination,
strcpy作用:將字串2復制到字符陣列1中去,
字符陣列1必須定義得足夠大,以便容納被復制的字串2,字符陣列1的長度不應小于字串2的長度,
“字符陣列1”必須寫成陣列名形式,“字串2”可以是字符陣列名,也可以是一個字串常量,
若在復制前未對字符陣列1初始化或賦值,則其各位元組中的內容無法預知,復制時將字串2和其后的′\0′一起復制到字符陣列1中,取代字符陣列1中前面的字符,未被取代的字符保持原有內容,
不能用賦值陳述句將一個字串常量或字符陣列直接給一個字符陣列,字符陣列名是一個地址常量,它不能改變值,正如數值型陣列名不能被賦值一樣,
可以用strncpy函式將字串2中前面n個字符復制到字符陣列1中去,
將str2中最前面2個字符復制到str1中,取代str1中原有的最前面2個字符,但復制的字符個數n不應多于str1中原有的字符(不包括′\0′),
1.2.4字串比較函式
strcmp(字串1, 字串2);
int strcmp( const char *string1, const char *string2 );

🔎strcmp:函式所在庫為string.h;函式引數有兩個,第一個為constchar*型別(第一個字串地址)第二個引數為const char*型別(第二個字串的地址);函式回傳值為int(相同則回傳0,字串1>字串2則回傳一個正整數,字串1<字串2則回傳一個負整數),
💡對兩個字串比較不能直接用str1>str2進行比較,因為str1和str2代表地址而不代表陣列中全部元素,而只能用 (strcmp(str1,str2)>0)實作,系統分別找到兩個字符陣列的第一個元素,然后順序比較陣列中各個元素的值,
strcmp作用:比較字串1和字串2,
字串比較的規則是: 將兩個字串自左至右逐個字符相比(按ASCII碼值大小比較),直到出現不同的字符或遇到′\0′為止,
(1) 如全部字符相同,則認為兩個字串相等;
(2) 若出現不相同的字符,則以第1對不相同的字符的比較結果為準,
比較的結果由函式值帶回,
(1) 如果字串1與字串2相同,則函式值為0,
(2) 如果字串1>字串2,則函式值為一個正整數,
(3) 如果字串1<字串2,則函式值為一個負整數,
1.2.5測字串長度的函式
strlen(字串);
size_t strlen( const char *string );//size_t本質上是unsigned int 型別

🔎strlen:函式所在庫為string.h;函式引數為const char*型別(需要被計算字串長度的字串地址);函式回傳值為unsigned int(size_t)(回傳傳入字串長度大小),
strlen作用:測驗字串長度的函式,函式的值為字串中的實際長度(不包括′\0′在內),
1.2.6轉換為大小寫的函式
strlwr(字串);//大寫字母轉小寫字母
char *_strlwr( char *string );
strupr(字串)//小寫字母轉大寫字母

🔎strlwr:函式所在庫為string.h;函式引數為char*型別(需要被轉換成小寫的字串地址);函式回傳值為char*(回傳傳入字串地址),

🔎strupr:函式所在庫為string.h;函式引數為char*型別(需要被轉換成大寫的字串地址);函式回傳值為char*(回傳傳入字串地址),
作用:
srtlwr將字串中大寫字母換成小寫字母,
strupr將字串中小寫字母換成大寫字母,
2.指標陣列與陣列指標
2.1指標陣列
指標陣列就是陣列元素型別為指標的陣列,
int* arr1[10]; //整形指標的陣列
char* arr2[40]; //一級字符指標的陣列
char** arr3[50];//二級字符指標的陣列
2.2陣列指標
陣列指標就是指向一個陣列的指標,要弄清楚陣列指標是什么,首先必須知道陣列指標是指標還是陣列,我們從概念名字上進行分析,就能知道它是指標而不是陣列,
我們以類比的方式來搞清楚陣列指標究竟是什么?
我們已經學習過整型指標int*,浮點數指標float* double*,我們以型別名+變數名的方式定義一個相應型別的指標,
a = 8;
b = 2.2;
int* pa = &a;//整型指標
double* pb = &b;//雙精度浮點型指標
同理陣列指標也是如此,不過需要與指標陣列區分開來
int* arr[10];//整型指標陣列,是一個陣列,存放了10個整型指標型別元素
int (*parr)[10];//這就是一個陣列指標,是不是和指標陣列有點像呢?
//因為[]優先級高于*,如果不加括號,變數名首先會與[]結合表示成一個陣列
//如果(*變數名),那變數名首先會與*結合就表示一個指標
int (*parr)[10]表示一個指標,指向一個資料型別為int元素個數為10的一個陣列,所以parr是一個陣列指標變數,
2.3陣列名與&陣列名
陣列名一般情況下指的是陣列首元素地址,與陣列名[0]等價,但是有二般情況,使用sizeof(陣列名),這里的陣列名指的是全陣列整體;&arr并不是指陣列名存放的地址的地址,而是指一個陣列整體的首地址,陣列名+1加的是該陣列存放元素的資料型別的大小,如果為int型別的陣列,那就是加4,而&arr+1加的是陣列整體的大小,如果一個int型別的陣列有10個元素,那就是加40,
3.陣列傳參與指標傳參
3.1陣列傳參
3.1.1一維陣列傳參
對于test1 test2函式形參可以這么寫呢?
#include <stdio.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,0 };
int* arr2[10] = { 0 };
int i = 0;
for (i = 0; i < 10; i++)
{
arr2[i] = &arr1[i];
}
test1(arr1);
test2(arr2);
return 0;
}
//test1
void test1(int arr[]);//方式1
void test1(int arr[10]);//方式2
void test1(int* arr);//方式3
//test2
void test2(int* arr[]);//方式1
void test2(int* arr[10]);//方式2
void test2(int** arr);//方式3
3.1.2二維陣列傳參
對于test函式形參可以這么寫呢?
#include <stdio.h>
int main()
{
int arr[4][3] = { 0 };
test(arr);
return 0;
}
void test(int arr[4][3]);//方式1
void test(int arr[][3]);//方式2
void test(int arr[][]);//不能省略列數,錯誤
//對于二維陣列來說,每個元素指的是每一行,首元素指的是第一行陣列,所以可以使用陣列指標傳參
void test(int (*arr)[3]);//方式3
3.2指標傳參
3.2.1一級指標傳參
一個函式test(int* p)能接受什么引數?
int arr[10] = {0};
test(arr);//1.陣列名
int a = 10;
int* pa = &a;
test(pa);//或
test(&a);//2.一級指標
3.2.2二級指標傳參
一個函式test(int** p)能接受什么引數?
int n = 10;
int* p = &n;
int** pp = &p;
int* arr[10] = {0};
test(arr);//1.整型指標陣列
test(pp);
test(&p);//2.二級指標
4.函式指標與函式指標陣列
4.1函式指標
如果在程式中定義了一個函式,在編譯時會把函式的源代碼轉換為可執行代碼并分配一段存盤空間,這段記憶體空間有一個起始地址,也稱為函式的入口地址,每次呼叫函式時都從該地址入口開始執行此段函式代碼,
函式名就是函式的指標,它代表函式的起始地址,
函式名與陣列名有點相似,對函式名取地址得到的還是函式的地址,
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
💡運行結果:
004713B1
004713B1
D:\gtee\C-learning-code-and-project\test_813\Debug\test_813.exe (行程 20032)已退出,代碼為 0,
按任意鍵關閉此視窗. . .
可以定義一個指向函式的指標變數,用來存放某一函式的起始地址,這就意味著此指標變數指向該函式,例如:
int (*p)(int,int);
定義p是一個指向函式的指標變數,它可以指向函式型別為整型且有兩個整型引數的函式,此時,指標變數p的型別用int (*)(int,int)表示,
??函式指標定義通式:型別名 (*指標變數名)(函式引數表列)
??對于函式指標你要注意以下幾點:
🍋:定義指向函式的指標變數,并不意味著這個指標變數可以指向任何函式,它只能指向在定義時指定的型別的函式,
🍋:如果要用指標呼叫函式,必須先使指標變數指向該函式,
🍋:在給函式指標變數賦值時,只須給出函式名而不必給出引數,
🍋:用函式指標變數呼叫函式時,只須將(*p)代替函式名即可(p為指標變數名),在(*p)之后的括號中根據需要寫上實參,
🍋:對指向函式的指標變數不能進行算術運算,如p+n,p++,p–等運算是無意義的,
🍋:用函式名呼叫函式,只能呼叫所指定的一個函式,而通過指標變數呼叫函式比較靈活,可以根據不同情況先后呼叫不同的函式,
小栗子:
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 2;
int b = 6;
int (*p)(int, int) = Add;
int c = Add(a, b);
int d = (*p)(a, b);
printf("%d\n%d", c, d);
return 0;
}
💡運行結果:
8
8
D:\gtee\C-learning-code-and-project\test_813\Debug\test_813.exe (行程 21292)已退出,代碼為 0,
按任意鍵關閉此視窗. . .
💡分析兩個有趣的代碼
#include <stdio.h>
int main()
{
//代碼1
(*(void (*)())0)();
//代碼2
void (*signal(int, void(*)(int)))(int);
return 0;
}
🔑代碼1:
void (*)()是一個引數為慷訓傳值為空的函式指標型別,(void (*)())0意思是將0強制轉換成這種指標型別,(* (void (*)())0)()解參考被轉換過的0執行在該地址的函式,
🔑代碼2:
定義了一個名為signal的函式,其中引數為整型int和引數為int 回傳值為空的函式指標void(*)(int),回傳值為void (*)(int)型別的函式指標,
可以使用typedef將這個代碼簡化:
typedef void (*pf)(int)
void (*signal(int, void(*)(int)))(int);
//相當于
pf signal(int, pf);
4.2函式指標陣列及其在計算器的應用
//函式指標
int (*func)(int, int);
//指標陣列
int* arr[10];
//函式指標陣列
int (*funcarr[10])(int, int);
//函式指標陣列的指標
int (*(*pfuncarr)[10])(int, int);
函式指標陣列用途:轉移表
🌽栗子:整型加減乘除計算器
選單及其加減乘除函式
#include <stdio.h>
void menu()
{
printf("**********************************\n");
printf("**********************************\n");
printf("******* 1.add *******\n");
printf("******* 2.sub *******\n");
printf("******* 3.mul *******\n");
printf("******* 4.div *******\n");
printf("******* 0.exit *******\n");
printf("**********************************\n");
printf("**********************************\n");
}
int Add(int x, int y)//int (*)(int, int)
{
return x + y;
}
int Sub(int x, int y)//int (*)(int, int)
{
return x - y;
}
int Mul(int x, int y)//int (*)(int, int)
{
return x * y;
}
int Div(int x, int y)//int (*)(int, int)
{
return x / y;
}
void Calc(int(*pf)(int, int))
{
int x = 0;
int y = 0;
int ret = 0;
printf("請輸入2個運算元:>");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("ret = %d\n", ret);
}
1.使用函式指標傳參實作
int main()
{
int input = 0;
do
{
menu();
printf("請選擇:>");
scanf("%d", &input);
switch (input)
{
case 1:
Calc(Add);
break;
case 2:
Calc(Sub);
break;
case 3:
Calc(Mul);
break;
case 4:
Calc(Div);
break;
case 0:
printf("退出計算器\n");
break;
default:
printf("選擇錯誤\n");
break;
}
} while (input);
return 0;
}
💡運行結果:
**********************************
**********************************
******* 1.add *******
******* 2.sub *******
******* 3.mul *******
******* 4.div *******
******* 0.exit *******
**********************************
**********************************
請選擇:>1
請輸入2個運算元:>2 3
ret = 5
**********************************
**********************************
******* 1.add *******
******* 2.sub *******
******* 3.mul *******
******* 4.div *******
******* 0.exit *******
**********************************
**********************************
請選擇:>3
請輸入2個運算元:>2 3
ret = 6
**********************************
**********************************
******* 1.add *******
******* 2.sub *******
******* 3.mul *******
******* 4.div *******
******* 0.exit *******
**********************************
**********************************
請選擇:>0
退出計算器
D:\gtee\C-learning-code-and-project\test_813\Debug\test_813.exe (行程 7012)已退出,代碼為 0,
按任意鍵關閉此視窗. . .
2.使用函式指標陣列實作
int main()
{
int input = 0;
do
{
int x = 0;
int y = 0;
int ret = 0;
menu();
printf("請選擇:>");//1
scanf("%d", &input);
int (*pfArr[5])(int, int) = {0, Add, Sub, Mul, Div};
//0 1 2 3 4
if (input == 0)
{
printf("退出計算器\n");
}
else if(input>=1 && input<=4)
{
printf("請輸入2個運算元:>");
scanf("%d %d", &x, &y);
ret = pfArr[input](x, y);
printf("%d\n", ret);
}
else
{
printf("選擇錯誤\n");
}
} while (input);
return 0;
}
💡運行結果:
**********************************
**********************************
******* 1.add *******
******* 2.sub *******
******* 3.mul *******
******* 4.div *******
******* 0.exit *******
**********************************
**********************************
請選擇:>1
請輸入2個運算元:>2 3
5
**********************************
**********************************
******* 1.add *******
******* 2.sub *******
******* 3.mul *******
******* 4.div *******
******* 0.exit *******
**********************************
**********************************
請選擇:>3
請輸入2個運算元:>2 3
6
**********************************
**********************************
******* 1.add *******
******* 2.sub *******
******* 3.mul *******
******* 4.div *******
******* 0.exit *******
**********************************
**********************************
請選擇:>0
退出計算器
D:\gtee\C-learning-code-and-project\test_813\Debug\test_813.exe (行程 16872)已退出,代碼為 0,
按任意鍵關閉此視窗. . .
5.回呼函式
回呼函式就是一個通過函式指標呼叫的函式,如果你把函式的指標(地址)作為引數傳遞給另一個函式,當這個指標被用來呼叫其所指向的函式時,我們就說這是回呼函式,回呼函式不是由該函式的實作方直接呼叫,而是在特定的事件或條件發生時由另外的一方呼叫的,用于對該事件或條件進行回應,
比如上述計算器中的Calc函式,這個函式通過傳入的函式指標,去找到Add Sub Mul Div中的一個函式并呼叫它,
int Add(int x, int y)//int (*)(int, int)
{
return x + y;
}
int Sub(int x, int y)//int (*)(int, int)
{
return x - y;
}
int Mul(int x, int y)//int (*)(int, int)
{
return x * y;
}
int Div(int x, int y)//int (*)(int, int)
{
return x / y;
}
void Calc(int(*pf)(int, int))
{
int x = 0;
int y = 0;
int ret = 0;
printf("請輸入2個運算元:>");
scanf("%d %d", &x, &y);
ret = pf(x, y);//通過函式指標回傳呼叫呼叫加減乘除函式
printf("ret = %d\n", ret);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/293773.html
標籤:其他
下一篇:標準對話框
