C语言:随笔5--指针1
指針雖然是一個重要的概念,但是一點也不復雜。但他的確是C語言的精華。
正確而靈活的運用它,可以有效的表示復雜的數據結構;能動態的分配內存;能方便的使用字符串,有效而方便的使用數組。
為了說清楚什么是指針,必須弄清楚數據在內存中是如何存儲的,又是如何讀取的。
那就從一個美麗的故事開始吧。。。。
比如,昨天小A去圖書館借一本“我是美女”這本書的時候,在路上不小心碰到了一個帥哥,然后小A就非常勇敢的沖了上去一把掏出手機,文了該帥哥加的電話號碼和地址,然后帥哥覺得小A實在長相一般不肯告訴他家的電話號碼,就只是說了他家的地址,他是住在單身公寓的102號房,那這樣小A就可以去找他了哈哈,因為小A已經知道了他家的地址了,那這就跟我們的內存掛鉤了,我們知道內存有兩個屬性,第一個內存是一個線性的存儲空間,有兩個屬性一個是地址,另一個是地址上邊存放的數據。OK,內存是用來存放數據的,就像我們的地址,每一個門牌號碼都有一個家,家里面就會放帥哥,就是一樣的道理。我們通過地址才能夠索引到某個我們要索引的內存,如果我們不知道他們的地址門牌號我們應該怎么找呢,不能一個個挨家敲門吧。所以,我們的內存都是有編好地址的,所以只要在固定的地址里面它會有對應的數據。我們存數據就是靠這個地址來索引的,那我就知道我的東西存在哪里了,也知道我的東西要去哪里拿。
內存區的每一個字節都有一個編號,這就是“地址”。如果在程序中定義了一個變量,在對程序進行編譯時,系統就會自動的給這個變量分配內存單元。這叫做局部變量。
在C語言中,對變量的訪問有兩種方式,直接訪問和間接訪問。
舉個例子就是:
為了開一個A抽屜,有兩種方法:
(1)一種方法是將A鑰匙帶在身上,需要時直接找出該鑰匙打開抽屜,取出所需的東西。-----直接訪問
直接訪問如:a=5;
系統在編譯時,已經對變量分配了地址,例如若變量a分配的地址是2000,則該語句的作用就是把常數5保存到地址為2000的單元。(我們說內存中有兩個屬性嘛,一個是地址,然后和地址對應的空間存放的一個數據,那他地址就是2000存放的數據就是5,這個變量呢我們稱之為a。)
(2)另外一種方法是:為安全起見,將A鑰匙放到另外一個抽屜B中鎖起來。如果需要打開A抽屜,就需要先找出B鑰匙,打開B抽屜,取出A鑰匙,再打開A抽屜,取出A抽屜中之物。-----間接訪問
間接訪問如:scanf("%d",&a);//這個函數的作用讀取一個整形的變量,把他的值賦值給a
當我們調用這個函數的時候,把變量a的地址傳遞給函數scanf,函數首先把該地址保存到一個單元中,然后把從鍵盤接收的數據通過所存儲的地址保存到變量a中。
1、初識指針
在C語言中指針是一種特殊的變量,它存放的是地址(一般變量存放的是值嘛,例如剛才a=5,變量a存放的是5;但是我們的指針他是一個特殊的變量它是存放地址的,叫做指針變量)。假設我們定義了一個指針變量,int *i_pointer用來存放整型變量i的地址。可以通過語句i_pointer=&i;(取得i的地址把他賦值給i_pointer這個指針變量)
如上圖:假設變量i,變量j,變量k他們的地址分別是2000,2002,2004;它里邊存放的數據分別為3、6、9;而我們定義的這個指針變量呢它里邊存放的是一個地址,我們這里通過這一句i_pointer=&i,使得變量i的地址存放到了B處,所以B處存放的是i的地址,指針變量也是變量,數組變量也是變量,它跟變量是一樣的道理,只是指針變量存放的是一個地址,而我們普通變量存放的是一個值。我們只需要把他靈活的應用就很NB了。
將i的地址(2000)存放到i_point中。這時,i_pointer的值就是(2000)即變量i所占用單元的起始地址。(因為不同類型在內存中占的字節數是不同的)
要存取變量i的值,可以采用間接方式:先找到存放“i的地址”的變量i_pointer,從中取出i的地址(2000),然后取出i的值3。見下圖:直接的話,直接i=3;就取出來了。
*:叫做取值操作符;
&:叫做取地址操作符;(后邊會詳細分析這兩個操作負要怎么用,目前只是基本使用方法)
int i=2000;//i的值為2000
int *pointer;//我們聲明一個指針pointer//此處的*號并不是取值操作符,這個是申明它為指針的一個特征
pointer=&i;//在這里取出i的地址,比如說他的地址是1000,就把他的地址給了變量pointer//這個是取址
printf("%d\n",*pointer)//那我通過這個*號,取出指向這個地址的里邊的值,也就是打印出來的值應該是2000//這個才是取值操作
知道了一個變量的地址,就可以通過這個地址來訪問這個變量,因此又把變量的地址稱為該變量的”指針“。指針指向的是一個地址。而指針變量呢,C語言中定義一類特殊的變量這些變量專門用來存放變量的地址,稱為指針變量。(所以地址和變量是完全不同的兩回事。)
注意:指針變量的值(即指針變量中存放的值是地址(即指針))要區分“指針(是一個地址)”和“指針變量(是一個變量)”這兩個概念。
//定義一個指針變量
float *pointer_3;//pointer_3是指向float型變量的指針變量//pomiter_3是變量名而不是*pointer_3//*號只是起到表明它是指針
//*表示定義,*可以表示聲明定義一個指針,如果不在聲明的情況下,它是取值操作符
//下邊可以使用賦值語句使一個指針變量得到另一個變量的地址,從而使他指向一個該變量。如:
首先我們的pointer_1里面存放的是i的地址,所以它指向的是i這個變量;pointer_2存放的是j的地址,所以指向的是j這個變量。那么如果有如下語句:
pointer_1=pointer_2;//就是將pointer_2這個變量里面的內容把他存放覆蓋了pointer_1這個里面的內容。所以此時pointer_1里邊存放的不再是i的地址了,而是j的地址了。而此時pointer_1指向了j。當然pointer_2沒有發生改變還是指向的j;也就是說現在i被人拋棄了而兩個都直奔j了。
指針變量的前面的*號表示改變量的類型是指針變量。即類型說明符 *變量名;
在定義指針變量時,必須指定基類型。特別注意的是:只有整型變量的地址才能放到指向整型變量的指針變量中。
請牢記,指針變量中只能存放地址(指針),不要將一個整數(或任何其他非地址類型的數據)賦給一個指針變量,否則編譯器也會把該值當成一個地址來處理。
&變量名;//地址運算符&來表示變量的地址。
2、指針做函數參數
輸入a,b兩個整數,按大小順序輸出
#include<stdio.h>
//交換a,b的值
void swap(int *p1,int *p2);//行參定義的指向整數型的指針變量void main()
{int a,b;int *pointer_1, *pointer_2;scanf("%d %d",&a,&b);pointer_1=&a;//pointer_1該指針變量指向的是a。pointer_2=&b;if(a<b){swap(pointer_1,pointer_2);//所以實參需要傳入的是????指針呢?還是指針變量。pointer_1叫做指針變量}printf("\n%d > %d\n",a,b);
}
void swap(int *p1,int *p2)
{int temp;printf("I'm swap....\n");temp=*p1;*p1=*p2;*p2=temp;
}
輸入a,b,c三個整數,按大小順序輸出
#include<stdio.h>
//交換a,b的值
void exchange(int *q1,int *q2,int q3);//使得a>b>c。void main()
{int a,b,c;int *p1, *p2,*p3;scanf("%d %d %d",&a,&b,&c);p1=&a;//p1指針指向a的地址p2=&b;p3=&c;exchange(p1,p2,p3);//與上邊定義相比,是做了int *q1=p1;這個操作。(行參和實參的一個交換就相當于一個賦值操作,他們是通過棧來完成的)printf("%d %d %d\n",a,b,c);
}
void exchange(int *q1,int *q2,int *q3)
{void swap(int *pt1,int *pt2);//用于交換if(*q1<*q2)//p1和q1都是指向a變量的//即如果a<b的話交換{ swap(q1,q2);}if(*q1<*q3)//a如果小于c的話c,交換{ swap(q1,q3);}if(*q2<*q3)//b如果小于c的話再交換{ swap(q2,q3);}}
void swap(int *pt1,int *pt2)
{int temp;temp=*pt1;*pt1=*pt2;*pt2=temp;
}
3、數組與指針
(因為數組和指針都是指向了一個地址,數組是比較穩定的,而指針是隨時可變的;數組在定義的時候就占用了空間,而指針是一個變量,指針的空間隨時可以消除,而數組不能)
一個變量有地址,一個數組包含若干元素,每個數組元素都在內存中占用存儲單元(而且他們是連續的),他們都有相應的地址。
指針變量(里邊存放的是變量的地址)既然可以指向變量,當然也可以指向數組元素(把某一元素的地址放到一個指針變量中)
所謂數組元素的指針就是數組元素的地址。
定義一個指向數組元素的指針變量的方法,與以前介紹的指向變量的指針變量相同。
int a[10];//定義a為包含10個整型數據的數組
int *p;//定義p為指向整型變量的指針變量
//應當注意,如果數組為int型,則指針變量的基類型也應為int型
p=&a[2];//指針指向數組的第三個元素;把a[2]元素的地址賦給指針變量p,也就是說把p指向a數組的第2號元素。
p++;//表示
引用數組元素,可以用下標法也可以用指針法:
(1)下標法:a[i]的形式;
(2)指針法:*(a+1)或者*(p+i);//因為a是第一個元素的地址,其中的加i就是指向的是第i個元素,不是說地址加i,因為要看什么類型。
其中a是數組名,p是指向數組元素的指針變量,其初值p=a即p=&a[0];
注意:數組名(在編譯器是編譯為一個地址的)即“編譯成數組的第一個元素的地址。”(這就是上邊說的相似的地方)
用數組名做函數的參數--------
如:
void f(int arr[],int n)
{......
}
void main()
{int arr[10];... ...f(arr,10);//數組名相當于該數組的首地址
}
上述中的,f(int arr[],int n),在編譯時是將arr按照指針變量處理的,相當于將函數f的首部寫成f(int *arr,int n)所以以上這兩種寫法是等價的。
需要說明的是:C語言調用函數時虛實結合的方法,都是采用值傳遞方式,當用變量名作為函數參數時,傳遞的是變量的值;當用數組名作為函數參數時,由于數組名代表的是數組首元素地址,因此傳遞的值是地址,所以要求形參為指針變量。
例子:將數組a中n個整數按相反順序存放。
#include<stdio.h>
void reverse(int x[],int n)//形參x是數組名
void main()
{int i, a[10]={3,5,7,8,1,0,2,6,9,4};printf(" The original array:\n");for(i=0,i<10,i++){printf("%d",a[i]);}printf("\n");reverse(a,10);printf("The Array has been inverted:\n");
}
void reverse(int x[],int n)
{int temp,i,j,m;m=(n-1)/2;for(i=0;i<=m;i++){j=n-1-i;temp=x[i];x[i]=x[j];x[j]=temp;}
}
改變一下用指針做函數的參數:
#include<stdio.h>
void reverse(int *x,int n)//形參x為指針變量
void main()
{int i, a[10]={3,5,7,8,1,0,2,6,9,4};printf(" The original array:\n");for(i=0,i<10,i++){printf("%d",a[i]);}printf("\n");reverse(a,10);printf("The Array has been inverted:\n");
}
void reverse(int *x,int n)//形參x為指針變量
{int *p,temp,*i,*j,m;m=(n-1)/2;//中間序號i=x;//前邊的序號//i指向數組的第一個元素j=x-1+n;//后邊的序號//j指向數組的最后一個元素p=x+m;//指向中間配對for(;i<=p;i++,j--){temp=*i;*i=*j;*j=temp;}
}
用指針做參數------
例子:從10個數中找出最大值和最小值。
#include<stdio.h>
int max,min;//全局變量
void max_min_values(int array[],int n);
void main()
{int i,number[10];printf("Enter 10 integer numbers:\n");for(i=0;i<10;i++){scanf("%d",&number[i]);}max_min_values(number,10);printf("\nmax=%d,min=%d\n,"max,min);
}
void max_min_values(int array[],int n)
{int *p,*array_end;array_end=array+n;max=min=*array;for(p=array+1;p<array_end;p++){if(*p>max){max=*p;}else if(*p<min){min=*p;}}}
總結:如果有一個數組,想在函數中(子程序)改變此數組中的元素值,實參與形參的對應關系有以下四種:
(1)形參和實參都用數組名:
void main()
{int a[10];f(a,10)
}
void f(int [],int n)
{......
}
(2)實參采用數組名,形參用指針變量。如:
void main()
{int a[10];f(a,10);
}
void f(int *a,int n)
{......
}
(3)實參和行參都用指針變量。如:
void main()
{int a[10];int *p=a;//指針變量p指向數組的首地址f(p,10);//傳指針相當于傳a數組的地址
}
void f(int *x,int n)//int *x指針相當于指向a的地址,相當于傳值的時侯x=p,p指向a那么x也指向a
{......
}
(4)實參為指針變量,行參為數組名。如:
void main()
{int a[10];int *p=a;f(p,10);//1這里把數組的首地址當作實參傳過去,
}
void f(int x[],int n)//2定義數組來接收首地址,說明位置起始點一樣指向了同一組數組。
{......
}
例子:對一個數組中的元素按照從大到小的順序排列一下。
#include<stdio.h>
int max,min;//全局變量
void sort(int array[],int n);
void main()
{int *p,i,a[10]={3,7,4,9,2,0,5,8,1,6};printf("The origial array:\n");for(i=0;i<10;i++){ printf("%d",a[i]);}printf("\n");p=a;sort(p,10);printf("The result is:\n");for(p=a;i=0;i<10;i++){printf("%d",*p);p++;}printf("\n");
}
void sort(int x[],int n)
{int i,j,k,t;for(i=0;i<n-1;i++){k=i;//假設第一個是最大的,如果接著的那個比他大那就把他調過來for(j=i+1;j<n;j++){if(x[j]>x[k]){t=x[j];x[j]=x[k];x[k]=t;}}}}
?
總結
以上是生活随笔為你收集整理的C语言:随笔5--指针1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Paper5:Curved-Voxel
- 下一篇: C语言:随笔6--指针1.2