【典藏】大佬們都在用的結構體進階小技巧
掃描二維碼
隨時隨地手機看文章
1、來聊聊(輕松一刻)
今天跟大家分享一首華晨宇的《我管你》,個人覺得這首歌表達了一種年輕人的熱血感,每次聽都讓自己非常來勁。最近工作挺忙的,寫文章或許已經(jīng)成為了一種興趣和愛好了吧,也希望每次作者的嘮叨都能帶給各位小伙伴一些小小的收獲。
2、奇妙的"結構體"
今天講解這塊內(nèi)容,主要是受上篇文章結構體內(nèi)部對齊的影響,來重新為大家介紹一下結構體(如果小伙伴對結構體的基礎知識還不具備的話得回頭看一下相關C語言的書籍).
結構體:字面上的意思就是有著層次結構的一種數(shù)據(jù)形式,所謂的層次結構就是我們在結構體中定義的各種成員了,再白話一點:結構體就是一個數(shù)據(jù)包,里面可以包含各種各樣的數(shù)據(jù)。對于這句話我們還要深入挖掘一下"各種各樣的數(shù)據(jù)”,對于我們編程而言-“一切皆是數(shù)據(jù)”。進一步理解,結構體里面可以包括整個程序中你想包含的東西。如果這樣的推敲沒錯的話,那結構體還真有點東西了,作者畫個圖供大家揣摩,結構體的奇妙之處全在圖里面了!
3、結構體"硬核"技巧
對于結構體的應用太多了,今天這篇文章我主要為大家總結平時關于結構體的一些獨特小技巧,對于結構體更多優(yōu)秀的編程表現(xiàn),只能作者后續(xù)總結歸納以后分享給大家。好,下面進入這些有用的技巧:
1)結構體初始化有講究
我們大部分初學的小伙伴可能都不怎么會跟結構體變量直接進行初始化,(哈哈,可能很多小伙伴定了了變量根本就不會進行初始化)不過還是要養(yǎng)成比較好的編程習慣,雖然現(xiàn)在大部分集成開發(fā)環(huán)境都會為大家把一些全局變量初始化為0,不過對于代碼的可移植性、可預知性考慮還是建議既然定義了就要給一個初始狀態(tài)。
好了,先上代碼:
1#include <stdio.h>
2//結構體定義
3typedef struct __tag_Test
4{
5 int param1;
6 int param2;
7}stTest;
8//結構體初始化方式1
9stTest sTest1 = {
10 .param1 = 1,
11 .param2 = 2
12};
13//結構體初始化方式2
14stTest sTest2 = {
15 1,2
16};
17/*********************************************
18 * Fuction: main
19 * Author : (公眾號:最后一個bug)
20 ********************************************/
21int main(void) {
22
23 printf("sTest1.param1 = %d\n",sTest1.param1);
24 printf("sTest1.param2 = %d\n",sTest1.param2);
25 printf("sTest2.param1 = %d\n",sTest2.param1);
26 printf("sTest2.param2 = %d\n",sTest2.param2);
27 return 0;
28}
2)給同個結構體取多個名字
當你進行C編程時間久了以后,對于所屬性一致的變量等你都會用結構體進行封裝,那么可能出現(xiàn)同一個結構體可能多個地方使用的情況,并且結構體的作用會有所不同,就好像一個人他可能是一名學生,也有可能在外面兼職做一名服務員等等,如果這個人不換一身衣服或者做個標記什么的可能有時候我們難以分辨,那么在C程序里面我們會怎樣為他們換個衣服呢?簡單代碼如下:
1#include <stdio.h>
2
3 struct __tag_Man
4{
5 int Age;
6 int Height;
7};
8
9typedef struct __tag_Man stStudent;
10typedef struct __tag_Man stWaiter;
11
12stStudent sStudent = {
13 .Age = 12,
14 .Height = 20,
15};
16
17stWaiter sWaiter = {
18 .Age = 12,
19 .Height = 20,
20};
21
22int main(void) {
23
24 printf("sStudent.Age = %d\n",sStudent.Age);
25 printf("sStudent.Height = %d\n",sStudent.Height);
26 printf("sWaiter.Age = %d\n",sWaiter.Age);
27 printf("sWaiter.Height = %d\n",sWaiter.Height);
28 printf("公眾號:最后一個bug");
29 return 0;
30}
解析一下:上面的代碼確實挺簡單的,可能有些小伙伴一眼就可以看懂,不過這種使用方法時非常有價值的,特別是以后大家玩算法,比如說:節(jié)點都是一樣的,不過節(jié)點分為子節(jié)點和父節(jié)點,對于一些處理函數(shù)傳入的參數(shù)雖然是一樣的,不過意義卻不同,我們就可以通過這樣的方式進行處理,從而增加代碼可讀性。
3)0地址與結構體的妙用
這一塊的內(nèi)容算是這篇文章的重點內(nèi)容,各位小伙伴們要做好筆記了,作者一直非常強調(diào)一點的是多讀讀大佬們的代碼,并且善于總結一些常用的小技巧供大家平時使用,這不我們今天就拿Linux的kernal中的兩個宏定義來分享幾個結構體小技巧:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
解析一下:
1)第一個宏定義的功能是獲得一個結構體成員距離結構體首地址的偏移量,參數(shù)TYPE : 結構體類型;參數(shù)MEMBER : 結構體成員,其實這個算是比較簡單的,把0地址強制類型轉化為結構體類型指針,然后通過結構體指向成員即可獲得結構體成員變量,然后通過&進行取地址便獲得了結構體成員地址,成員的偏移 = (結構體成員地址 - 結構體首地址);然而結構體首地址為0,這樣成員的偏移 = 結構體成員地址,應該足夠清楚了吧。
2)第二個宏定義的功能是通過結構體成員變量獲得對應的結構體首地址(也就是結構體地址),參數(shù)ptr :結構體成員變量地址;參數(shù)type : 結構體類型;參數(shù)member :結構體成員,這個宏定義可能對于一些小伙伴而言在寫法上有一點點難度,不過其主要分兩部分,第一部分通過typeof獲得成員的類型并定義了一個const指針,定義為const的目的是不讓用戶對0地址的內(nèi)容進行寫操作,對于大部分芯片對不合法區(qū)域進行讀寫會引起異常。第二部分通過使用offsetof宏定義獲得結構體成員相對結構體首地址的偏移,這樣一相減便獲得了當前結構體成員所屬結構體的地址,原理公式:(結構體地址 = 結構體成員地址 - 結構體成員的偏移)。
3)這里大體說一下注意事項 : 在第二點我們談到了typeof關鍵字,該關鍵字是GUN C標準中擴展的關鍵字,所以在使用該宏定義的時候需要注意一下,不然采用其他標準進行編譯可能會報錯。
4)最后幫助大家理解的小程序
作者要說的全在代碼里面了:
#include <stdio.h>
/**********************************
* Fuction : from Linux Kernal
**********************************/
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
/**********************************
* Fuction : 結構體定義區(qū)
* Author : (公眾號:最后一個bug)
**********************************/
typedef struct _tag_Test{
int Member1;
int Member2;
int Member3;
}STest;
int main(void) {
int iTest = 0;
STest stTest;
printf("offsetof(STest , Member3) : %d\n",offsetof(STest , Member3));
printf("&stTest : 0X%X\n",&stTest);
printf("&(stTest.Member3) : 0X%X\n",&(stTest.Member3));
printf("container of Member3 : 0X%X\n",container_of(&(stTest.Member3),STest,Member3));
//這里主要是進一步讓大家理解第二個宏
//且右側必須加()
iTest = ({int Val = 5; Val;});
printf("iTest = %d\n",iTest);
printf("歡迎關注公眾號:最后一個bug\n");
return 1;
}
程序運行的結果如下:
offsetof(STest , Member3) : 8
&stTest : 0X452469E0
&(stTest.Member3) : 0X452469E8
container of Member3 : 0X452469E0
iTest = 5
歡迎關注公眾號:最后一個bug
4、最后小結
今天的小知識就為大家分享到這來了,里面還有很多作者沒講得特別全面的小知識,大家看完文章以后記得都查閱一下不懂的知識,如果實在不理解也可以聯(lián)系作者進行交流,不過最近作者也是特別的忙,不然也不會這么久才更一篇文章,不過還是希望各位一直支持作者,作者也會盡最大的努力為大家?guī)韺嵱酶咝У那度胧街R。
最近文章推薦:
點【在看】是最大的支持
免責聲明:本文內(nèi)容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!