C C 深度分析(一)

2022-08-17 18:21:17 字數 4862 閱讀 2701

table  page[10]

,int main()

value  addvalue;

addvalue.val_1 = 2;

addvalue.val_2 = 4;

int  result = 0;

result  = page[0]-> unisetfunc(&addvalue);

printf(「the result of add is %d\n」, result);

return 0;

此時陣列就轉換為類似於python語言中的字典的結構,便於後續的開發利用以及追加公升級和維護。

**分析:首先我們知道函式的名字可以做為函式的入口位址(類似於陣列的名代表陣列的位址一樣),所以在table結構體中我們定義了乙個成員函式int (*unisetfunc)(value*); 此處unisetfunc作為函式的入口引數,value*代表函式的形參型別;table型別的陣列 page[10]即包含了結構體的資料以及函式的入口位址,可以通過呼叫page[0]-> unisetfunc(&addvalue)來間接地呼叫add函式並實現addvalue中addvalue.val_1和addvalue.val_1兩個數求和的運算。

記憶體命中率問題:

為了可以提高**執行效率,要才充分的利用記憶體空間,應連續的訪問記憶體區域。

我們知道陣列在記憶體中存放的位置是一整塊的區域,並且是連續存放的,對於定義的陣列array[2][2]來說,假設array[0][0]的位址為0x04030,則array[0][1],array[1][0],array[1][1] 的位址分別為0x04031, 0x04032, 0x04033;提高記憶體命中率的即是應該盡可能的連續的訪問記憶體空間區域,而非跳躍式的訪問;接下來讓我們來看乙個矩陣相乘的問題。

for (int i = 0; i < 2; ++i)

for (int j = 0; j < 2; ++j)

for (int k = 0; k < 3; ++k)

matrix[i][j] += matrix1[i][k] * matrix2[k][j];

以上**是常用的將矩陣matrix1與matrix2相乘然後賦值給matrix的方法,即用matrix1矩陣得到行向量乘以矩陣matrix2的列向量,然後賦值給matrix,這樣由於矩陣在記憶體儲存的結構,我們可以清楚的知道訪問matrix2的時候並非採用連續的訪問方式,故記憶體的命中率較低。接下來我們看一種高記憶體命中率的方法。

for (int i = 0; i < 2; ++i)

for (int k = 0; k < 3; ++k)

for (int j = 0; j < 2; ++j)

matrix[i][j] += matrix1[i][k] * matrix2[k][j];

可以看出**僅僅將第二個for迴圈與第三個for迴圈交換了位置,而其他的部分沒有任何變化,然而記憶體的命中率卻大大的提高了,我們採用將matrix1與matrix2矩陣內部各原素依次相乘然後再累加的方式,來進行矩陣相乘的目的,這樣在訪問matrix1與matrix2矩陣時沒有發生任何記憶體未命中的問題,從而提高了記憶體命中的概率。

volatile,const以及static之間的關係:

const關鍵字為常量關鍵字,它作用的量為常量,不允許程式去改變該常量的值,如const int  value = 12;此常量value值不允許程式將其改變,在開發的過程const關鍵字會經常用到,為了防止程式意外的改變某一固定的常量,我們應及時的給其加上const關鍵字;另外const關鍵字作用於常量時必須直接給常量初始化,因為在整個程式執行大的過程中不允許對其改變,故必須立即初始化,例如:const  int  value = 12 是正確的,而const int value; value = 12;這樣的語法是錯誤的! 接下來我們來研究乙個稍微難一點的問題,即常量指標與指標常量。先看一段**:

#define switch 1

int main()

int val_1 = 5;

int val_2 = 10;

const int *p1 = &val_1;

int const *p2 = &val_1;

int *const p3 = &val_1;

#ifdef  switch          // this is a switch

*p1 = 20;

*p2 = 21;

*p3 = 22;

#endif

#ifndef  switch

p1 = &val_2;

p2 = &val_2;

p3 = &val_2;

#endif

printf("%d\n", *p1);

printf("%d\n", *p2);

printf("%d\n", *p3);

return 0;

在cygwin編譯器下執行,我們可以看到這樣的錯誤:

從圖中我們可以清楚的看到,指標p1與p2僅能讀取val_1中的值為指標常量,即不能改變它所指的變數的內容,所以*p1 = 20; *p2 = 21;兩條命令是錯誤的!(#ifdef switch … #endif 為條件編譯即為巨集開關)。然後我們將#define switch 1 語句給注釋掉,此時將執行第二塊**,得到結果如下:

int main()

int val_1 = 5;

int val_2 = 10;

const int *p1 = &val_1;

int const *p2 = &val_1;

int *const p3 = &val_1;

printf("frist\n");

printf("%d\n", *p1);

printf("%d\n", *p2);

printf("%d\n", *p3);

p1 = &val_2;

p2 = &val_2;

*p3 = 22;

printf("second\n");

printf("%d\n", *p1);

printf("%d\n", *p2);

printf("%d\n", *p3);

return 0;

執行的結果為:

最後終結:常量指標(const int *p或int const *p)表示指標p不能改變它所指向位址裡面所存的值,而可以改變它所指向的位址;指標常量(int *const p)表示指標p不能改變它所指向的位址,即指標不能改變它所指向的位置,但是可以改變它所指的位置中的內容。若想要指標既不能改變所指向的位置,又不能改變該處的內容,那麼可以這樣定義:

const int * const p = &a;或int const *const p = &a; 在定義函式的時候,若該入口引數在程式執行的過程中不希望被改變,則一定要將該形參用const來修飾,一來這樣可以防止該段程式將其改變,二來對於形參而言,乙個無論是否是const修飾的實參都可以將其傳入const形的形參,而乙個const形的實參是無法傳入非const形的形參中,所以為了使編譯不出錯在定義函式的時候,一定要將不希望被改變的量用const關鍵字來修飾。

static關鍵字為靜態關鍵字,它的作用是將作用的變數存入記憶體中,而非存入暫存器中(即將變數存入堆中而非棧中),並且該作用的變數僅儲存最近一次獲取的值。接下來我們來看一段**。

void  countfun ()

static  int  count = 0;

++count;

printf(「this is %d number, enter into this function !\n」, count );

int main()

for (int i = 0; i < 5; ++i)

countfun();

return 0;

這段**的執行結果如下:

而若將除去static關鍵字,則執行的結果如下:

由此我們可以清楚的看出,static作用的變數count只會存入當前的結果,因此迴圈呼叫countfun( )函式的時候並沒有從新將count變數置為0,而是儲存了前一次的值。

static關鍵字在專案中的應用是很廣泛的,它不僅僅有上述所介紹的特點,同時若想要定義的全域性變數在整個工程中僅在當前.c檔案中有效時,也應該將這個全域性變數用static來修飾,這樣在其他的檔案中是無法訪問這個變數,從而降低了模組間的耦合度,提高了模組的內聚性,防止其他檔案將其改變,從而更加的安全。

volatile關鍵字在嵌入式領域中是十分重要的乙個關鍵字,尤其是在與硬體相關或多執行緒的程式設計中更為重要。volatile關鍵字修飾的變數說明它是可以隨時發生改變的,我們不希望編譯器去優化某些**的時候,需要將這個變數用volatile關鍵字來修飾,從而程式每次訪問該變數的時候是直接從記憶體中提取出來,而不是從臨時的暫存器中將該變數的副本給提取出來利用!例如當我們想要實現某個中斷處理時,其用來做判斷條件的標記位則應該用volatile來修飾,這樣當這個中斷在別的地方被觸發的時候就可以被實時的檢測到,不至於由於優化而忽略中斷。接下來我們看一段**:

int main()

int b = i;

printf(「i = %d\n」, b);

return 0;

}此程式輸出結果為i = 10;i = 16; 若將volatile關鍵字去掉,則結果為i = 10;i = 10;即不加關鍵字會將彙編**忽略掉,所以為了防止**優化以及可以及時檢測到外部程式對該變數的改變,我們必須將該變數加上volatile關鍵字。我們知道volatile關鍵字表徵該量是易變的,const關鍵字代表該量是常量不能改變,那麼volatile與const是否可以一起修飾同乙個量呢,答案是肯定的,例如在硬體程式設計中rom所儲存的資料是不允許使用者改變的,即指向該資料的指標必須為常量指標(const int *p = &ram_data),然而開發商卻可以將其意外的改變,為了防止rom的內容被意外的改變時,而使用者程式沒有及時的發現,必須將該量用volatile修飾,所以應這樣定義該指標(volatile const int *p = &rom_data)。

C C 深度分析(二)

第二章 位運算 在數字解碼與編碼的過程中,位運算的操作是司空見慣的事,同時位運算在提高程式的效能方面也獨占鰲頭,因此位運算操作是必需要深入了解的問題。在乘法以及除法的操作中我可以使用未執行來提高 的質量,例如 a a 16 這種操作完全可以替換為 a a 4 我們知道左移一位相當於將原數乘以2,左移...

深度學習筆記(16) 誤差分析(一)

假設正在除錯貓分類器 然後在開發集取得了90 準確率,相當於10 錯誤 這離希望的目標還有很遠 也許需要看了一下演算法分類出錯的例子 注意到演算法將一些狗分類為貓 試想一下,可以針對狗,收集更多的狗圖 或者設計一些只處理狗的演算法功能之類的 為了讓的貓分類器在狗圖上做的更好,讓演算法不再將狗分類成貓...

戰略分析實戰 如何深度分析一家企業

文章同步公 眾 號 資料新商業 專業有深度的實戰文章 帶你晉公升高階崗位 作者介紹 數分團隊leader 專業書籍 資料實踐之美 作者之一 今天文章的主題是如何深度分析一家企業或者叫戰略分析,這並不是侷限在一般資料分析層面的東西,職級從底層人員到中高層管理,我想都需要對自己的企業有個比較巨集觀且全面...