C語言是C++的母語,萬變不離指針,指針是C語言的一大法寶!

2021-01-07 騰訊網

我們都知道C語言是一門過程性語言,所謂過程性就是在解決問題時,將問題按步驟分解。

例如,做菜的時候,先點火,再倒油,接著下菜翻炒,最後加鹽和醬油。但有時候借鑑面向對象的思想來組織代碼,邏輯層次會更加清晰。

C和C++的最大區別便是,C++有類,C沒有類的概念。單單這一個類使得C缺失很多的東西。好在C有結構體,勉強可以當0.1個類來使用。

眾所周知,類有三大特性:封裝、繼承、多態。我們來看看C語言如何借鑑類的三大特性來更好的組織代碼。

1、繼承

C語言沒有嚴格意義上的繼承,可以藉助結構體嵌套實現類似於繼承的形式,但始終不盡人意。

C++的類可以實現成員的訪問控制,例如將變量b聲明成private,那麼外部就無法訪問。但C的結構體做不到。

在C++裡頭,父親的私有成員,兒子是無法訪問的。結構體嵌套也做不到。因為結構體根本就沒有訪問控制的概念。

對於C++而言,訪問控制實質上是在編譯層做的,我們仍舊可以通過指針來間接訪問。

例如:

儘管b被聲明成私有,但我們仍舊有辦法訪問它(藉助指針繞過語法檢查):

2、封裝

封裝就是把數據和方法打包到一個類裡面。C++的實現大致如下:

這樣做的好處是顯而易見的。

一個類實現了一個小模塊,使得代碼結構比較清晰。對外接口和數據定義成public,允許調用者直接訪問。

內部接口和數據定義成private,外部不可見。

在 QT 中,為了更好的隱藏一個類的具體實現,一般是一個公開頭文件、一個私有頭文件,私有頭文件中定義實現的內部細節,公開頭文件中定義開放給客戶程式設計師的接口和公共數據。看看QObject (qobject.h),對應有一QObjectPrivate(qobject_p.h ) ,其他的也類似。

我們可以藉助C語言的指針和結構體來實現方法和數據的封裝。基本框架如下:

在結構體裡定義成員變量很容易,直接int a;

在結構體裡定義成員函數要使用函數指針,比如:

所以,我們把上面的框架具體化就是:

實際上,C++的成員函數也是通過函數指針的形式來實現,本質上是一致的。

我們都知道類的成員函數和類的成員變量是分開存儲的,同一個類的所有對象,成員函數只需要佔據一份地址空間。

在定義結構體之後,函數指針並沒有賦值,一般我們會定義一個結構體初始化函數來初始化結構體成員,這有點類似於類的構造函數,但類的構造函數在創建對象時自動調用,而我們這個結構體初始化函數只能自己手動調用了。

同樣的,對標C++的析構函數,我們在C語言裡頭有一個去初始化的函數來完成模塊的去初始化,這種思想不就是一樣的嗎?

偽構造函數

注意,我們把兩個operation函數定義成了static,這樣子文件之外的函數就不能調用它,只能通過manager結構體來調用。是不是感覺有點封裝的意味。

去初始化函數我就不寫了。

為了達到上面的目的,簡單修改下,我們把函數operation2定義成一種類型,

結構體定義稍作修改:

結構體初始化函數也要做相應的修改,增加了一個函數指針形參:

通過上面的操作,我們用結構體和函數指針完成了模塊化封裝。

我看了網上的博客,有些人為了特意模仿類,還用以下方式實現了類似於類的構造函數:

以及類似於類的析構函數:

使用示例:

個人不是很喜歡這種做法,萬一忘記調用manager_delete還有內存洩露的風險。

結構體歸根到底還是結構體,不能實現成員對外不可見。而C++中將成員聲明成private之後,外部就無法訪問了。

C語言裡想這麼做,只能將該成員移出結構體,定義為static形式。因為C不支持在結構體內部定義static變量(不信,你可以自己去試下)。

為何不能在結構體內定義static變量,想想就知道了,static變量的地址在編譯連結之後是唯一且確定的,而結構體只有在實例化時才能確定其地址,並且每個結構體實例都有自己的地址空間。

3、多態

多態在上面的例子也有體現。C語言實現的多態並非是嚴格意義上的多態,但是這種思想的應用很廣泛,我們姑且叫它多態吧。你不解C++的多態也沒關係,絲毫不影響你理解下文。

linux的VFS便借鑑了這種思想。VFS(Virtual File System)是內核提供的文件系統抽象層,其提供了文件系統的操作接口,可以隱藏底層不同文件系統的實現。

一個文件系統無非就是實現對文件、目錄的管理。針對文件VFS定義了統一的結構體:

strcut file代表一個文件,每種文件系統(比如ext3,vfat)實現讀寫等操作的方式都不一樣,所以將這些方法封裝成函數指針,統一定義在結構體struct file_operations內。

每個文件系統各自完成自己的實現。

再寫一個實際的例子。

定義一個人的標準接口和數據如下:

中國人見面時,說你好:

英國人見面時,說hello:

現在來初始化它們各自的問候方式:

英國人和中國人對外呈現都是struct man,其見面問候的接口都是man.say_hello,但其底層實現卻可以不一樣。

並且我們可以在程序運行時,隨意的更改中國人的問候方式。比如嬰兒時期,只會「哇哇」叫,長大了才會說「你好」,我們可以改變成員say_hello的值,讓其在不同時期指向不同的函數,從而達到運行時多態的目的。

其實呢,C++的多態,也是通過函數指針來實現的,學習過C++的同學就會知道,含有虛函數的類,會維護一個虛函數表,裡面存放了虛函數的地址。

所以說啊,C語言是C++的母語,萬變不離指針,指針是C語言的一大法寶。

相關焦點

  • c語言指針與字符數組
    ,字符串的使用在C語言中也是非常重要的,常常會遇到一些操作,如字符串的修改、拷貝、字符串長度等,在物聯網的應用中也尤為突出,物聯網應用中所用的模組,大多是需要使用AT指令的,這就需要對字符串的操作。我們定義一個指針變量char * pc;內存示意,通常指針是4個字節,假設這裡的地址是200
  • C 語言程序設計---指針
    上次 C 語言寫到了數組,有些書是先講指針,有些書是先講函數,按照我以前學習 C 語言的順序,以及對 C 語言的理解,學習的順序是這樣的:數組--->指針--->函數,所以本篇文章講解 C 之指針。C 語言是值得好好學習的一門語言,是一門基礎語言,更是我編程入門的語言,其中很多編程思想,至今影響著我,在工作中對我的幫助很大。
  • C語言指針詳解
    先聲明幾個指針放著做例子:例一:(1)int *ptr;(2)char *ptr;(3)int **ptr;(4)int (*ptr)[3];(5)int *(*ptr)[4];如果看不懂後幾個例子的話,請參閱我前段時間貼出的文章 如何理解c和c++的複雜類型聲明>&
  • 為什麼指針被譽為 C 語言靈魂?
    所以,要想徹底理解指針,首先要理解 C 語言中變量的存儲本質,也就是內存。在 C 語言中我們會這樣定義變量:int a = 999;char c = 'c';當你寫下一個變量定義的時候,實際上是向內存申請了一塊空間來存放你的變量。
  • C語言的那些小秘密之函數指針
    我們經常會聽到這樣的說法,不懂得函數指針就不是真正的C語言高手。我們不管這句話對與否,但是它都從側面反應出了函數指針的重要性,所以我們還是有必要掌握對函數指針的使用。先來看看函數指針的定義吧。本文引用地址:http://www.eepw.com.cn/article/270442.htm  函數是由執行語句組成的指令序列或者代碼,這些代碼的有序集合根據其大小被分配到一定的內存空間中,這一片內存空間的起始地址就成為函數的地址,不同的函數有不同的函數地址,編譯器通過函數名來索引函數的入口地址,為了方便操作類型屬性相同的函數,c/c++引入了函數指針,函數指針就是指向代碼入口地址的指針
  • C/C++ 語言指針詳解(一)
    指針是 C 語言的精髓,因為有了指針,C 語言可以和彙編語言比效率。在教學過程中,指針這部分內容常常給學生帶來困惑。下面我來說說指針在 C 及 C++語言中的用法。1.指針的概念上一篇文章,變量的三個要素之一,變量的值包括變量的數據值和變量的地址值。
  • C語言簡明教程(九)指針(二)
    通過改變指針變量的值使它指向字符串中的不同字符。編寫程序 11-3.c 用 i++ 來遍歷數組,程序 11-4.c 用 p++ 遍歷數組。解題思路:定義一個函數 copy_string 用來實現字符串複製的功能,在主函數中調用此函數,函數的形參和實參可以分別用字符數組名或字符指針變量。分別編程,以供分析比較。編寫程序 11-5.c 用字符數組名作為函數參數,程序 11-6.c 用字符型指針變量作實參,程序 11-7.c 用字符指針變量作形參和實參。
  • c語言的指針真的很有趣
    指針是一個很有趣的東西,想要成為一個出色的程式設計師,那麼指針就要學的好,指針涉及到內存分配的問題。每一個變量都有自己的內存,可以使用&來訪問變量存儲的位置。ta就是一個地址。這語句中用*來指定指針變量int *ip; /* 一個整型的指針 */double *dp; /* 一個 double 型的指針 */float *fp; /* 一個浮點型的指針 */
  • C語言陷阱與技巧31節,都說void*指針是「萬能指針」,它萬能在哪
    (void *dest, constvoid *src, size_t n);void *memmove(void *dest, constvoid *src, size_t n);前面的章節在討論C語言指針時,提到指針從某種程度上來說,無非就是一個地址,它的類型只是用於說明數據結構的。
  • C 語言指針詳解
    指針pp為指向指針p的指針定義指針變量C語言中,定義變量時,在變量名 前 寫一個 * 星號,這個變量就變成了對應變量類型的指針變量。必要時要加( ) 來避免優先級的問題。引申:C語言中,定義變量時,在定義的最前面寫上typedef ,那麼這個變量名就成了一種類型,即這個類型的同義詞。
  • C語言 | 指向指針的指針
    「要成為絕世高手,並非一朝一夕,除非是天生武學奇才,但是這種人…萬中無一」這道理放在C語言學習上也一併受用
  • 為什麼有人說指針是 C 語言的精髓?
    其他高級語言都是把這塊基本屏蔽掉了,不在讓程式設計師直接操作指針,這裡不直接操作指的是不讓程式設計師用指針進行運算和強轉而不是徹底沒有了。 繼續說為什麼學習指針,為什麼學習指針就必須要說到指針的優點了。 a、指針可以直接操作變量地址,所以很靈活。 b、指針操作會減少很多變量的拷貝使得程序性能提升。 c、可以動態分配內存。
  • C語言函數指針之回調函數
    如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。2 為什麼要用回調函數?
  • C+相比其他語言到底難在哪裡?
    看過程式語言排行榜的都知道,c/c++自02年以來,不管時代如何發展,其排名一直在前五以內,足見其在程式語言界的地位。編程界流行這麼一句話:c幾乎什麼都能做,c++幾乎什麼都能做好,足見其功能的強大。
  • 深入淺出剖析C語言函數指針與回調函數(一)
    百度的權威解釋如下:回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。
  • C 語言會比 C++ 快?
    在一些算法中,內部函數將使用原始指針來最佳化發布的運行性能,這意味著無論如何都不會執行邊界檢查。此外,算法輸入使用原始指針,需要仔細處理。由於在許多關鍵位置使用了原始指針,我會使用 Address Sanitizer 作為 CI 管道的一部分運行構建,偶爾也會在本地運行嘗試,因此我對缺少越界訪問感到安全。
  • C語言的指針,簡介
    指針,是一個表示變量或函數的地址的無符號整數。指針的字節數,與CPU的位數有關,32位機是4位元組,64位機是8位元組。與高級語言的long類型的大小是一致的。所以在java的JNI庫裡,如果需要把native層的C結構體的指針保存到java層,一般是定義一個long類型的變量。指針指向的變量,可以是普通變量、數組變量、結構體變量,以及數組元素、結構體的成員變量,還可以是指針變量。指針還可以指向函數,叫做函數指針。
  • C語言:數組和指針
    數組運行指針(Pointer)指針變量保存的是數據存儲的地址。'*'間接引用,『*』的優先級小於『[]』。c是指針數組,int *a[3],[]符號優先級高,先看a[3]是數組,int *整形指針;d是數組指針,int (*a)[3],()符號優先級高,(*a)是指針,指向有三個元素的整形一維數組;e是指向可變長度數組的指針,int (*a)[](其實和d一樣,在二維數組可以當做行指針
  • 入門C語言中的數組,字符串常量與指針
    int q[3] = {1, 2, 3};要聲明對另一個文件中定義的數組的外部引用,請使用extern int a[];字符串常量在C語言中,類型數組char用於表示字符串,字符串的結尾由設置為0的字節標記(也稱為NUL字符)以下定義都將其數組設置為相同的值
  • 深入淺出剖析C語言函數指針與回調函數
    回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進行響應。