前言
下面是我自學的筆記以及源代碼,系統的學習完,其實發現 指針其實也並沒那麼可怕! 還有一些比較繞的公式其實沒有必要去死記硬背!仔細一想,我們在學習指針之前學習過它,還不止一次,scanf,數組,都應用到指針這個東西!!所以指針並不複雜!!一步一步的學總會學會它!!!
C語言內存模型:
堆棧:自頂向下,以及每種數據類型所對應的十六進制下的位數!這都是要有一定的基礎!
先從變量的地址學起:
#include <stdio.h>
int main()
{
int a=10;
int p=(int)&a;
printf("%p\n",a);//a對應的16進制
printf("0x%x\n",&a);//a的地址,按十六進制
printf("%p\n",p);//賦值後與a的地址值相同
printf("0x%x\n",&p);//p的地址
printf("%p\n",&p);//p的地址
return 0;
}
運行結果:
數組的地址:
#include <stdio.h>
int main()
{
int a[10];
printf("%p\n",&a);
printf("%p\n",a);
printf("%p\n",&a[0]);
printf("%p\n",&a[1]);
return 0;
}
運行結果:
指針:就是保存地址的變量。
普通變量:放的值---->【值】int a;
指針變量:放的別人的地址---->【別人的地址】int *a;
作爲參數的指針:
#include <stdio.h>
void fun(int *p);
int main(void)
{
int i=6;
printf("&i=%p\n",&i);
fun(&i);
g(i);
return 0;
}
void fun(int *p)
{
printf("p=%p\n",p);
printf("*p=%d\n",*p);
}
void g(int k)
{
printf("k=%d\n",k);
}
運行結果:
函數內部擁有了main函數的變量地址,從而可以訪問外部變量。
當我們訪問到一個外部變量的地址是,我們可以使用*p(指針)來修改此變量。實際上,我們是需要通過傳進來的指針參數來得到我們想要的結果。
#include <stdio.h>
void fun(int *p);
int main(void)
{
int i=6;
printf("&i=%p\n",&i);
fun(&i);
g(i);//i在上一步已經被fun函數修改成26
return 0;
}
void fun(int *p)
{
printf("p=%p\n",p);
printf("*p=%d\n",*p);
*p=26;//通過地址訪問外部變量,修改i的值
}
void g(int k)
{
printf("k=%d\n",k);
}
運行結果:
1.*左值叫左值:*p=指向的地址所對應的值(叫值)
2.傳入地址:int i;scanf("%f",i);當編譯的時候不會報錯,因爲你傳進來的數值被當作地址,寫到了不該寫的地方,所以運行時會報錯!
指針的使用
使用指針交換變量
#include <stdio.h>
int main(void)
{
int a=5;
int b=6;
swap(&a,&b);
printf("a=%d,b=%d",a,b);
return 0;
}
void swap(int *pa,int *pb)
{
int t=*pa;
*pa=*pb;
*pb=t;
}
因爲有返回值的函數只能返回一個,所以如果需要函數返回多個值,這時候是體驗到指針所帶來的便利!
接下來還是一個通過指針參數來求數組中最大值和最小值。這是原來通過函數很難解決的。
#include <stdio.h>
int main(void)
{
int a[]={1,2,3,4,5,10,100,20,22};
int min,max;
minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
printf("min=%d,max=%d",min,max);
return 0;
}
void minmax(int a[],int len,int *min,int *max)
{
int i;
*min=*max=a[0];
for(i=0;i<len;i++){
if(a[i]<*min){
*min=a[i];
}
if(a[i]>*max){
*max=a[i];
}
}
}
下面我們以這個求最值的函數來研究數組和指針的關係:
#include <stdio.h>
int main(void)
{
int a[]={1,2,3,4,5,10,100,20,22};
int min,max;
printf("main sizeof(a)=%lu\n",sizeof(a));//main中數組a的大小
printf("main a=%p\n",a);//main中的a的地址
minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
printf("a[0]=%d\n",a[0]);
printf("min=%d,max=%d\n",min,max);
int *p=&min;//有一個變量min=2,p指向了min,可以看做把p看作一個數組,p[1]
printf("*p=%d\n",*p);//
printf("p[0]=%d\n",p[0]);
printf("*a=%d\n",*a);//指的就是a[0];
int b[]=a;//不可以
int *q=a;//這個可以
//因爲int b[]被看作 int * const b;//數組是常量指針
return 0;
}
//數組變量組作爲參數
void minmax(int *a,int len,int *min,int *max)//數組可以修改成指針
{//a[]可以修改爲指針*a,兩者沒有區別,在參數表中是等價的,數組變量是特殊的指針
int i;
printf("minmax sizeof(a)=%lu\n",sizeof(a));//函數中a的大小
printf("minmax a=%p\n",a);//函數裏的數組地址,和main裏的一摸一樣,可以修改
a[0]=1000;
*min=*max=a[0];
for(i=0;i<len;i++){
if(a[i]<*min){
*min=a[i];
}
if(a[i]>*max){
*max=a[i];
}
}
}
運行結果:
一:是當數組作爲參數時,它的相當就是把數組的地址,即與a[0]的地址相同,所以在minmax函數中求sizeof(a)時,大小就是固定的:4,在mian函數中sizeof(a)求的的大小:4*9=36
二:地址相同,&a由於在mian和普通函數中傳入的都是a[0],所以地址相同。
三:數組就是特殊的指針,因爲當你在普通函數裏傳入數組時,修改裏面的數據,也會使得main中的數據同時改變,如我修改的a[0]=1000
四:a與a[0]的值相同。
五:還有就是數組不可以相互賦值,例如:int b[ ]=a;//會直接出現編譯錯誤,但是q=a;可以賦值給指針,因爲,數組本質上可以看作:int * const b; 就是一個常量的數組,所以數組間不能相互賦值。
const和指針:
如果用const來修飾指針變量,那麼有以下三種情況:
int i;
const int *P1=&i;
int const *P2=&i;
int *const P3=&i;
其實判斷是指針的指向不能變,還是通過指向來修改變量的值,就看*****是在const前還是後。
const在前:一,二相同,指的不能通過指向來修改變量的值,即:
const在後,說明這個指針是個常量,一旦指向莫格變量後就不能修改,即:
指針的運算:
#include <stdio.h>
int main(void)
{
char ac[]={0,1,2,3,4,5,6,7,8,9};
char *p=&ac[0];//指向了數組的第一個單元
char *p1=&ac[5];
while(*p!=-1){//2.可以實現遍歷
printf("%d\n",*p++);//取出p所指向的那個數據,完事之後順便把p移到下一個位置上去
}
//不同類型的指針不能相互賦值
printf("p1-p=%p\n",p1-p);
printf("ac =%p\n",ac);
printf("ac+1 =%p\n",ac+1);
int ai[]={0,1,2,3,4,5,6,7,8,9};
int *q=ai;
printf("q=%p\n",q);
printf("q+1=%p\n",q+1);//1.不是+1,是加一個sizeof
return 0;
}
運行結果:
一:你可以使用指針變量指向一個數組,從而實現數組的賦值操作,賦值後的指針變量完全可以看作數組來用。
二:可以使用while(*p!=-1){printf("%d ",*p++)},來實現數組的遍歷
三:指針變量+1,加的是sizeof個地址值
用指針來做什麼呢?
需要傳入較大的數據時用作參數
傳入數組後對數組操作
函數返回不止一個結果
需要用函數修改不止一個變量
動態申請的內存
輸入數據:
C99之前:
int *a=(int )malloc(nsizeof(int))//n是你需要申請的數據個數
下面我們來講一講malloc。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int number;
int *a;
int i;
printf("輸入數量:");
scanf("%d",&number);
//int a[number];
//佔據多少空間
a=(int*)malloc(number*sizeof(int));//malloc的參數是這個數組佔據多少個字節,所以需要使用number來確定數組的大小,同時要乘以sizeof
for(i=0;i<number;i++){
scanf("%d",&a[i]);
}
for(i=number-1;i>=0;i--){
printf("%d ",a[i]);
}
free(a);
return 0;
}
運行結果:
一:malloc函數就是stdlib.h這個庫函數中的,所以我沒如果要使用它,就在main函數頭聲明一下
二:這是一個動態內存申請的函數,主要是用來使用指針的方式給數組申請空間。例如:int a;如何給a賦予一定的空間呢?你可以這樣做:a=(int)malloc(n*sizeof(int ));n是你要申請的數組的大小。
三:使用malloc申請空間後,在程序需結束時,一定要free掉。要不然大型的程序會崩掉!
四:就是free只能free一次,並且是free掉malloc的地址,其他的不行!
總結:
學到這裏,你估計也就明白了,重要的是研究指針的和數組的關係,以及指針的運算,前提還需要你明白內存的意義,這也是我所領悟的,後期還會繼續把字符串,鏈表都給總結一下。文章一定有許多不足之處,敬請指出,我會及時更正!!
我學習時參考的鏈接:
https://www.bilibili.com/video/av15267247?p=88