1. 內(nèi)存地址和內(nèi)存空間
學(xué)習(xí)指針的相關(guān)知識(shí),首先需要了解下內(nèi)存。我們通常所說的內(nèi)存,往往指的是內(nèi)存空間。其實(shí)內(nèi)存是由內(nèi)存地址和內(nèi)存空間共同組成的,且內(nèi)存地址和內(nèi)存空間是一一對(duì)應(yīng)的。
內(nèi)存地址是一個(gè)編號(hào),代表一個(gè)內(nèi)存空間的位置。
內(nèi)存空間是存儲(chǔ)數(shù)據(jù)的空間,真正保存數(shù)據(jù)的地方。
在計(jì)算機(jī)中存儲(chǔ)器的容量是以字節(jié)為基本單位的。也就是說一個(gè)內(nèi)存地址代表一個(gè)字節(jié)(8bit)的存儲(chǔ)空間。

有個(gè)非常形象的比喻:
內(nèi)存就像小區(qū)房子一樣,內(nèi)存地址代表房子的門牌號(hào),內(nèi)存空間是房子內(nèi)居住的空間。
已經(jīng)被出售了的,代表空間被申請(qǐng)占用了;
已經(jīng)辦理入住的,代表空間被數(shù)據(jù)寫入;
正在出售的,代表此空間空閑;
2. 指針相關(guān)表示方法
-
2.1 指針,指向某空間的標(biāo)記,比如:
char *charPointer —> charPointer是指向 char 類型的指針。char 內(nèi)存中占用一個(gè)字節(jié)內(nèi)存空間,charPointer指向了這個(gè)字節(jié)的內(nèi)存地址
int *intPointer —> intPointer是指向 int 類型的指針。int 內(nèi)存中占用四個(gè)字節(jié)內(nèi)存空間,ntPointer指向了這個(gè)四個(gè)字節(jié)中首字節(jié)的內(nèi)存地址。
2.2 取地址
符號(hào) &,可以幫助我們獲取相關(guān)內(nèi)存中的地址。
指針就是先申請(qǐng)內(nèi)存空間,然后把指向這個(gè)數(shù)據(jù)內(nèi)存的首地址存入申請(qǐng)的內(nèi)存空間。
所以指針必須初始化才有意義。
int a = 1;
int *d ; //申請(qǐng)一個(gè)可以保存 int 類型內(nèi)存地址的指針
d = &a;// 把 a 的地址賦值給指針
printf("%d \n", a); // log結(jié)果:1
printf("%p \n", &a); //log結(jié)果:0x7ffeefbff59c
printf("%lu \n", sizeof(a)); //log結(jié)果:4
printf("%p \n", d); //log結(jié)果:0x7ffeefbff59c
printf("%lu \n", sizeof(d)); //log結(jié)果:8
- 2.3 解引用
我們已經(jīng)知道指針的內(nèi)存空間中保存了相關(guān)的數(shù)據(jù),那么怎么來(lái)使用這個(gè)數(shù)據(jù)呢?這里需要的就是解引用,也就是間接訪問。
需要注意的是:
NULL 指針 :什么都沒有指向的指針(默認(rèn) NULL = 0)
int a = 1;
int *d ;
d = &a;
printf("%d \n", a); // log結(jié)果:1
*d = 5; //這里就是解引用(間接訪問) :* 這個(gè) * 是解引用指針
printf("%d \n", a); // log結(jié)果:5
3. 指針的相關(guān)運(yùn)算
通過取地址,指針,解引用……相關(guān)的表示,我們衍射出很多相關(guān)的運(yùn)算
- 3.1 通過指針操作相關(guān)的變量
int a = 12;
int *d = &a;// 創(chuàng)建指針 指向a的地址
*d = 10 - *d;// 這里其實(shí)就修改了a變量
printf("%d \n",a); // log 結(jié)果:-2
//先取地址 然后解引用
*&a = 25;// 這行等價(jià)于 a = 25;
printf("%d \n",a);// log 結(jié)果:25
printf("%p \n",&a);// 取a 的地址::0x7ffeefbff59c
//對(duì)這個(gè)地址數(shù)值進(jìn)行操作
*(int *)0x7ffeefbff59c = 20; // (int *)0x7ffeefbff59c ->代表指針常量,這個(gè)是把內(nèi)存地址強(qiáng)轉(zhuǎn)成一個(gè)指針
printf("%d \n",a);//打印結(jié)果 :a = 20;
-
3.2 多級(jí)指針
多級(jí)指針是指,把一個(gè)變量的地址賦值給一個(gè)指針,然后把這個(gè)指針的地址又賦值給新的指針……多級(jí)指針的解引用,也是一層層通過相關(guān)的地址找到最終變量,然后才能對(duì)變量進(jìn)行相關(guān)操作
int a = 12;
int *b ;
b = &a;
int **c;//指向指針的指針 :指向指針的地址
c = &b;
/* 解引用: **
第一個(gè)* 指向b 的內(nèi)存地址,得到指針b
第二個(gè)* 指向a 的內(nèi)存地址,得到變量a
*/
**c = 25;
printf("%d \n",a);//打印結(jié)果 :a = 25;
- 3.3 練習(xí)指針的++,--
char c1,c2,c3;
char *cp;
//定義初始化函數(shù)
void setup() {
c1 = 'a';
c2 = 'f';
c3 = 'c';
cp = &c1;
}
int main(int argc, const char * argv[]) {
setup();
printf("%c : %c : %c \n",c1,c2,c3);//a : f : c
printf("%p : %p : %p \n",&c1,&c2,&c3);//0x100001030 : 0x100001031 : 0x100001032
printf("%p : %p \n",&c1,cp);//獲取 c1 cp 地址:0x100001030 : 0x100001030
printf("%p \n",&cp);//指針的地址:0x100001038
printf("%c \n",*cp);//指針指向的地址:a
printf("%c \n",*cp + 1);//指針指向的地址:b ;原因:a的asc值97 ,97+1 = 98 (b的asc值)
printf("%p : %p \n",cp + 1,&c2);//指針指向的地址:0x100001031 : 0x100001031
printf("%c \n",*(cp + 1));//指針指向的地址:f : 原因:指針指向的內(nèi)存地址 + 1,指向了下一個(gè)byte的內(nèi)存
printf("%p : %p \n",++cp,&c2);//0x100001031 : 0x100001031
// 練習(xí) // 結(jié)果:
setup();
printf("%c \n",*cp);//a
printf("%c \n",++*cp);//b
setup();
printf("%c \n",*cp++);//a
setup();
printf("%c \n",*(cp + 1));//f
setup();
printf("%c \n",++*cp++);//b
setup();
printf("%c \n",++*++cp);//g
}
4. 指針與字符串
字符串指針->指向的是字符串第一個(gè)字符的內(nèi)存地址;
字符串都是以"\0"結(jié)尾;
計(jì)算字符串長(zhǎng)度的demo:
// 計(jì)算字符串長(zhǎng)度
int my_strlen(char *string){
int length = 0;
while (*string++ != '\0') {
length++;
}
return length;
}
int main(int argc, const char * argv[]) {
char *name = "bill gates"; //指向字符串:指向的是第一個(gè)字符;
printf("%s \n",name);//bill gates
unsigned long length = strlen(name); // 字符長(zhǎng)度 c系統(tǒng)自帶函數(shù)
printf("%lu \n",length);//10
//自定義函數(shù)
int myLength = my_strlen(name);
printf("%d \n",myLength);//10
}
查找字符串中的某個(gè)字符demo
//查找字符串
int find_char(char **strings,char word,int count){
char *string ;
char character;
int num = 0;
while ((string = *strings++) && num++ < count) {
while ((character = *string++ ) != '\0') {
if ( character == word ){
return 1;
}else{
printf("繼續(xù)查找……%c \n",character);
}
}
}
return 0;
}
int main(int argc, const char * argv[]) {
char *s0 = "zero", s1 = "first", s2 = "second",s3 = "third",*s4 = " ";
int total = 5;
char *strs[total];
strs[0] = s0;
strs[1] = s1;
strs[2] = s2;
strs[3] = s3;
strs[4] = s4;
char word = 'a'; //查找詞
if ( find_char(strs, word,total) ){
printf("find_char :%c \n",word);
}else{
printf("unfind_char :%c \n",word);
}
}
5. 指針的運(yùn)算常在數(shù)組中使用
算數(shù)運(yùn)算:指針 +(-) 整數(shù) ,指針 - 指針(只限于指向同一數(shù)組的指針)
單獨(dú)的兩個(gè)指針進(jìn)行 加減乘除 是沒有意義的
關(guān)系運(yùn)算(只限于指向同一數(shù)組的指針): >, >=, <, <=, !=, ==
// 遍歷數(shù)組
void logArray(char arr[],int count){
for(int i = 0 ;i < count ; i++){
printf("%d : %c \n",i,arr[i]);
}
}
int main(int argc, const char * argv[]) {
char *cp;
int total = 5;
char characters[total];
/*cp < &characters[total];
指針的邏輯比較
*/
for (cp = & characters[0]; cp < &characters[total]; cp++) {
*cp = 'a';
}
logArray(characters, total);
char * cBegin = &characters[0];
char * cEnd = &characters[total];
for (cp = cEnd; cp >= cBegin; cp--) {
*cp = 'b';
}
logArray(characters, total);
/*long count = cEnd -cBegin;
指針的加減,判斷數(shù)組中元素間隔的距離
*/
long count = cEnd -cBegin;
printf("array count : %ld \n",count);
}
6. 指針和函數(shù)
- 6.1 函數(shù)的返回值可以是指針
int* find_intValue(int arr[] ,int arr_count ,int value ){
for (int i = 0 ; i<arr_count; i++) {
if (arr[i] == value){
return &arr[i];
}
}
return NULL;
}
int main(int argc, const char * argv[]) {
// 數(shù)組中查找指定元素,找到返回元素指針,否則返回 NUL
int total = 5;
int arr[total];
for(int i = 0 ;i < total ; i++){
arr[i] = i;
}
int *result = find_intValue(arr, total, 3);
if (result != NULL) {
printf("find Value \n");
}else{
printf("can not find Value \n");
}
}
- 6.2 函數(shù)的參數(shù)也可以是指針
// 數(shù)據(jù)交換
void swap_0(int a ,int b){
int temp = a;
a = b;
b = temp;
}
void swap_1(int *a ,int *b){
int temp = *a;
*a = *b;
*b = temp;
}
int main(int argc, const char * argv[]) {
//交換
int m = 10;
int n = 20;
// 傳值 操作,并不能完成數(shù)據(jù)的交換
//參數(shù)把數(shù)值拷貝一份,函數(shù)內(nèi)部進(jìn)行了交換,不會(huì)影響到外部;
printf("swap_0交換前 m= %d : n= %d \n",m,n);
swap_0(m, n);
printf("swap_0交換后 m= %d : n= %d \n",m,n);
// 傳址 操作,才能完成數(shù)據(jù)的交換
//參數(shù)把地址拷貝一份,函數(shù)內(nèi)部根據(jù)地址獲取變量進(jìn)行了交換,所以在內(nèi)存中數(shù)據(jù)發(fā)生了交換
printf("swap_1交換前 m= %d : n= %d \n",m,n);
swap_1(&m, &n);
printf("swap_1交換后 m= %d : n= %d \n",m,n);
/*log結(jié)果:傳值操作和傳址操作是明顯不同的
swap_0交換前 m= 10 : n= 20
swap_0交換后 m= 10 : n= 20
swap_1交換前 m= 10 : n= 20
swap_1交換后 m= 20 : n= 10
*/
}