日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

万字长文搞定C语言指针

發(fā)布時間:2024/10/14 编程问答 94 豆豆
生活随笔 收集整理的這篇文章主要介紹了 万字长文搞定C语言指针 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄:
1.指針是什么?
2.定義和使用指針變量
???? ?? 定義指針變量
???? ?? 指針的初始化、賦值、取值
???? ?? 指針變量的交換
3.指針變量作為函數(shù)參數(shù)
4.通過指針引用數(shù)組
???? ?? 指針引用一維數(shù)組
???? ?? 指向一維數(shù)組的指針+1
???? ?? 指針變量作為函數(shù)的參數(shù)
???? ?? 指針指向多維數(shù)組
???? ?? 指向一維數(shù)組的指針
5.通過指針引用字符串
???? ?? 字符形指針變量
???? ?? 字符串指針變量和字符數(shù)組的比較
6.指向函數(shù)的指針
???? ?? 函數(shù)指針
???? ?? 返回指針值的函數(shù)
7.指針數(shù)組和多重指針
???? ?? 指針數(shù)組
???? ?? 指向指針變量的指針
8.動態(tài)內(nèi)存分配與指向它的指針變量
???? ?? 動態(tài)內(nèi)存分配與C語言內(nèi)存模型
???? ?? 動態(tài)內(nèi)存分配與釋放函數(shù)

前言:指針是C語言最重要的一塊知識,也是我們必須要掌握的內(nèi)容,對于初學(xué),可能很難,但是迎難而上才是我們學(xué)習(xí)必須有的態(tài)度。由于博主水平有限,如果博客中出現(xiàn)錯誤,還忘指正,博主會在第一時間修改

1.指針是什么?

在我們學(xué)習(xí)C語言的過程中難免會定義變量,如:

int n=1;

對程序進(jìn)行編譯的時候會根據(jù)n的數(shù)據(jù)類型為n分配內(nèi)存,我們通過前面的學(xué)習(xí)知道,int類型的數(shù)據(jù)在內(nèi)存中占據(jù)4個字節(jié),內(nèi)存區(qū)的每一個字節(jié)都有一個編號,這個編號叫地址(這就像人的名字一樣,一個名字對應(yīng)一個人)。地址指向變量單元。實(shí)際上,計(jì)算機(jī)是通過變量名找到存儲單元的地址,對變量值的存取都是通過地址進(jìn)行的,比如上邊定義的變量n

這里我們區(qū)分幾個概念:

地址就相當(dāng)于一個旅館房間的門牌號,變量單元就相當(dāng)于房間,存放的數(shù)據(jù)就相當(dāng)于房間里邊的人。

接下來我給出指針的概念:

一個變量的地址稱為該變量的指針,如果有一個變量專門存儲另一變量的地址,則稱這個變量為指針變量

講到這里不知道你有沒有一個疑問,既然int型的變量有4個字節(jié),每個字節(jié)有一個地址,那么這個變量的指針是四個字節(jié)中的哪一個字節(jié)的地址?

答案是第一個字節(jié)。口說無憑,我們做一個實(shí)驗(yàn),我們知道一維數(shù)組的內(nèi)存空間是連續(xù)分配的,而且數(shù)組中的每一個下標(biāo)都相當(dāng)于一個變量(如arr[[0],arr[1]…),那么我們打印數(shù)組中的連續(xù)兩個下標(biāo)就可以得出結(jié)論了

2.定義和使用指針變量

2.1定義指針變量

類型名 *指針變量名

int *p;

類型名代表指針指向的數(shù)據(jù)的數(shù)據(jù)類型(也稱基類型),比如上面定義的指針變量p 只能用來指向int類型的數(shù)據(jù),不能指向浮點(diǎn)型數(shù)據(jù)。*指的是定義的變量是指針類型。

2.2指針的初始化、賦值、取值

指針可以定義時初始化如:

int a=1; int *p=&a;

&a就是把a(bǔ)的地址傳遞給整形指針p

注意:注意不要把變量賦給指針,要把變量的地址賦給指針

同樣的也可以定義后賦值如:

int a=1; int *p; p=&a;

注意:再定義后初始化時p已經(jīng)是一個指針類型的變量不要寫成*p=&a;

當(dāng)我們想要取得指針?biāo)赶驅(qū)ο蟮闹禃r,我們需要*運(yùn)算符

int a=1; int *p1=&a; printf("%d",*p); //以上程序打印結(jié)果:1 //如果你使用printf("%d",p);是以整形的形式打印p所代表內(nèi)存中地址的編號

2.3指針變量的交換

舉個例子,請想一想下面的程序應(yīng)該輸出什么:

#include<stdio.h> int main() {int a=1;int b=2;int *p1=&a;int *p2=&b;printf("%d %d\n",*p1,*p2);int *p;p=p1;p1=p2;p2=p;printf("%d %d\n",*p1,*p2);printf("%d %d\n",a,b); }

在p1和p2沒交換之前在內(nèi)存中的指向:

交換之后p1和p2在內(nèi)存中的指向:

交換的只是指向,并沒有交換變量單元里邊的內(nèi)容,如果把a(bǔ)和b的值交換了,就交換的是變量單元里邊的內(nèi)容,如下:

3.指針變量作為函數(shù)參數(shù)

看一個程序:

#include<stdio.h> void swap(int *a1,int *a2) {int temp=*a1;*a1=*a2;*a2=temp; } int main() {int a=1;int b=2;int *p1=&a;int *p2=&b;printf("*p1=%d *p2=%d\n",*p1,*p2);swap(p1,p2);printf("*p1=%d *p2=%d\n",*p1,*p2); }

打印結(jié)果:

emm,沒錯就是這樣的,是變量單元里內(nèi)容的交換,好像自己又行了


再看一個程序:

#include<stdio.h> void swap(int *a1,int *a2) {int *temp=a1;a1=a2;a2=temp; } int main() {int a=1;int b=2;int *p1=&a;int *p2=&b;printf("%d %d\n",*p1,*p2);swap(p1,p2);printf("%d %d",*p1,*p2); }

嗯,是這樣,地址交換,看一眼答案:


怎么會這樣??????

地址交換,值應(yīng)該變了啊,別急,首先我們要知道:

C語言里邊的實(shí)參變量和形參變量的數(shù)據(jù)傳遞是值傳遞

什么意思呢?就是形參其實(shí)是實(shí)參的一個副本,這怎么講呢,對于上面的那一段代碼下面一張圖:

p1把自己的值(表示地址)給了a1,p2把自己的值給了a2,也就是說就相當(dāng)于新定義了兩個指針變量和p1,p2指向一樣。也就是說p1與p2指向始終未變,看下圖,我們輸出指針的地址:


如上所示,那么我們就可以知道,交換的只是a1和a2的指向,與p1和p2無關(guān)。如果你第一個程序不是這么分析,那么可以再試著分析一遍

4.通過指針引用數(shù)組

4.1指針引用一維數(shù)組

我們明確兩個概念:

1.一維數(shù)組的內(nèi)存分配是連續(xù)的
2.一維數(shù)組的數(shù)組名代表首元素的地址

所以我們可以得到如下:

int a[10]; int *p1=&a[0]; //int *p1=a;本行與上一行等價

4.2指向一維數(shù)組的指針+1

我們知道,指針是一個字節(jié)的地址編號,那么當(dāng)一個指針指向數(shù)組元素,指針+1是不是內(nèi)存中下一個字節(jié)的地址,來看一個程序:

很明顯,不是簡單的地址+1,而是加上一個數(shù)組元素所占字節(jié),其實(shí)不只是一維數(shù)組,任意類型的指針+1加上的都是指針基類型的字節(jié)數(shù),我們看下面一個程序:


通過指針這個性質(zhì),我們可以可以使用指針找到指針任意位置的元素,所以可以用下面的方式遍歷數(shù)組(注意:請不要隨意訪問你未申請的內(nèi)存):

4.3指針變量作為函數(shù)的參數(shù)

我們知道當(dāng)我們使用數(shù)組數(shù)組作為函數(shù)參數(shù)有:

void fun(int arr[],int len);

其實(shí)程序在編譯的時候就把a(bǔ)rr[]當(dāng)成指針變量處理,所以上面一行代碼就等價于

void fun(int *arr,int len);

那么以下兩個函數(shù)(fun1與fun2)也是等價的:

這里有一個注意點(diǎn):

實(shí)參數(shù)組名代表一個固定的地址,或者說是指針常量,但是形參數(shù)組名并不是一個固定的地址,而是可以看做一個指針變量

舉個例子(程序編譯運(yùn)行會報錯):

這是因?yàn)槲覀兌x的一位數(shù)組首地址是一個指針常量,我們知道常量的值是無法修改的,所以我們使用arr=arr+3;這行代碼就有問題。但是當(dāng)我們把這個指針常量傳給函數(shù)參數(shù)時,函數(shù)參數(shù)這個時候就是一個指針變量,這個時候就可以進(jìn)行類似于arr=arr+3;的操作

4.4指針指向多維數(shù)組

int a[10][10];

這里我們主要弄清幾個概念:

表示形式含義備注
a二維數(shù)組名,指向一維數(shù)組a[0],即0行首地址行首地址
a[0], *(a+0), *a, &a[0][0]0行0列元素地址元素地址
a+1, &a[1]1行首地址行首地址
a[1], *(a+1)1行0列元素的地址,即a[1][0]的地址元素地址
*(a[1]+2), *(*(a+1)+2), a[1][2]1行2列元素的值,即a[1][2]的值

備注的行首地址元素地址什么意思呢?我們看一個程序:

我們知道a是代表行首地址,*a代表元素地址,使用整形的形式打印出他們的地址發(fā)現(xiàn)是一樣的,那么你可能會問他們有什么區(qū)別,再看一個程序:

二維數(shù)組可以看做是多個一維數(shù)組為元素組成的數(shù)組,a映射到第一行(也就是第一個一維數(shù)組的首地址),*a則是映射到第一行第一列(也就是第一行第一個元素)的首地址:

第一行的首地址和第一個元素的首地址當(dāng)然是一樣的,因?yàn)榈刂肥窃氐谝粋€字節(jié)的編號,只是兩者映射的范圍不同,映射的范圍不同+1所產(chǎn)生的結(jié)果也不同(第二張圖),比如上面兩張圖我們自己也可以算算,比如a是第一行數(shù)組的首地址6683776,那么a+1代表第二行數(shù)組的首地址,他倆中間隔著是10個整形元素,一個整形元素是4個字節(jié),那么一共隔了4*10=40個字節(jié),所以根據(jù)數(shù)組中的元素在內(nèi)存中是連續(xù)分配的a+1的地址是a的地址+40,結(jié)合上面兩張圖a+1的地址是6683816印證了我們的想法。*a映射到的是元素,所以 * a+1映射到的就是下一個元素,一個整形元素四個字節(jié),所以按照正常邏輯 他倆相差四個字節(jié),上面的兩張圖,*a地址是6683776,*a+1的地址是6683780,這就印證了上面的講解。補(bǔ)充一點(diǎn):a和a[0]都是指向第一行,只是不同的表現(xiàn)形式而已

4.5指向整個一維數(shù)組的指針

int (*p)[4];

以上定義p表示為一個指針變量,它指向包含四個整形元素的一維數(shù)組,注意它指向的是整個一維數(shù)組,而不是一個整形元素

int a[4];

p和a是不同的,a是映射到數(shù)組第一個元素,p是映射到整個一維數(shù)組,看下面程序應(yīng)該就知道我在說什么了:

用指針數(shù)組的指針作為函數(shù)的參數(shù)時:

#include<stdio.h> void func(int (*p)[8]) {} int main() {int arr[10][8]={0};func(arr); }

這個程序注意兩個細(xì)節(jié):

1.由于p映射整個一維數(shù)組,所以傳參的時候傳遞的是二維數(shù)組名
2.由于函數(shù)參數(shù)中p是指向含8個整形元素的一維數(shù)組,所以傳入的二維數(shù)組的每一個一維數(shù)組長度也是8

5.通過指針引用字符串

5.1字符形指針變量

char *str="www.baidu.com";

C語言通過字符串指針變量(str)來引用字符串常量,同時字符串常量(www.baidu.com)按照字符數(shù)組處理;str映射到的是第一個字符的地址(str+1就映射到第二個字符的地址)

注意:通過字符數(shù)組或字符指針變量可以輸出一整個字符串,而對于一個數(shù)值型數(shù)組(int a[10]),是不能企圖利用數(shù)組名輸出它全部的數(shù)據(jù)的。

5.2字符串指針變量和字符數(shù)組的比較

char str1[20]="www.bilibili.com"; //字符數(shù)組只能定義的時候賦值,不能定義后使用str1[20]="www.bilibili.com"; char *str2="www.bilibili.com"; //對于指針變量來說可以定義后再賦值如:str2="123123";

注意:

1.字符數(shù)組里邊的每一個元素存放字符串的一個字符,字符串指針變量存放的是字符串首個字符的地址
2.str1是指針常量,str2指針變量
3.編譯時為字符數(shù)組分配若干個存儲單元,而對字符串指針變量只分配一個存儲單元
4.字符數(shù)組中的各個元素是可以改變的,字符串指針變量指向的字符串常量是不可被改變的,但是字符串指針變量的指向是可以改變的

6.函數(shù)與指針

6.1函數(shù)指針

如果我們在程序中定義了一個函數(shù),那么編譯系統(tǒng)會為函數(shù)分配一段存儲空間,這段存儲空間的其實(shí)地址稱為函數(shù)的指針

#include<stdio.h> int max(int a,int b) {if(a>b)return a;return b; } int main() {int (*p)(int,int);//p只能指向返回值為int類型,參數(shù)也是兩個int類型的函數(shù)p=max;int a=1;int b=2;printf("%d",p(a,b)); }

同一個函數(shù)指針可以先后指向同類型的不同函數(shù)

6.2返回指針值的函數(shù)

顧名思義,返回指針類型其實(shí)就是返回地址類型得函數(shù),一般得定義形式為:

類型名 *函數(shù)名(參數(shù)列表)

例子:

#include<stdio.h> int *max(int *a,int *b) {if(*a>*b)return a;return b; } int main() {int a=1;int b=2;int *p=max(&a,&b);printf("%d",*p); }

7.指針數(shù)組和多重指針

7.1指針數(shù)組

定義格式:

類型名 * 數(shù)組名[數(shù)組長度]

例子:

再看一個程序:

str[0]存放字符串”aaaio"中第一個字符的地址,str[1]存放字符串"bbb"中第一個字符的地址。對于一個指針數(shù)組來說,注意,這里和二維數(shù)組不同的是:str不是指向第一行的地址,str里邊存放的是str[0]的地址,相當(dāng)于一個二級指針,如下(str與str[0]的地址):


str,str[0]與str[0][0]的關(guān)系就相當(dāng)于,str是門牌號1,打開門牌號1對應(yīng)的門,里邊有一個門牌號2,這個門牌號2就是str[0],再打開門牌號2的門就可以找到元素str[0][0]

一定要注意指針數(shù)組存放的是內(nèi)容是地址

這里還有一個知識點(diǎn),你可以發(fā)現(xiàn)指針str,str+1,str+2(也就是二級指針)相差八個字節(jié),那么也就是一個二級的char類型的指針占據(jù)八個字節(jié),如下:


那么是不是不同類型的指針?biāo)嫉膬?nèi)存空間大小不同,做一個實(shí)驗(yàn)(sizeof函數(shù)返回所占字節(jié)數(shù)):

結(jié)論:

在64位計(jì)算機(jī)中,不管什么類型的指針,都占據(jù)8個字節(jié)

注意:這里只是64位計(jì)算機(jī),32位計(jì)算機(jī)指針占據(jù)四個字節(jié)

7.2指向指針變量的指針

當(dāng)一個指針指向一個普通數(shù)據(jù)的時候,把這個指針稱為一級指針,指針變量既然是變量,那么肯定也是占據(jù)內(nèi)存中的,所以我們還可以用指針指向這個一級指針的的存儲空間,這時候指向一級指針的指針就叫做二級指針,同理,指向二級指針的指針稱為三級指針。

舉個例子:

a[0]相當(dāng)于存放數(shù)據(jù)元素1的地址,a[1]相當(dāng)于數(shù)據(jù)元素2的地址,a存放a[0]這個整形指針的地址,a+1相當(dāng)于存放a[1]這個整形指針的地址。a與p就相當(dāng)于兩個二級指針。

8.動態(tài)內(nèi)存分配與指向它的指針變量

8.1動態(tài)內(nèi)存分配與C語言內(nèi)存模型

我們復(fù)習(xí)幾個概念:

1.局部變量是按照動態(tài)存儲方式分配內(nèi)存(分配在動態(tài)存儲區(qū))
2.全局變量是按照靜態(tài)存儲方式分配內(nèi)存(分配在靜態(tài)存儲區(qū))

動態(tài)存儲區(qū)分為堆和棧,動態(tài)內(nèi)存分配就是分配堆中的內(nèi)存空間,因?yàn)槎阎械膬?nèi)存空間是程序員自行分配和釋放的

C語言的內(nèi)存模型分為5個區(qū):棧區(qū)、堆區(qū)、靜態(tài)區(qū)、常量區(qū)、代碼區(qū)。每個區(qū)存儲的內(nèi)容如下:

內(nèi)存區(qū)域內(nèi)容
棧區(qū)存放函數(shù)的參數(shù)值、局部變量等,由編譯器自動分配和釋放,通常在函數(shù)執(zhí)行完后就釋放了,其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧
堆區(qū)就是通過new、malloc、realloc分配的內(nèi)存塊,編譯器不會負(fù)責(zé)它們的釋放工作,需要用程序區(qū)釋放。分配方式類似于數(shù)據(jù)結(jié)構(gòu)中的鏈表。“內(nèi)存泄漏”通常說的就是堆區(qū)。
靜態(tài)區(qū)全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域,未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。程序結(jié)束后,由系統(tǒng)釋放。
常量區(qū)常量存儲在這里,不允許修改。
代碼區(qū)顧名思義,存放代碼

8.2動態(tài)內(nèi)存分配與釋放函數(shù)

1.void *malloc(unsigned int size):作用是在動態(tài)存儲區(qū)中分配一個長度為size的連續(xù)空間,unsigned代表沒有符號位的整形數(shù)據(jù)(非負(fù)整數(shù)),返回所分配內(nèi)存區(qū)域第一個字節(jié)的地址.分配失敗返回NULL指針
2.void *calloc(unsigned n,unsigned size):作用是在動態(tài)內(nèi)存空間中分配n個長度為size的連續(xù)空間,分配失敗返回NULL指針
3.void free(void *p):釋放指針變量p所指向的動態(tài)空間
4.void *realloc(void *p,unsigned int size):對已經(jīng)通過malloc函數(shù)calloc函數(shù)獲得了動態(tài)空間,想改變其大小,用此函數(shù)重新分配

注意:void*類型的指針表示指向空類型或者不指向確定的類型的數(shù)據(jù)

以上函數(shù)得使用#include<stdlib.h>

使用舉例:

#include<stdio.h> #include<stdlib.h> int main() {int i=0;int *p=(int*)malloc(4);//(函數(shù)前的int*代表把分配的內(nèi)存轉(zhuǎn)換成int*類型)*p=3;printf("%d\n",*p);free(p); }

總結(jié)

以上是生活随笔為你收集整理的万字长文搞定C语言指针的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。