欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C語(yǔ)言指針和數(shù)組深入探究使用方法

 更新時(shí)間:2022年08月09日 10:41:01   作者:程序猿教你打籃球  
在C語(yǔ)言和C++等語(yǔ)言中,數(shù)組元素全為指針變量的數(shù)組稱為指針數(shù)組,指針數(shù)組中的元素都必須具有相同的存儲(chǔ)類型、指向相同數(shù)據(jù)類型的指針變量。指針數(shù)組比較適合用來指向若干個(gè)字符串,使字符串處理更加方便、靈活

1、數(shù)組參數(shù)和指針參數(shù)

1.1 一維數(shù)組傳參

這里在前幾期我們已經(jīng)初略的見識(shí)過了,但是這里我們要提一個(gè)概念,數(shù)組給函數(shù)傳參是會(huì)發(fā)生降維的,降維成什么呢?我們看代碼:

這里通過打印形參的大小,發(fā)現(xiàn)是 4,其實(shí)也不奇怪,目前我們是 32 位操作環(huán)境,所以一個(gè)指針也就是 4 個(gè)字節(jié),所以從這里我們可以看出,數(shù)組傳參的時(shí)候,是發(fā)生降維的,數(shù)組名除了 &數(shù)組名 和 sizeof(數(shù)組名) 其他所有情況都是首元素地址,所以本質(zhì)上我們是降維成指向其數(shù)組內(nèi)部元素類型的指針,為什么呢,因?yàn)樗菙?shù)組首元素的地址,首元素是int 類型,所以傳過去的也是對(duì)應(yīng)的 int 類型的指針,同理我們需要拿同類型指針變量來接收,所以本質(zhì)上我們 p 變量中保存的就是 arr[0] 的地址!

我們?cè)诳匆欢未a:

void printSize(int arr[100], int n)
{
    for (int i = 0; i < n; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    printSize(arr, 10);
    return 0;
}

如上這段代碼有問題嗎?其實(shí)是沒有問題的,實(shí)際傳遞數(shù)組大小與函數(shù)形參指定的數(shù)組大小沒有關(guān)系,因?yàn)樗呀?jīng)是指針了,只是訪問方式被打通了,第二期我們有講過,那么既然如此,我們也可以不要里面的元素個(gè)數(shù)直接成 printSize(int arr[], int n) 這樣也是可以的,至少不會(huì)讓閱讀者感到誤會(huì)。

1.2 一級(jí)指針傳參

void print(int* p, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", *(p + i));
	}
    printf("\n");
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//一級(jí)指針p,傳給函數(shù)
	print(p, sz);
	return 0;
}

這里我們需要討論一個(gè)問題,指針作為參數(shù)需要發(fā)生拷貝嗎?

答案是需要的,因?yàn)橹羔樧兞恳彩亲兞?,在傳參上得符合變量的要求,也就是在棧上開辟空間,同時(shí)我們也知道,main 函數(shù)中的 p 是一個(gè)局部變量,它只在 main 函數(shù)內(nèi)有效,所以只能對(duì)實(shí)參做一份拷貝,并傳遞給被調(diào)用的函數(shù)。

1.3 二維數(shù)組參數(shù)和二級(jí)指針參數(shù)

這個(gè)例子我們發(fā)現(xiàn),二維數(shù)組傳參的時(shí)候也會(huì)發(fā)生降維,如何理解呢?上一期我們用了數(shù)組指針來接收了二級(jí)指針傳參,這里我們就來做一個(gè)總結(jié):

任何維度的數(shù)組,傳參的時(shí)候都要發(fā)生降維,降維成指向其內(nèi)部元素類型的指針,那么,二維數(shù)組內(nèi)部元素我們可以看成是多個(gè)一維數(shù)組,所以,二維數(shù)組傳參其實(shí)是降維成指向一維數(shù)組的指針,而這里的 arr 也就代表著首元素地址,也就是第一行一維數(shù)組的地址!這也就是我們之前可以拿指針數(shù)組來接收的原因了。

這里我們還是可以省略第一個(gè)下標(biāo)的值:char arr[][4] ,但是為什么不能省略第二個(gè)下標(biāo)值呢?我們可以想一下,之前寫用數(shù)組指針接收是這樣寫的 char (*p)[4] ,上面我們提到過,int arr[] 用來接收實(shí)參,它本質(zhì)上就是個(gè)指針,所以 char arr[][4] 本質(zhì)上是個(gè)數(shù)組指針,從他的角度看,他指向了一個(gè)存放 4 個(gè) char 類型元素的數(shù)組,所以如果省略了第二個(gè)下標(biāo)則指針類型不明確!

1.4 野指針的問題

這個(gè)問題其實(shí)很多書中都會(huì)有寫,我們這里就簡(jiǎn)單提一下:

  • 指針未初始化,默認(rèn)是隨機(jī)值,如果直接訪問會(huì)非法訪問內(nèi)存
  • 指針越界訪問,當(dāng)指針指向不屬于我們的內(nèi)存,p就是野指針
  • 指針指向的空間被釋放,如果動(dòng)態(tài)開辟的內(nèi)存被釋放但是指針沒置NULL,就會(huì)形成野指針,他仍然記錄者已經(jīng)不屬于他的內(nèi)存
  • 返回局部變量的地址,如果我們一個(gè)函數(shù)被銷毀后但是仍然返回函數(shù)內(nèi)局部變量的地址也會(huì)造成也會(huì)造成野指針

2、函數(shù)指針

指針變量是用來保存地址的,那么函數(shù)有地址嗎?有!函數(shù)是由我們自己寫的一些語(yǔ)句構(gòu)成的,程序運(yùn)行的時(shí)候就會(huì)把定義好的函數(shù)中的語(yǔ)句調(diào)用到內(nèi)存中去,那么函數(shù)代碼在內(nèi)存中開始的那個(gè)內(nèi)存空間的地址也就是函數(shù)的地址!

這里我們也能發(fā)現(xiàn),函數(shù)是有地址的,而且 &函數(shù)名 和 單獨(dú)的函數(shù)名 都能表示函數(shù)的地址。

那么我們?nèi)绻氚押瘮?shù)的地址存起來該如何做呢?有了上面學(xué)習(xí)指針數(shù)組和數(shù)組指針的經(jīng)驗(yàn),其實(shí)函數(shù)指針也很好理解:

void (*pfun) () 其實(shí)這么寫可以了,我們來解讀下這句代碼:pfun 先和 * 結(jié)合,正如我們之前所說,就能說明他是一個(gè)指針,指向的是一個(gè)無參數(shù)并且無返回類型的函數(shù)。

那我們?nèi)绻赶蛞粋€(gè) int add (int x, int y) 這樣的一個(gè)函數(shù),我們應(yīng)該如何定義函數(shù)指針呢?

int (*p) (int, int) 如同上面一樣,首先要保證 p 是指針,所以帶上括號(hào),指向的是一個(gè)返回值為 int 參數(shù)為 int int 的函數(shù)。

接下來我們來使用函數(shù)指針,使用方法跟函數(shù)一樣,直接把指針變量名當(dāng)函數(shù)名使用即可:

讓我們來看一道有意思的題:

int main()
{
	(*(void (*)())0)();
	return 0;
}

首先這道題的解法肯定先從 0 下手,我們先分析,0 前面的 (void (*) ()) 是什么?這很明顯是一個(gè)函數(shù)指針類型,所以可以理解成把 0 強(qiáng)轉(zhuǎn)成函數(shù)指針,也就是把 0 當(dāng)成了一個(gè)函數(shù)的地址,然后再 * 引用這個(gè)地址,也就是找到 0 地址處的函數(shù)進(jìn)行調(diào)用。所以此代碼就是一次函數(shù)調(diào)用,被調(diào)函數(shù)無參,返回類型是void。

3、函數(shù)指針數(shù)組

有了上面的學(xué)習(xí)就很好理解了,無非就是保存函數(shù)地址的數(shù)組,那么它的語(yǔ)法格式是什么呢?

int (*arr[10]) (int, int)

這里我們可以分析到:首先 arr 跟 [ ] 先結(jié)合,所以它是個(gè)數(shù)組,這個(gè)數(shù)組的每個(gè)元素是 int (*) (int int) 類型的函數(shù)指針,它的作用主要是轉(zhuǎn)移表,那我們這里就簡(jiǎn)單用一下即可

假設(shè)我們需要兩個(gè)整數(shù)的 + - * / 我們寫完了四個(gè)函數(shù)是不是可以放到一個(gè)數(shù)組里,然后通過訪問數(shù)組下標(biāo)就能調(diào)用我們想用的函數(shù)了:

int add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int mul(int x, int y)
{
	return x * y;
}
int div(int x, int y)
{
	return x / y;
}
int main()
{
	int (*arr[4]) (int, int) = { add, sub, mul, div };
	printf("加法:%d\n", arr[0](1, 2));
	printf("減法:%d\n", arr[1](5, 2));
	printf("乘法:%d\n", arr[2](3, 3));
	printf("除法:%d\n", arr[3](6, 2));
	return 0;
}

4、指向函數(shù)數(shù)組的指針

看到這可能有的小伙伴覺得越來越套娃了,但其實(shí)這個(gè)也很好理解,無非就是一個(gè)指針指向了一個(gè)數(shù)組,數(shù)組每個(gè)元素是函數(shù)指針,這里我們簡(jiǎn)單了解下概念即可,用的其實(shí)也不是很多,當(dāng)別人如果寫了這種代碼我們能看懂就行:

函數(shù)指針如何定義:

int test(char* str)
{
	if (str == NULL) {
		return 0;
	}
	else
	{
		printf("%s\n", str);
		return 1;
	}
}
int main()
{
	//函數(shù)指針pfun
	int (*pfun)(char*) = test;
	//函數(shù)指針的數(shù)組pfunArr
	int (*pfunArr[5])(char* str);
	pfunArr[0] = test;
	//指向函數(shù)指針數(shù)組pfunArr的指針ppfunArr
	int (*(*ppfunArr)[5])(char*) = &pfunArr;
	return 0;
}

我們來分析一下這個(gè):int(*(*ppfunArr)[5])(char*),首先看到 (*ppfunArr) 這括號(hào)括起來先跟 * 結(jié)合證明它是一個(gè)指針,指向的類型是什么呢?把它去掉剩下的就是它的類型,int(*[5])(char*),通過這個(gè)可以發(fā)現(xiàn),是一個(gè)帶有5個(gè)元素的數(shù)組,每個(gè)元素的類型是一個(gè)函數(shù)指針,而函數(shù)的返回值為int,參數(shù)為 char*

這里我們能看懂即可。

5、回調(diào)函數(shù)

回調(diào)函數(shù)指的就是一個(gè)通過函數(shù)指針調(diào)用的函數(shù),如果你把函數(shù)的指針(地址),作為參數(shù)傳遞給另一個(gè)函數(shù)的話,當(dāng)這個(gè)指針被用來調(diào)用其指向的函數(shù),這里就被稱為回調(diào)函數(shù)。其實(shí) qsort 函數(shù)就是很典型使用了回調(diào)函數(shù)的例子,感興趣的可以自行下來了解一下,這里我們就簡(jiǎn)單的演示下如何使用,用回調(diào)函數(shù)實(shí)現(xiàn)三個(gè)數(shù)比較大小:

int max(int x, int y, int z, int(*pfun)(int, int))
{
	if (x > pfun(y, z)) {
		return x;
	}
	else
	{
		return pfun(y, z);
	}
}
int tmp(int x, int y) 
{
	return x > y ? x : y;
}
int main()
{
	int ret = max(10, 20, 30, tmp);
	printf("%d\n", ret);
	return 0;
}

比較三個(gè)數(shù)的最大值是有更優(yōu)的解決方案的,我們這里只是演示一下回調(diào)函數(shù)的簡(jiǎn)單使用,跟上面一樣,會(huì)用即可,其實(shí)不用研究的特別深入

6、一道筆試題

int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	printf("%s\n", **++cpp);
	printf("%s\n", *-- * ++cpp + 3);
	printf("%s\n", *cpp[-2] + 3);
	printf("%s\n", cpp[-1][-1] + 1);
	return 0;
}

這道題我就不講解了,學(xué)習(xí)一定得有自己研究的一個(gè)過程,包括后續(xù) Java 的文章,每一期基本上都會(huì)留一個(gè)小疑問讓大家自己下去解答,其實(shí)這道題很簡(jiǎn)單,耐心畫畫圖就能理解了,如果你能自己解決這道題,說明你的指針的數(shù)組這兩章的內(nèi)容已經(jīng)通關(guān)了,實(shí)在是難以解決的話,可以問一下博主。

后續(xù)其實(shí)還有動(dòng)態(tài)內(nèi)存管理,但是這個(gè)知識(shí)點(diǎn)無非就是掌握對(duì) malloc calloc realloc free 的使用,如果你是以后 C++ 方向可學(xué)習(xí)一下,如果你是 Java 方向其實(shí)有個(gè)基本認(rèn)識(shí)就行,畢竟 Java接觸底層不多,有了前面學(xué)習(xí)的鋪墊,去網(wǎng)上看看內(nèi)存管理的文章是很輕松學(xué)會(huì)的,學(xué)習(xí)最主要是培養(yǎng)學(xué)習(xí)的能力,

最后來個(gè)大總結(jié):從剛開始我們一共講解了32個(gè)關(guān)鍵字,在關(guān)鍵字中也穿插了很多內(nèi)容,比如大小端,結(jié)構(gòu)體,往后就是符號(hào)的理解了,包括我們平常用的注釋,以及各種運(yùn)算符但是除法和取模我們沒有放進(jìn)去,這個(gè)在JavaSE系列中會(huì)介紹,再往后就是對(duì)預(yù)處理的深入理解了,最終我們以數(shù)組和指針結(jié)尾,C語(yǔ)言系列就到此結(jié)束了。

到此這篇關(guān)于C語(yǔ)言指針和數(shù)組深入探究使用方法的文章就介紹到這了,更多相關(guān)C語(yǔ)言指針和數(shù)組內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 如何基于 Blueprint 在游戲中創(chuàng)建實(shí)時(shí)音視頻功能

    如何基于 Blueprint 在游戲中創(chuàng)建實(shí)時(shí)音視頻功能

    我們?cè)诒疚南葋碇v講如何在 Unreal 中用 Blueprint 快速實(shí)現(xiàn)。稍后會(huì)分享基于 C++的實(shí)現(xiàn)步驟。感興趣的朋友跟隨小編一起看看吧
    2020-05-05
  • 深入解析C++中的函數(shù)模板和函數(shù)的默認(rèn)參數(shù)

    深入解析C++中的函數(shù)模板和函數(shù)的默認(rèn)參數(shù)

    這篇文章主要介紹了深入解析C++中的函數(shù)模板和函數(shù)的默認(rèn)參數(shù),是C++入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-09-09
  • C語(yǔ)言中宏和函數(shù)的9個(gè)區(qū)別詳解

    C語(yǔ)言中宏和函數(shù)的9個(gè)區(qū)別詳解

    C語(yǔ)言中的宏和函數(shù)是非常相似的,它們都可以完成類似的功能。本文為大家整理了C語(yǔ)言中宏和函數(shù)的9個(gè)區(qū)別,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-04-04
  • 深入了解C語(yǔ)言棧的創(chuàng)建

    深入了解C語(yǔ)言棧的創(chuàng)建

    棧只允許在一端進(jìn)行插入或刪除操作的線性表。首先棧是一種線性表,但是限定這種線性表只能在某一端進(jìn)行插入和刪除操作,這篇文章主要介紹了C語(yǔ)言對(duì)棧的實(shí)現(xiàn)基本操作
    2021-07-07
  • 詳解在C++中顯式默認(rèn)設(shè)置的函數(shù)和已刪除的函數(shù)的方法

    詳解在C++中顯式默認(rèn)設(shè)置的函數(shù)和已刪除的函數(shù)的方法

    這篇文章主要介紹了在C++中顯式默認(rèn)設(shè)置的函數(shù)和已刪除的函數(shù)的方法,文中講到了C++11標(biāo)準(zhǔn)中的新特性,需要的朋友可以參考下
    2016-01-01
  • C++內(nèi)嵌匯編示例詳解

    C++內(nèi)嵌匯編示例詳解

    這篇文章主要介紹了C++內(nèi)嵌匯編,本文的所有代碼是在我自己的VS2008中測(cè)試的,由于環(huán)境的差別,不能保證能在所有的編譯器上運(yùn)行,需要的朋友可以參考下
    2022-01-01
  • C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法

    C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法

    這篇文章主要介紹了C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-09-09
  • C++學(xué)習(xí)之線程詳解

    C++學(xué)習(xí)之線程詳解

    多線程是開發(fā)中必不可少的,往往我們需要多個(gè)任務(wù)并行,就需要多線程開發(fā)。本文將帶大家深入學(xué)習(xí)一下C++中的常用的一些知識(shí)點(diǎn),感興趣的同學(xué)可以了解一下
    2021-12-12
  • C語(yǔ)言單鏈表的圖文示例講解

    C語(yǔ)言單鏈表的圖文示例講解

    單鏈表是鏈表的其中一種基本結(jié)構(gòu)。一個(gè)最簡(jiǎn)單的結(jié)點(diǎn)結(jié)構(gòu)如圖所示,它是構(gòu)成單鏈表的基本結(jié)點(diǎn)結(jié)構(gòu)。在結(jié)點(diǎn)中數(shù)據(jù)域用來存儲(chǔ)數(shù)據(jù)元素,指針域用于指向下一個(gè)具有相同結(jié)構(gòu)的結(jié)點(diǎn)。?因?yàn)橹挥幸粋€(gè)指針結(jié)點(diǎn),稱為單鏈表
    2023-02-02
  • 詳解QML?調(diào)用?C++?中的內(nèi)容

    詳解QML?調(diào)用?C++?中的內(nèi)容

    這篇文章主要介紹了QML?怎么調(diào)用?C++?中的內(nèi)容,這里主要是總結(jié)一下,怎么在 QML 文件中引用 C ++ 文件里定義的內(nèi)容,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10

最新評(píng)論