指標是C語言的精髓,也是C語言的難點,下面是我學習途中整理的一些筆記,
1、指標和地址
和指標相關的兩個運算子:
&:取地址運算子 *:取指標所指向的記憶體單元的值
char c='A'; char *p=&c;
析:這個指標變數用于存放char型別變數的地址,定義的時候進行了初始化,把變數c的地址賦給了變數p,&c是用去取的變數c的地址,則變數p存放的是一個地址,此時我么說變數p指向了變數c,
char c2=*p+1
這兒的*p 的含義是取得指標變數p 所指向的變數的值,也就是取地變數c的值'A',因此變數c2的值為'B',
注意:char *p 定義了一個指向char型變數的指標,p存放的是一個char型變數的地址,而之后的*p是取的指標p所指向的變數的值,&c取的是變數c的地址,可以理解為 “指標= =地址”
2、指標的大小
在32為編譯器下,指標大小都是32位(4位元組),在64位編譯器下,指標大小都是64位(8位元組)
3、指標的定義和使用
C語言中的指標是專門用來存放記憶體地址的變數,每一個指標都有一個與之相關的資料型別,該型別決定了指標所指向的變數的型別,如一個char型指標只能指向char型變數,
指標定義的一般形式:資料型別 *指標變數名
①char *pc ②int *pi,p
第1個和第2個的指標在定義的時候未進行初始化,因此他們所指向的變數都是未定的,使用未初始化的指標通常會導致出錯,應該絕對避免使用未初始化的指標,
char *p; int i=*p;
如上的指標并未進行初始化,指標p并沒有明確的指向某個變數,在此情況下要取得p所指向的變數的值是很危險的,程式運行是很容易奔潰,
注意:避免使用未初始化的指標,在定義指標時最好將他初始化為NULL,即明確當前指標不指向任何變數,
例:int a=10;
int * p ;
*p = &a; //這行代碼導致記憶體問題,它是操作野指標所指向的記憶體
這樣的程式在運行中是會崩潰的,p沒有初始化,它的值是一個亂數,*p不是操作的p本身記憶體,是操作的p所指向的記憶體,操作野指標所指向的記憶體,正常的寫法是:p=&a
注意:所謂的空指標只是為了做一個標志,標志著這個變數沒人使用,千萬不要去操作空指標所指向的空間,給這個指標變數賦值為NULL,NULL其實也就是數字0
4、指標的操作
同型別的指標變數可以相互賦值
int a=10;
int * p1;
p1 =&a;
int *p2=p1; //同型別的指標變數相互賦值
*p2 = 222;
printf("a=%d\n",a); //可以有多個指標變數指向同一塊記憶體
回傳局部變數的地址(堆疊區空間)
1 in func()
2 {
3 int a =10; //區域變數
4 return &a;
5 }
6
7 int main()
8 {
9 int *p;
10 p=func(); //記憶體會出問題
11 *p=111;
12 return 0;
13 }
決議:1、定義在{}內部的變數為區域變數,區域變數由系統來分配空間,離開{}后系統自動回收,用戶不能在使用這塊空間,
2、p=func(),相當于把&a賦值給p ,p指向a的地址,但是func()函式呼叫完畢后,a會自動釋放,空間被系統回收,用戶不能在操作
3、*p=111 , 把 111 賦值給p指向的記憶體空間,這中操作是不行的,就是上面說的野指標問題
堆區空間的使用
只要程式不結束,只要不手動區釋放記憶體,空間是不會被回收的
int * func()
{
int * p1;
//1、p指向堆區空間,堆區空間需要呼叫malloc函式回傳
//2、只要程式不結束,用戶不free,堆區空間就一直存在
//3、int * 是指向int 型的變數,所以需要分配sizeof(int)大小的空間
p1=(int *)malloc(sizeof(int )) ;
return p1;
}
int main(void)
{
int * p2;
p2 = func(); //通過函式回傳堆區空間地址
if(p2==NULL) //空間分配失敗
{
printf("p2==NULL\n");
return -1;
}
*p2 =111;
printf("p2=%d\n",*p);
//1、一個malloc對應一個free
//2、free(p)不是釋放p的空間,而是釋放p所指向的堆區空間
//3、free完的空間不能在使用,同一塊記憶體只能釋放一次
free(p);
p= NULL;
}
決議:1、從main函式開始,定義了一個指向int型變數的指標,然后把func()函式的回傳值p1賦值給p2,
2、p1申請了一塊堆區空間,假設空間大小為0xaabbb,func函式回傳p1,則p2的值也為0xaabbb,
3、指標變數p2也指向堆區空間,當func呼叫完畢,func內部的p1自動釋放,但是堆區空間不釋放,
4、 *p2=111,其實是給p2指向的堆區空間賦值,用完之后需要釋放空間,free(p)釋放的不是p的空間,而是p所指向的堆區空間
5、指標和陣列
int a[]={2,4,6,8,10}
int *p1=a;
int *p2=&a[0];
指標指向陣列a的第一個元素,即a[0],指標p2也是如此,指標變數p1和p2存放的都是a[0]的地址,
在指向陣列某個元素的指標上加上或減去一個整型數值,就可以指向另外一個陣列元素,前提是沒有超出陣列的范圍,如下:
p1=p1+3此時p1指向a[3],存放陣列元素a[3]的地址,
注意:1、在指標上進行加減運算后所得到的指標,必須指向同一個陣列或指向陣列存盤空間的下一個單元,
2、不能對陣列名執行++,- -操作,比如a++是不合法的,這是因為a是陣列名,它是陣列的首地址,它的值在程式運行程序中是固定不變的,是常量
陣列元素也可以是指標型別,這種陣列稱為指標陣列也就是說指標陣列的每一個元素都是指標變數,
指標陣列定義的一般形式為:型別名 *陣列名[陣列長度] 如:char *a[5]
運算子*的優先級低于運算子[],因此a先與[5]結合,形成a[5]的形式,它顯然是一個陣列,然后在與*結合,表示陣列元素的型別為指標,每個陣列元素都指向一個整型變數,這里的a就是一個二級指標,它表示指標的指標,
#include<stdio.h>
int main()
{
int a[2][5]={1,3,5,7,9,2,4,6,8,10};
int (*p)[5],i;
p= a;
for(i=0;i<5;i++)
{
printf("%d",(*p)[i]);
}
printf("\n");
p++;
for(i=0;i<5;i++)
{
printf("%d",(*p)[i]);
}
printf("\n");
return 0;
}
輸出結果:1 3 5 7 9
2 4 6 8 10
決議:1、int (*p)[5],表示p是一個指標,它指向含有5個元素的一維陣列,p也只能指向一個包含5個元素的一維陣列,p就是該一維陣列的首地址,這里*p兩邊的括號是不可少的,因為[]的優先級bi*高,
2、p= a,p指向二維陣列a的第一行,然后通過(*p)[i]訪問該行的每一個元素
3、p++,使p指向二維陣列的a的第二行,
注意:區別int(*p)[5]和int *p[5],前者是一個指標,它指向一個含有5個元素的陣列,后者是一個陣列,它的長度為5,陣列中每一個元素指向一個整型變數,
6、指標和函式
指標做函式的引數
函式的引數不僅可以是整型、字符型、實際上也可以是指標型別,它的作用是將一個變數的地址傳送到函式中,
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void change(int i,int *p)
{
i++;
if(p !=NULL)
(*p)++;
}
int main()
{
int a=5,b=10;
change(a,&b);
printf("a=%d,b=%d\n",a,b);
return 0;
}
輸出結果 a=5,b=11
決議:1、主函式呼叫了change函式中,一個實參為變數a,另一個實參為變數b的地址,因為change函式的第二個引數為指標,所以必須傳遞一個變數地址,對于指標型實參,引數也可以是NULL,因此change函式中必須檢查
p是否為NULL,如果實參為NULL,那么陳述句(*p)++將導致程式崩潰,
2、函式的引數是局限于該函式的區域變數,函式呼叫時,系統為函式的區域變數分配記憶體,在主函式中呼叫change時,系統分配了8個位元組的記憶體,4個位元組保存變數i的值,4個位元組保存指標變數p的值,i保存的是數值5
而指標變數p保存的是變數b的地址,i++并不是作用于變數a,而是作用于剛剛分配好的4位元組存盤空間,此時i的值為6而a的值依然是5,
3、對于(*p)++,p保存的是變數b的地址,*p的含義是系統根據地址找到變數b,然后對它執行加1操作,然后b的值就變成了11,函式呼叫結束后,系統分配的記憶體也就被回收了,
注意:如果一個函式的引數中有指標,為了程式的健壯性,在函式中需要檢查引數是否為NULL,
7、指向字串的指標
C語言中訪問一個字串有多種方法,
①、用字符陣列存放一個字串
char string[ ] = "Linux C";
printf("%s\n",string);
string是一個字符陣列名,它同時也是該字符陣列的首地址,也就是“Linux C”這個字串的首地址
②、用字串指標來指向字串(字串指標)
char *p="Linux C" ;
printf("%s\n",p);
C語言對字串常量的處理,在記憶體中開辟出一個字符陣列來存盤該字串常量,并把開辟出的字符陣列的首地址賦值給p,
注意:string[0] = 'A' 是可以的,而p[0] ='A' 是非法的,因為p指向的是字串常量,常量的內容不可改變,把p指向另外一個字串常量或字符陣列是合法的,如:p ="Hello World" ; p= string.
示例:在函式中實作字串拷貝
#include<stdio.h>
#include<stdlib.h>
void copy_string1(char src[],char dst[]) /*copy_string1(char *src,char *dst)*/
{
int i;
for(i=0;src[i] != '\0';i++)
dst[i]=src[i];
dst[i]='\0';
}
void copy_string2(char *psrc,char *pdst)
{
for(;*psrc != '\0';psrc++,pdst++)
*pdst=*psrc;
*pdst='\0';
}
int main()
{
char a[] = "Linux C Progam",b[20],c[20];
copy_string1(a,b);
copy_string2(b,c);
printf("%s\n%s\n%s\n",a,b,c);
return 0;
}
決議:1、copy_string1函式的引數是兩個陣列,而copy_string2函式的引數是兩個指向字串的指標,這兩種方式是完全等價的,copy_string1(char src[],char dst[])等價于copy_string1(char *src,char *dst)
2、copy_string1(a,b),把a和b的值(該值是指向陣列第一個元素的指標,而這個指標就是存放陣列第一個元素的記憶體地址)賦給src和dst,src和dst保存的是a和b的首地址,copy_string2(z,c)也一樣,
psrc和pdst也都保存這陣列a和c的第一個元素地址,
3、對于copy_string1(a,b),a的值是a[0]即字符L記憶體地址,src的值也就是a的值,因此通過src可以很方便的對陣列a進行操作,b也是一樣的
注意:陣列,如char a[20],a是指向陣列第一個元素的指標,它的值不可以被該變,它在程式運行程序中始終指向陣列的第一個元素,而在函式定義中,如copy_string1(char src[],char dst[]),src也是一個指標,它的值
是可以改變的,也就說它可以指向其他字串指標中,
字符陣列由若干個元素組成,每個元素存放一個字符,而字串指標中存放的是地址(字串的首地址),而不是將字串存放在字串指標中
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/33476.html
標籤:C
上一篇:【網路編程02】簡單聊天程式
