C語言——指針的進階——第1篇——(第26篇)

堅持就是勝利

文章目錄

  • 一、字符指針
    • 1、面試題
  • 二、指針數組
  • 三、數組指針
    • 1、數組指針的定義
    • 2、&數組名 VS 數組名
    • 3、數組指針的使用
      • (1)二維數組傳參,形參是 二維數組 的形式
      • (2)二維數組傳參,形參是 指針 的形式
      • (3)總結
  • 四、數組傳參和指針傳參
    • 1、一維數組傳參
    • 2、二維數組傳參
    • 3、一級指針傳參
    • 4、二級指針傳參
  • 五、函數指針
    • 1、舉例理解:
    • 2、分析兩段有趣的代碼
      • (1)代碼1
      • (2)代碼2
        • 學習 typedef 類型重命名
        • 代碼2太復雜,簡化為如下形式

初級階段:
1、指針就是個變量,用來存放地址,地址唯一標識一塊內存空間。
2、指針的大小是固定的 4/8 個字節(32位平臺 / 64位平臺)。
3、指針是有類型,指針的類型決定了指針的 + - 整數的步長,指針解引用操作的時候的權限。
4、指針的運算。

一、字符指針

在指針的類型中,我們知道有一種指針類型為字符類型 char*
一般使用:

#include <stdio.h>int main()
{char ch = 'w';char* ps = &ch;*ps = 'w';return 0;
}

還有一種使用方式如下:

#include <stdio.h>int main()
{char arr[] = "abcde";//[a b c d e \0]const char* p = "abcde";   //本質是:指針變量p 指向 首字符 a 的地址//并且 "abcde" 是 "常量字符串",得加上 constprintf("%s\n", p);   //結果:abcde  printf("%c\n", *p);  //結果:areturn 0;
}
代碼:const char* p = "abcde";

特別容易以為是 把字符串 “abcde” 放到字符指針 p 中,
但是本質是把 字符串 abcde 首字符 的地址放到了 p 中。

在這里插入圖片描述

1、面試題

#include <stdio.h>int main()
{char str1[] = "abcde";char str2[] = "abcde";const char* str3 = "abcde";const char* str4 = "abcde";if (str1 == str2){printf("str1 and str2 are same\n");}else{printf("str1 and str2 are not same\n");  //正確}if (str3 == str4){ printf("str3 and str4 are same\n");  //正確}else{printf("str3 and str4 are not same\n");}return 0;
}

在這里插入圖片描述

在這里插入圖片描述
這里 str3 和 str4 指向的是一個同一個常量字符串。C/C++ 會把常量字符串存儲到單獨的一個內存區域,當幾個指針指向同一個字符串的時候,它們實際會指向同一塊內存。
但是用相同的常量字符串去初始化不同的數組的時候就會開辟出不同的內存塊。所以 str1 和 str2 不同,str3 和 str4 不同。

二、指針數組

指針數組 是一個 存放指針 的數組。

int* arr1[10];  //整型指針的數組
char* arr2[4];  //一級字符指針的數組
char** arr3[5];  //二級字符指針的數組
//使用 指針數組 模擬實現 二維數組#include <stdio.h>int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };int* arr[3] = { arr1,arr2,arr3 };int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", arr[i][j]);}printf("\n");}return 0;
}

在這里插入圖片描述
在這里插入圖片描述

三、數組指針

1、數組指針的定義

類比:
整型指針——指向整型變量的指針,存放整型變量的地址的指針變量。
字符指針——指向字符變量的指針,存放字符變量的地址的指針變量。

數組指針——指向數組的指針,存放數組的地址的指針變量。

在這里插入圖片描述

2、&數組名 VS 數組名

int arr[10];

arr 和 &arr 分別是什么?
arr 是 數組名,數組名 表示 數組首元素 的地址。

那 &arr 數組名 到底是什么?

#include <stdio.h>int main()
{int arr[10] = { 0 };printf("arr = %p\n", arr);printf("&arr = %p\n", &arr);printf("arr + 1 = %p\n", arr + 1);printf("&arr + 1 = %p\n", &arr + 1);return 0;
}

在這里插入圖片描述
&arr 和 arr ,雖然值是一樣的,但是意義是不一樣的。

實際上:
&arr 表示的是:數組的地址,而不是數組首元素的地址。
&arr 的類型是:int(*)[10],是一種數組指針類型
數組的地址 + 1,跳過整個數組的大小,所以,&arr +1 相對于 &arr 的差值是40。

#include <stdio.h>int main()
{int arr[10] = { 0 };printf("%p\n", arr);   //類型:int *printf("%p\n", &arr[0]);  //類型:int *printf("%p\n", &arr);   //類型:int(*)[10]   數組指針類型return 0;
}

在這里插入圖片描述

電腦中 int[10] * ,這么寫是錯誤的,是為了方便你理解,其實應該寫成 int( * )[10] , 數組指針類型

在這里插入圖片描述
在這里插入圖片描述

在這里插入圖片描述

#include <stdio.h>int main()
{int arr[10] = { 0 };printf("%p\n", arr);printf("%d\n", sizeof(arr));return 0;
}

在這里插入圖片描述

總結:(數組名的理解)
數組名是數組首元素的地址。

有2個例外:
(1)sizeof(數組名),這里的數組名不是數組首元素的地址,數組名表示整個數組,sizeof(數組名) 計算的是 整個數組的大小,單位是字節。
(2)&數組名,這里的數組名表示整個數組,&數組名 取出的是 整個數組的地址。
(3)除此之外,所有的地方的數組名都是數組首元素的地址。

3、數組指針的使用

#include <stdio.h>int main()
{char  arr[6] = "abcde";char(*p)[6] = &arr;printf("%c \n", *(*p));  //p 中放的是 &arr , *p 就是 arr(數組名), *(*p) 就是 *(arr),就是首字母,就是對首字母地址解引用printf("%s", p);return 0;
}

在這里插入圖片描述

#include <stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = arr;int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(p + i));}putchar('\n');for (i = 0; i < 10; i++){printf("%d ", *(arr + i));}return 0;
}

用指針數組,來遍歷輸出數組的每個元素,感覺使用起來很變扭!

#include <stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int(*p)[10] = &arr;  //*&arr 那么*&就互相抵消,*&arr 就是 arr(數組名)int i = 0;for (i = 0; i < 10; i++){                                //p 中存放的就是 &arr//由于*&arr 就是 arr(數組名)//*p 就是 *(&arr)  就是 arr(數組名)printf("%d ", *((*p) + i));  //(*p) 就是 arr(數組名) //(*p)+i  就是 arr + i  //*(arr + i) 就是 遍歷數組的每一個元素putchar('\n');printf("%d ", (*p)[i]);    //arr[i]//效果一樣}return 0;
}

在這里插入圖片描述
所以,數組指針 不是像 上面的代碼那樣使用,這樣反而弄巧成拙了。

問:數組指針怎么使用呢?
答:一般在 二維數組 上才方便。

(1)二維數組傳參,形參是 二維數組 的形式

//二維數組傳參,形參是二維數組的形式#include <stdio.h>void Print(int arr[3][5], int r, int c)
{int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", arr[i][j]);}printf("\n");}
}int main()
{int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };Print(arr, 3, 5);return 0;
}

(2)二維數組傳參,形參是 指針 的形式

//二維數組傳參,形參是指針的形式#include <stdio.h>void Print(int(*p)[5], int r, int c)
{int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){printf("%d ", *(*(p + i) + j));  //這個不好理解,下方圖片仔細想}printf("\n");}
}int main()
{int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };//arr 是數組名,數組名表示數組首元素的地址Print(arr, 3, 5);return 0;
}

在這里插入圖片描述
在這里插入圖片描述

(3)總結

一維數組傳參,形參的部分可以是數組(本質還是指針),也可以是指針

//一維數組傳參,形參的部分可以是數組(本質還是指針),也可以是指針void test1(int arr[5], int sz)    //int arr[5] 本質還是指針,只是寫成了數組的形式
{}void test2(int* p, int sz)
{}int main()
{int arr[5] = { 0 };test1(arr, 5);test2(arr, 5);return 0;
}

二維數組傳參,形參的部分可以是數組,也可以是指針

//二維數組傳參,形參的部分可以是數組,也可以是指針void test3(char arr[3][5], int r, int c)
{}void test4(char(*p)[5], int r, int c)
{}int main()
{char arr[3][5] = { 0 };test3(arr, 3, 5);test4(arr, 3, 5);return 0;
}

學了指針數組和數組指針,我們來一起回顧并看看下面的代碼的意思:
在這里插入圖片描述

四、數組傳參和指針傳參

在寫代碼的時候,難免要把【數組】或者【指針】傳給函數,那函數的參數該如何設計?

1、一維數組傳參

在這里插入圖片描述

#include <stdio.h>void test(int arr[])  //ok?  答:可行
{}                    void test(int arr[10])  //ok?  答:可行
{}                      //[10]中的10,可以寫成100、1000都行,反正 int arr[10] 本質就是指針void test(int *arr)  //ok?  答:可行
{}void test2(int *arr[20])  //ok?  答:可行
{}                        //原因:形參實參類型保持一致void test2(int** arr)  //ok?  答:可行
{}                     //int *arr2[20] 就是數組20個元素,每個元素類型都是 int * //一級指針的地址,就是 二級指針
int main()
{int arr[10] = { 0 };int* arr2[20] = { 0 };test(arr);test2(arr2);return 0;
}

2、二維數組傳參

在這里插入圖片描述

void test(int arr[3][5])  //ok?  答:可行
{}void test(int arr[][])  //ok?  答:不可行
{}                      //不可以省略行void test(int arr[][5])  //ok?  答:可行
{}//總結:二維數組傳參,函數形參的設計只能省略第一個 [ ] 的數字
//因為對一個二維數組,可以不知道有多少行,但是必須知道一行多少元素
//這樣才方便運算void test(int* arr)  //ok?  答:不可行
{}                   //arr 是數組指針的地址,arr 是數組第一行的地址 arr == &arr[0]void test(int* arr[5])  //ok?  答:不可行
{}                      //int* arr[5] 是指針數組,有5個元素,每個元素的類型是 int*void test(int(*arr)[5])  //ok?  答:可行
{}                       //int (*arr)[5]數組指針,元素類型是 int[5],是數組某一行的指針void test(int **arr)  //ok?  答:不可行
{}                    //arr的類型是 int*,明顯錯誤//二級指針 是 用來接收一級指針的地址的
int main()
{int arr[3][5] = { 0 };test(arr);
}

3、一級指針傳參

#include <stdio.h>void print(int* p, int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d\n", *(p + i));}
}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]);//一級指針p,傳給函數print(p, sz);return 0;
}

當一個函數的參數部分為一級指針的時候,函數能接受什么參數?

void test(char* p)
{}char ch = '2';
char* ptr = &ch;
char arr[] = "abcde";test(&ch);  //第一種
test(ptr);  //第二種
test(arr);  //第三種

4、二級指針傳參

區別:
二維數組傳參,形參既可以是數組,也可以是指針。
二級指針傳參,形參只可以是指針。

#include <stdio.h>void test(int** ptr)
{printf("num = %d\n", **ptr);
}int main()
{int n = 10;int* p = &n;int** pp = &p;test(pp);test(&p);return 0;
}

當函數的參數為二級指針的時候,可以接受什么參數?

void test(char** p)
{}int main()
{char c = 'b';char* pc = &c;char** ppc = *pc;char* arr[10];   //指針數組,類型是 char*//二級指針,是一級指針的地址test(&pc);test(ppc);test(arr);  //是數組元素類型 char* 的地址return 0;
}

五、函數指針

函數指針——指向函數的指針
在這里插入圖片描述
函數名 是 函數的地址
&函數名 也是 函數的地址
在這里插入圖片描述

int (*pf)(int , int) 去掉 指針名 pf ,剩下的就是指針類型 int ( * )(int , int)

在這里插入圖片描述

1、舉例理解:

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

2、分析兩段有趣的代碼

(1)代碼1

//代碼1
( *( void (*)() )0 )();

1、將 0 強制轉換成 void (*)() 類型的函數指針
2、調用 0 地址處的這個函數

在這里插入圖片描述

//0x0012ff40 可以理解為:int
//           也可以理解為:int*//同理://0          可以理解為:int
//           也可以理解為:int*//void (*p)()  -- p是函數指針
//void (*)() 是函數指針類型//從0開始切入分析
//0 被強制類型轉換為 void (*)() 類型的函數指針
// 0 此時變成了函數指針,也就是說 0 變成 地址了
//* 解引用  ()代表 函數
// 函數指針就是指針,指針就是地址
//調用 0 地址 處的這個函數//代碼1
( *( void (*)() )0 )();

我們自己寫的應用程序是不可以訪問 0 地址的。
舉例的是 系統程序。

(2)代碼2

//代碼2
void (*signal(int , void(*)(int)))(int);

在這里插入圖片描述

//signal 是一個函數聲明
//signal 函數有 2 個參數
//第 1 個參數的類型是 int
//第 2 個參數的類型是 void(*)(int) 函數指針類型,該函數指針指向的函數有一個 int 類型的參數,返回類型是 void 
//signal 函數的返回類型也是 void(*)(int) 函數指針類型,該函數指針指向的函數有一個 int 類型的參數,返回類型是 void //代碼2
void (*signal(int , void(*)(int)))(int);
學習 typedef 類型重命名
typedef unsigned int uint;  //將 unsigned int 重命名為 uint  //正確
typedef int* ptr_r;  //將 int* 重命名為 ptr_r  //正確

數組指針,這種書寫格式是錯誤的

//對 數組指針 進行 類型重命名typedef int(*)[10] parr_t;  //這么寫就是錯誤的,不能以這種方式來書寫

應該改為這樣
在這里插入圖片描述

//對 數組指針 進行 類型重命名//錯誤的書寫格式
//typedef int(*)[10] parr_t;  //這么寫就是錯誤的,不能以這種方式來書寫//正確的書寫格式
typedef int(*parr_t)[10];

函數指針類型 跟上面的書寫格式一致

代碼2太復雜,簡化為如下形式

原代碼:

void (*signal(int , void(*)(int)))(int);

簡化后:

typedef void(*pfun_t)(int);  //將 void (*)(int)  重命名為 pfun_t
pfun_t signal(int, pfun_t);

微軟雅黑字體
黑體
3號字
4號字
紅色
綠色
藍色

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/716447.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/716447.shtml
英文地址,請注明出處:http://en.pswp.cn/news/716447.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【RT-Thread應用筆記】英飛凌PSoC 62 + CYW43012 WiFi延遲和帶寬測試

文章目錄 一、安裝SDK二、創建項目三、編譯下載3.1 編譯代碼3.2 下載程序 四、WiFi測試4.1 掃描測試4.2 連接測試 五、延遲測試5.1 ping百度5.2 ping路由器 六、帶寬測試6.1 添加netutils軟件包6.2 iperf命令參數6.3 PC端的iperf6.4 iperf測試準備工作6.5 進行iperf帶寬測試6.6…

未來三年AI的深度發展:AIGC、視頻AI與虛擬世界構建

人工智能&#xff08;AI&#xff09;正站在科技演進的前沿&#xff0c;未來三年將見證其在多領域實現更深層次的突破。以下是對AI發展方向的深度探討以及其對各行業的深遠影響&#xff1a; 1. AIGC的演進與全面提升&#xff1a; AIGC&#xff0c;即AI通用性能力&#xff0c;將…

AI前沿-YOLOV9算法

AI前沿-YOLOV9算法 關注B站查看更多手把手教學&#xff1a; 肆十二-的個人空間-肆十二-個人主頁-嗶哩嗶哩視頻 (bilibili.com) 今天我們來一起說下最近剛出的YOLOV9算法 論文和源碼 該算法的原始論文地址為&#xff1a;https://arxiv.org/abs/2402.13616 該算法的原始代碼地…

Muduo庫編譯學習(1)

1.muduo庫簡介 muduo是由Google大佬陳碩開發&#xff0c;是一個基于非阻塞IO和事件驅動的現代C網絡庫&#xff0c;原生支持one loop per thread這種IO模型&#xff0c;該庫只支持Linux系統&#xff0c;網上大佬對其褒貶不一&#xff0c;作為小白用來學習就無可厚非了。 git倉庫…

b站小土堆pytorch學習記錄——P14 torchvision中的數據集使用

文章目錄 一、前置知識如何查看torchvision的數據集 二、代碼&#xff08;附注釋&#xff09;及運行結果 一、前置知識 如何查看torchvision的數據集 &#xff08;1&#xff09;打開官網 https://pytorch.org/ pytorch官網 &#xff08;2&#xff09;打開torchvision 在Do…

Linux:top顯示信息

簡介 top命令是Linux系統中常用的性能監控工具&#xff0c;它可以實時顯示系統中各個進程的CPU使用情況以及其他系統資源的使用情況。當您在終端中運行top命令時&#xff0c;它會顯示一個實時更新的列表。 CPU相關的信息 CPU狀態&#xff1a; us&#xff08;user&#xff09…

梯度和梯度損失

梯度主要用于計算圖像融合過程中的梯度損失&#xff0c;對應的損失函數是梯度損失&#xff08;loss_grad&#xff09;。 梯度的作用&#xff1a; 圖像的梯度是指圖像中每個像素的灰度變化率&#xff0c;通常用于表示圖像的邊緣和紋理信息。在圖像融合任務中&#xff0c;通過計算…

Unity游戲輸入系統(新版+舊版)

使用新版還是舊版 舊版 using System.Collections; using System.Collections.Generic; using UnityEngine;public class c5 : MonoBehaviour {void Start(){}void Update(){// 注意要在游戲中 點鼠標鍵盤進行測試// 鼠標// 0左鍵 1右鍵 2滾輪if (Input.GetMouseButtonDown(0)…

【javaSE-語法】lambda表達式

【javaSE-語法】lambda表達式 1. 先回憶一下&#xff1a;1.1 接口不能直接通過關鍵字new進行實例化1.2 函數式接口1.3 匿名內部類1.31 匿名內部類在代碼中長啥樣&#xff1f;1.32 構造一個新的對象與構造一個擴展了某類的匿名內部類的對象&#xff0c;兩者有什么區別&#xff1…

maven--->maven中的<properties>屬性有什么作用?

&#x1f64c;&#x1f64c;&#x1f64c;&#x1f64c;&#x1f64c;&#x1f64c; 在Maven中&#xff0c;元素用于定義項目中可重用的屬性值。這些屬性值可以在項目的POM文件中被引用&#xff0c;以便在整個項目中統一管理和使用。通過使用元素&#xff0c;可以避免在POM文件…

SpringBean生命周期之InitializingBean,初始化bean

1 yml文件 weixin:appid: aaaaaapartner: 12313214partnerkey: ccccccert: C:\\Users\\lenovo\\Desktop 2 Bean初使化 import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframewor…

midjourney提示詞語法

更高級的提示可以包括一個或多個圖像URL、多個文本短語和一個或更多個參數 Image Prompts 可以將圖像URL添加到提示中&#xff0c;以影響最終結果的樣式和內容。圖像URL總是位于提示的前面。 https://docs.midjourney.com/image-prompts Text Prompt 要生成的圖像的文本描述。…

YOLOv6、YOLOv7、YOLOv8網絡結構圖(清晰版)

承接上一篇博客&#xff1a;YOLOv3、YOLOv4、YOLOv5、YOLOx的網絡結構圖(清晰版)_yolox網絡結構圖-CSDN博客 1. YOLOv6網絡結構圖 2. YOLOv7網絡結構圖 3. YOLOv8網絡結構圖

搭建 LNMP 架構

一 理論知識 &#xff08;一&#xff09;架構圖 &#xff08;二&#xff09;CGI 由來 最早的Web服務器只能簡單她響應瀏覽器發來的HTTP請求&#xff0c;并將存儲在服務器上的HTML文件返回給瀏覽器&#xff0c;也就是靜態html文件&#xff0c;但是后期隨著網站功能增多網站開…

c++階梯之模板初階

1. 泛型編程 void Swap(int& x, int& y) {int tmp x;x y;y tmp; }void Swap(double& x, double& y) {double tmp x;x y;y tmp; }void Swap(char& x, char& y) {char tmp x;x y;y tmp; } int main() {int a 10, b 20;double c 1.1, d 2.2…

《Spring Security 簡易速速上手小冊》第7章 REST API 與微服務安全(2024 最新版)

文章目錄 7.1 保護 REST API7.1.1 基礎知識詳解7.1.2 重點案例&#xff1a;使用 JWT 進行身份驗證和授權案例 Demo 7.1.3 拓展案例 1&#xff1a;API 密鑰認證案例 Demo測試API密鑰認證 7.1.4 拓展案例 2&#xff1a;使用 OAuth2 保護 API案例 Demo測試 OAuth2 保護的 API 7.2 …

讀書筆記-三國演義-夏侯惇

夏侯惇&#xff08;公元夏侯惇&#xff08;公元190年-公元220年&#xff09;&#xff0c;字元讓&#xff0c;沛國譙縣&#xff08;今安徽亳州市&#xff09;人&#xff0c;是中國東漢末年至三國時期魏國重要將領之一。他是曹操麾下的得力將領&#xff0c;以勇猛忠誠而聞名于世。…

linux安裝matlab獲取許可證

1.點擊許可證 2. 3. 4. 4.主機ID 打開linux輸入 /sbin/ifconfigether后邊的就是 6.計算機登錄名 打開linux輸入 whoami7. 8. 9.

局域網https自簽名教程

們的客戶是在內網環境里面&#xff0c;所以就只能用自簽名證書來搞&#xff0c;我一想這還不容易&#xff0c;就迅速的百度了一下隨便找了個文章開始照貓畫虎&#xff0c;很快就弄完了&#xff0c;但是弄完后發現還是有問題&#xff0c;而且https 還是報不安全&#xff0c; 1、…

(規劃)24屆春招和25屆暑假實習路線準備規劃

春招&&暑假實習&#xff1a; 1.八股&#xff1a; 可以去一些八股網站上面進行閱讀。 2.項目&#xff1a;至少準備1-2個項目&#xff0c;可以條理清晰的進行項目介紹和難點剖析。 3.算法&#xff1a; hot100 &#xff0c;劍指offer 能刷的很熟&#xff0c;算法關就差…