陣列、指標、字串大禮包!
指標:
int *p,q //表示p是指標,q是int;
int *p,*q //表示p和q都是指標
(*是和變數在一起的,是用來宣告指標)
指標有什麼用呢?
- 1.用
void swap(int* ,int* );
交換值 雖然沒有返回值,但是利用指標可以實現
如果是傳進兩個整型,那就必須要有返回值,不然函式內部的交換隻是拷貝,對外界無影響。於是就很麻煩啊;
使用場景特點:像swap()
這樣的函式,函式返回有多個值,於是就要用到指標節省力氣 - 2.同樣是
void maxmin();
要找陣列中的最大值和最小值,於是有兩個輸出量,於是就用到指標 - 3.在用函式做除法的時候,既要用
int devide();
返回一個ret來判斷除數是否為0,如果除數不是0,則還要返回相除的結果,於是要用指標存放結果送出去。 - ※4.要對陣列中某個元素操作的時候 可以用指標
指標和陣列的愛恨情仇:
int sum(int *ar,int n);
int sum(int ar[],int n);
上面兩種函式原型等價,就算定義指標,下面也還是可以用[],當作陣列來處理
min=2;
int *p=&min;
這時,有*p=2
同時也有p[0]=2
,因為這時把min認為是陣列,即int min[1];
為什麼有陣列a[]和陣列b[],但是不能將a、b進行賦值?
因為int a[100];
相當於int * const a;
(陣列變數是const的指標,所以不能被賦值)
所以陣列在定義之後就不能再進行賦值了
int *q=a;
指標和const:
- 1.指標是const:指標不能再指向其他變數;
int * const q=&i;
- 2.指標所指向的變數是const:可以改變的變數的值,也可以改變指標的指向;
但是就是不能【通過這個指標】改變變數的值(即可以i=20
,但不能做*p=20
)
const int *p=&i;
(你給我一個變數的指標,我保證不通過這個指標去改變它所指的變數的值!)
(比如將常數指標傳到函式中時,就可以有效避免它所指的變數值被更改,相當於讓函式發誓!↑)
※eg.區分以下含義
int i;
const int* p1=&i; 所指變數int const* p2=&i; 所指變數
int const p3=&i; 指標
判斷依據:const在的前面還是後面
思路:從左到右,碰到就判定為指標的定義,再往右就是指標變數,如果後碰到const那肯定是對指標作用的了
- 3.const陣列:
const int a[]={1,2,3,4,5,6,7,8,9};
如果是int a[]={1,2,3,4,5,6,7,8,9};
那麼a陣列本來就已經是個const指標了;
雖然整體不能進行賦值,但是單個單元還是可以進行修改,如a[0]=2;
這時候再加一個const
,那麼則代表陣列的每個單元都是const int
(比如在函式中使用,傳進一個const int a[10]引數,可以有效保護陣列不被修改,安全)
關於動態記憶體分配:
int number;
int a;
printf(“輸入數量:”);
scanf("%d",&number);
//int a [number];
a=(int)malloc(number*sizeof(int));
(eg.若是直接a=malloc(number*sizeof(int));
那麼得到的【預設】是void*
型別,如果是a=(int*)malloc(number*sizeof(int))
就得到了int*型別)
最後在程式結束前來一個free(a); 而且只能還申請來的空間的【首地址】!!(意思就是 如果進行過p++,或者是指向了其他變數,那麼就要將指標歸位,才能釋放記憶體free(p)
(理解:malloc的時候,系統會記住它給了你什麼,你還的時候要原封不動地還給它free)
※常見問題:
- 1.申請了沒free→長時間執行記憶體逐漸下降
新手:忘了
老手:找不到合適地free時機 - 2.free過了再free,系統會奔潰
- 3.地址變過了,直接去free
So 好習慣:有malloc 馬上跟上free!
字元陣列和字串:
區別:字元陣列不能用字串運算方式進行運算
前者只是一個個字元 後者再最後有一個’\0’
這個\0表示字串結束了,但它不是字串的一部分
char *str="Hello";
雖然只有五個單詞,但卻佔留個位置,因為編譯器會自動顯示一個’\0’
"Hello"會被編譯器變成一個字元陣列放在某處,這個陣列的長度是6
因為結尾還是表示結束的’\0’,兩個相鄰的字串常量會被自動連線起來
字串特點:
字串常量:
char *s="Hello,world!";
char *s2="Hello,world!";
於是會發現兩個指標指向同一個位置,而且是個小地址
而且為常量,不能改;
如果你想修改某個值,則必須寫成字元陣列:
`char s[]=“Hello,world!”;
那麼為什麼呢?有什麼區別呢?
char *s=“Hello,world!”;(我是一個指標,我指向那個重要的地方!只讀!)
char s[]=“Hello,world!”;(我是一個數組,東西就在我這裡!)
那麼是用指標還是陣列?
char *是字串嗎?
不一定,可能是單個字元,也可能是字元陣列
char *a={'H','e','l','l','o'};
這個時候表示字元陣列;
char *a={'H','e','l','l','o','\0'};
這個時候表示字串**(字元陣列後有\0)**;
字串的輸入和輸出
char string[8];
scanf("%s",string);
printf("%s",string);
scanf();
讀入一個單詞(到空格、tab或回車為止)
小知識:
- tip1:
/
"123456"等價於{‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,’\0’}
它表示一個字串常量,雖然表面看上去有六位,但是實際上卻又七位
這是因為它獨特的字串血統,最後會自動有一個\0;
/
- tip2:
/
char a[7]中是由a[0] a[1] a[2] a[3] a[4] a[5] a[6]
其中的**'7’是指一共有7個元素**
然而事實上,由於"1234567"賦值給陣列得到的是字串陣列,最後有一個’\0’
所以啊,"1234567"其實是有8位的!!
於是,如果讓a[7]="1234567";
陣列會溢位!!
/
- tip3:
/
那麼如何規避這種情況呢?
可以在scanf()
的時候做手腳
scanf("%6s",word);
** 在%後面加一個’6’就可以只拿前面6位了!!**
一共有7個元素,於是最後一個元素位置拿來放'\0'
這就完美了~
/
- tip4:
/
空字串:
char buffer[100]="";
意思:這是一個空的字串,buffer[0]=='\0'
他是個有效的字串
但是
char buffer[]="";
意思:這個陣列的長度只有1!而且只有一個元素,buffer[0]='\0';
於是可以得出結論:
自己定義陣列長度常常有浪費(冗餘)
但是若是空著給編譯器定義長度,他就很小氣地幫你卡死
即你右邊有多少,他就給多少空間你
/
- tip5:
/
字串中的套娃:二維陣列和多重指標
定義有效的二維陣列:
a[][]={“Hello”,“World”};(錯誤)
a[][10]={“Hello”,“World”};(正確)(這個時候,a[0]=char [10]
)
即一定要給定每個元素的長度(第二個框裡面要有內容):如"Hello"和"World"的長度
但是啊,我很噁心,給它scanf()
了一個很長的字串,超過了它的定義長度,那怎麼辦呢??
那我就把char a[][10]
寫成char *a[]
,這個時候啊,a[0]
就變成了char *
,這不就沒有長度限制了嘛?
(ps.char *a[]
就表示指標陣列,數組裡面都是指標(可以理解為很多不限長度的字串~))
/
- tip6:
/
※字串的賦值:(理解/重要!)
1.初始化賦值,char a[]="Hello,World!";
2.先定義,然後***迴圈***給每個元素賦值;
3.動態賦值:同樣先定義,然後***迴圈***給每個元素scanf()
總結:想要一次性修改陣列的值?不可能,它在定義的時候才可以直接一次性賦值
其他情況必須使用迴圈!!一個一個改!!
/
小知識over~
單字元的輸入輸出:
上面這個Try to understand??
(程式碼原理沒看懂???)
int getchar(int )
和int putchar(int )
都是int型別
,如果是字元的話就取ASCII碼
;
- Q1:為什麼是
int
而不是char
呢?因為char
的範圍太小,而EOF
在stdio
中預設為-1
,於是就用int
才能取到; - Q2:
getchar
明明是得到一個字元,為什麼能復讀字串呢?(本質是因為我按一個鍵,它提取一個鍵)
getchar()
是實時的,碰到它,它就等著使用者按鍵,【並且把所有的操作存放在stdin流中】
(首先有一個緩衝區shell
,它為中間商**,我們對鍵盤的所有操作都被儲存在那裡)
getchar()
函式就是小弟,給shell
【一個一個】地送東西,我們按什麼他就送什麼
當我們按下回車的時候,getchar()
知道是最後一個貨物
然後讓putchar()
一個個從shell
這個倉庫中輸出,把自己運過去地一件件**小物品***(每一個字元)***送到我們看得到地介面上輸出;
#include<string.h>裡面的函式使用:(只能作用與字串)
- 1.strlen:用於輸出字元
strlen(“Hello”)==5;
sizeof(“Hello”)==6;
(另外:若char a[100]=“Hello,World!”,那麼strlen(a)=12,因為它是根據’\0’判斷的
此時sizeof(a)=100,因為它是看空間的)
如何寫一個strlen函式
?
※思路:傳入一個指標(即字串),從第一個開始遍歷,一直到'\0'
- 2.strcmp:用於比較
int strcmp(const char *s1,const char *s2);
比較兩個字串,返回規則:
0:s1==s2
1:s1>s2
-1:s1<s2
理解:
char s1[]=“abc”;
char s2[]=“abc”;
那麼s1==s2嗎?NO!因為這個時候比的是地址!地址當然不一樣
那麼strcmp(s1,s2);得到什麼的?得到0,因為字串一樣!
所以判斷兩個字串是否相等,不能直接比較,一定要用函式strcmp
strcmp()函式是用ASCII碼比
(ps.比較"abc"和"abc “,出現的結果是-1,因為第二個字串有空格” ")
※區分:
定義陣列char s1[]=“abc”; 這時,s1表示陣列的【首地址】;
定義指標char *s1=“abc”; 這時,*s1表示陣列中第一個元素的值;這時可以通過s1++來遍歷噢`
- 3.strcpy函式:用於copy(抄作業)
具體使用方法:
注意函式中的兩個引數,是先找一個盒子,然後再把東西抄襲進去
最後返回的是到盒子裡面找抄到的東西
那麼什麼叫不能重疊呢?
因為strcpy
的功能抄作業***
所以我們經常用它來複制一個字串:先malloc一塊地址(具體多大要思考噢),然後strcpy抄作業*
具體形象解釋:(右側)
(小聲BB:·strlen·測量的是字串常量長度,不包括’\0’,所以要+1)
這裡的功能是先申請一塊動態記憶體,讓dst指標指向這片區域
然後用函式抓住dst這個空盒子,往裡面塞src
之後函式完事就把盒子送出去(返回)
- 4.strcat
具體使用:
形象表現:
strcpy
和strcat
的對比:
strcpy
是抄作業***,它裡面的第一個引數是空的(它是白卷!太不努力了啊!)
而strcat
則是自己寫到一半,發現題目不會做,再去抄別人的答案
strcpy
是從dst[0]
開始抄src
;
strcat
則是從dst[strlen(dst)]
開始抄src;
※共同點:操作者(學渣)都是函式裡面的第一個引數(即第一個字串指標),第二個引數(即第二個字串指標)都是學霸*
But,因為第二個引數太長,可能會超出第一個引數的界限,於是就會出現安全問題!!
(即學霸的答案太詳細,學渣發現自己字太大抄不下了??)
那該怎麼辦呢??
不慌,下面有安全版本的函式,可以限制抄作業的量,以保證不會超過界限
船新版本!比原來函式多了一個n,而且函式多了一個引數,來限制抄作業的數量
對於strcmp
,它變成strncmp
之後,多了個引數n,可以把兩個字串從左到右限制為n個參賽選手的個數
- 5.strchr和strrchr
功能是查詢
前者是從左往右找
後者多了個r
,是從右往左找
第一個引數是字串
第二個引數是要找的字元(注意是int型別
啊!!不是char
,比如'l'
)
返回的是指標,指向要找的字元
如上圖,怎麼在"Hello"中找到第二個’l’呢?
啊可惡,當然是找兩次啊!!!
第一次指標p返回第三個字元'l'
的地址,這個時候指標p
是返回"llo"
,那麼p+1
就是"lo"
接下來,再來一次p=strchr(p+1,'l');
這樣返回的指標p
就指向第二個'l'
,其實也就是指向'lo'
(後面有\0
)
就找到了第二個'l'
的位置了!!
妙啊~~
那麼我們已經會取"llo"
了
如果我想取前面的"He"呢?該怎麼辦?
p指標
指向第一個'l'
char c
用於存放這個'l'
,最後要復原字串用啊!!
然後通過*p=’\0’把’l’替換掉
於是字串變成了"He\0lo\0"
這裡其實變成了兩個字串,一個是"He"
,一個是"llo"
又因為s
是陣列名,代表陣列的首地址
,所以字串s就是第一個字串"He"
然後指派一個指標t
用strcpy
去抄s
的作業就行了
最後的最後,復原字串,把"He\0lo\0"
變回"Hello\0"
就用*p='l'
就OK了
圖解:
真的是非常的Amazing啊!!!
- 6.strstr和strcasestr:
字串中找一個字串(返回的是一個指標)
strstr
就是在字串中找一個字串
strcasestr
功能相同,就是**【忽略大小寫】**!!