地址:
在C語言中定義的每一個變數都擁有屬於自己的儲存單元的地址,
通常我們認爲一個變數擁有兩個值,一個左值(地址)一個右值(變數的值)
在scanf()中使用到的 取地址符(&),就是向這個地址中輸入數據從而給變數賦值
存取地址 就需要用到指針,用於儲存地址
對於物件的存取
1.直接存取:通過物件名去實現存取,但有作用域的限制
2.間接存取:通過地址去存取,不受作用域影響
指針
用於儲存其他物件的地址值。
在32位元的系統中佔4個位元組,64位元的佔8個位元組
1)指針的定義
指針變數的定義和其他變數的相同也是: 變數的型別 變數名= 初始值
int * p = NULL; 指針
這裏表示定義了一個int * 型別指針p;他初始化直向空(空指針),
還有一種定義: int * p ; 這種定義,沒有賦初始值,他是一個(野指針)指向未知,不知道是否能存取,能否修改。通常我們都是先定義空指針。在給他賦值。
2)與指針相關的運算:&取地址符 *指向運算子
在1中定義p 需要賦值(或者申請空間malloc),才能 纔能實現他的作用
賦值 int a = 5;
p = &a; //此時將a的地址付給了p,也可以說是p指向了a。
此時 *p == 5;
兩個符號可以約掉 *&p == p 但是&*不能
3)指針變數作爲函數的參數
形參 = 實參的值,
指針傳入的是地址值,形參和實參指向的是同一個地址,改變形參的值同時會時實參的值發生變化。
在實現交換兩個數的值時,傳入地址,改變地址對應的值,可以實現交換,,但是注意的是,有題目是交換了傳入的地址,也就是兩個形參的地址做了交換,這樣並不能交換實參的值。
4)陣列與指針
陣列元素是和普通元素是一樣的,陣列元素也有它自己的地址。
陣列元素也有左值和右值,只不過陣列的每一個元素的地址是相鄰。
陣列名 可以代表首元素的地址。
int a[10];
a => &a[0],陣列名a當作指針來看。
指針加減,看當前的指向,如一維陣列的p=>a =》a[0] p+1 =>a[1];
5)指針常數和常數指針
指針常數: 指針本身不能改變的,但是指向的空間裏面
的內容可變。
如: 陣列名 int * const a;
常數指針:是指向常數的指針。
指向的物件是常數,那麼這個物件不能夠改變的。
指針本身可以被改變,但是指向的空間的內容不能改變。
const int * a;
int const * a;
6)陣列名和指針
陣列名是一個指針常數。
陣列名可以代表整個陣列,但有些情況下是當作指針來看的。
如陣列 : int a[10];
a <=> &a[0];
a代表的是a[0]的地址 a+1代表的是a[1]的地址。
此處的+1移動的是多長需要看當前a代表的是什麼來決定。
如 : int a[3][4];
此時 a 代表的就是&a[0] a+1 =&a[1] 看成3行4列,可以理解爲。首先指向的是第0行,加1 ,指向的是第1行,其地址在數值上等於他們那一行的第一個元素的地址。
如:一維陣列
int a[5] = {1,2,3,4,5};
int * ptr = (int *)(&a + 1); //此時ptr指向了a陣列後面的空間
printf("%d %d\n", *(a + 1), *(ptr - 1)); // 2 5
如:二維陣列
*(a[1] + 2) 此時 a[1] 應該當作指針來看
*(&a[1][0] + 2)
=> *(&a[1][2])
=> a[1][2] //!!!!表示第1行第2列的那個元素 (不要寫代表值)
練習題:
**a[3][4] 寫出表達式的含義 **
&a[1][0]: 第1行第0列的元素的地址
a[2]: (1)代表a[2]這一個陣列 (2)代表第2行第0列元素的地址
&a[0] + 1: =》 &a[1] (1)第一行的地址
*(&a[1][0] + 1): 代表第1行第1列的那個元素
7)指針陣列 與 陣列指針
a)指針陣列:
指針陣列是一個數組,不過陣列中存放的全是指針
定義如:int(任意型別都可以) * p[10]; 相當於一個數組裏面存放了10個int * 型別的數據
b)陣列指針:
陣列指針是一個指針,用於指向一個數組
定義: int (*p)[10]; 指向一個有10個元素的陣列 <=> int a[10];
8) 字串與指針
字串就是又一串字元組成 (String型別????C語言中好像還沒涉及到這個型別)
字串的表達方式有兩種
a)字元陣列
char a [6] = {'a','a','a','a','a','\0'};
b)字元指針
char * p = "abcd"
字串末尾會預設爲"\0" 表示字串結束
字串我們只需要儲存他的首地址即可,讀取時會一直讀到0結束
在C語言裏面字串是儲存在一個叫做 .rodata(只讀數據)的記憶體區域。
只讀區域無法修改
char * p = "123456";
此時執行 *(p+1)=‘a’; 會出現段錯誤,因爲此區域的數據是無法修改的。
字元陣列
char a [6] = {'a','a','a','a','a','\0'};
//與普通的陣列相同,儲存在一個.data/棧空間,陣列區域數據是可讀可寫的
同時大小也是與陣列相同
sizeof(a)=6;
注意!!!使用sizeof時
char s[] = {'a','b','c','d','e'};// 這種末尾沒有0
sizeof(s) = 5
char s[] = {"abcde"}; //這種末尾會新增0
=> char s[] = {'a','b','c','d','e','\0'};
sizeof(s) = 6
《strlen 計算的值不包括0》
9) 字串常用函數
(1) strlen: 用來求一個字串的長度
標頭檔案: #include <string.h>
計算字串的長度,不包含'\0'
l = strlen("abcd\nabc"); == 8 // \n看成一個?? \大多數都是表示跳脫
l = strlen("123\0123abc\0abc"); == 8 //讀到0結束,但是第一個\0並不是末尾,
我們碰到這樣的需要判斷他是否爲8進位制的樹,此時\012是當成8進位制來看的。
\XXX 可接一個、兩個或三個八進制的數
(2)strcpy/strncpy 字串拷貝
標頭檔案: #include <string.h>
strcpy(char *dest, const char *src);
複製 一個字串到另一個字串,都是從頭開始
strcpy(沒有考慮越界)strncpy增加了長度限制,
(1) 遇到\0拷貝結束,此時\0也會被拷貝
(2) 已經拷貝n個字元了(後面的\0不會自動拷貝,除非第n字元爲\0)
(3)strcmp/strncmp
字串比較,同樣strcmpy是全部比較,strncmp是限制比較的長度
字串比較是一個一個字元比較,
如果 c1>c2 返回 1,如果c1<c2 返回-1
如果相同則繼續比較到下一個字元,知道結束,返回0
(4)strcat / strncat
源字串空間應該足夠大。將字串鏈接到尾部。
同樣 拷貝到\0結束,strncpy限制大小