C語言關鍵字(三)

之前的兩篇文章

嵌入式Linux:c語言深度解剖(數據類型關鍵字)?

zhuanlan.zhihu.com圖標嵌入式Linux:c語言深度解剖(入門篇)?

zhuanlan.zhihu.com圖標

這篇文件繼續講解C語言關鍵字

想問大家一個問題,什麼是聲明什麼是定義?

舉個例子:

A)int i;

B)extern int i;(關於 extern,後面解釋)

哪個是定義?哪個是聲明?或者都是定義或者都是聲明?

什麼是定義:所謂的定義就是(編譯器)創建一個對象,為這個對象分配一塊內存並給它取上一個名字,這個名字就是我們經常所說的變數名或對象名。但注意,這個名字一旦和這塊內存匹配起來,它們就同生共死,終生不離不棄。並且這塊內存的位置也不能被改變。一個變數或對象在一定的區域內(比如函數內,全局等)只能被定義一次,如果定義多次,編譯器會提示你重複定義同一個變數或對象。

什麼是聲明:有兩重含義,如下:

第一重含義:告訴編譯器,這個名字已經匹配到一塊內存上了,(就比如你拿出結婚證告訴別人,我已經和別的內存塊結婚了,請不要再勾搭我了)。

第二重含義:告訴編譯器,我這個名字我先預定了,別的地方再也不能用它來作為變數名或對象名。(這個就有點耍賴,你拿著一個假的結婚證告訴別人,我已經結婚了,但是實際上,你還沒有妹子,還沒有分配內存空間)。

好,這樣一解釋,我們可以很清楚的判斷:A)是定義;B)是聲明。

那他們的區別也很清晰了。記住,定義聲明最重要的區別:定義創建了對象並為這個對象一塊內存,而聲明的時候是沒有分配內存空間的。

1,不需要記住的關鍵字----auto

auto:他太普通了,你就當它不存在吧。編譯器在默認的預設情況下,所有變數

都是 auto 的。

2,最快的關鍵字---- register

register:這個關鍵字請求編譯器儘可能的將變數存在 CPU 內部寄存器中而不是通過內

存定址訪問以提高效率。注意是儘可能,不是絕對。你想想,一個 CPU 的寄存器也就那麼

幾個或幾十個,你要是定義了很多很多 register 變數,它累死也可能不能全部把這些變數放

入寄存器吧,輪也可能輪不到你。

2.1,皇帝(CPU)身邊的小太監----寄存器

把cpu類比成為一個皇帝,那register就是皇帝身邊的小太監了,不知道大家見過太監沒有,我們看各種宮斗劇的時候,太監是唯命是從,只要皇帝叫做什麼,太監馬上就去做,速度之快令人瞠目結舌,也就是因為速度快,所以皇帝才配有太監,而且不止有一個太監,太監就像一個文件中轉站,把下面人的摺子拿給皇帝批閱。

所以太監的特點是

1、響應速度快

2、數量少,只給皇帝工作

3、價格貴

2.2,使用 register 修飾符的注意點

雖然寄存器的速度非常快,但是使用 register 修飾符也有些限制的:register 變數必須是能被 CPU 寄存器所接受的類型。意味著 register 變數必須是一個單個的值,並且其長度應小於或等於整型的長度。 而且 register 變數可能不存放在內存中,所以不能用取址運算符「&」來獲取 register 變數的地址。

3、確定位置的關鍵字----static

3.1、static 修飾變數

修飾靜態全局變數:作用域僅限制於被定義的文件中,其他文件即使用extern聲明也沒有辦法使用,作用域從定義之處開始,到文件結尾處,在定義之前的代碼不能使用。本文件可以在之前加extern ,不過這樣還不如直接在頂端定義。

靜態全局變數:在函數體裡面定義的,就只能在函數裡面使用了,由於static定義的變數存在靜態區,改函數執行結束,變數的值還是存在,不會銷毀,下次該函數調用時,static定義的變數值是上一次調用時候的值。

3.2、static修飾函數

在函數前面加static表示函數成為靜態函數,表示該函數的作用域僅限於定義處到文件結尾。如果全局函數有一個函數名字和靜態函數一樣的名字,編譯器不會報錯,使用本文件的靜態函數運行。

#include <stdio.h>

static int j;

void func1(void)
{
static int i = 0;
i++;
printf("i = %d
", i);
}

void func2(void)
{
j = 0;
j++;
printf("j = %d
", j);
}

int main(int argc, char *argv[])
{
int k = 0;
for(k = 0; k<10; k++)
{
func1();
func2();
printf("
");
}
return 0;
}

大家運行上面代碼加深下對static的理解

4、大喇叭關鍵字----extern

上面有一個例子已經說到了extern,extern就像一個大喇叭一樣,他不分配內存,就是不討老婆,但是總是跟別人說,誰誰娶媳婦了,這個標識符就定義了,這個函數被定義了,如此如此,不過這個大喇叭非常有用,比如他可以跟編譯器說,這個傢伙已經討老婆了,你可以用他生孩子了,就不要再讓他二婚了。

既然extern不能給別人髮結婚證,那麼下面這個

extern int i = 10;

是否有問題?

不能髮結婚證,就是不能分配內儲存,沒有內存怎麼把10存下來?所以肯定是錯的。

5、讓cpu最累的關鍵字----volitile

volitile這個關鍵字讓cpu非常累,每次運行到他的位置,都要去內存重新確定他的值,在中斷的使用的變數最好用這個關鍵字修飾,因為中斷的變數你不知道什麼時候會被改變,volatile的變數是說這變數可能會被意想不到地改變,這樣,編譯器就不會去假設這個變數的值了。說白了就是不能讓cpu偷懶,所以說是類似老大CPU了。

6、只能讀的關鍵字----const

const可以理解為別人只能來讀我的值,不能改變我,有的人寫代碼加上這個就是擔心自己的值被改變,比如const int i =5;下一次,i =6;這就是有問題的,i 的值已經確定了,就像define一樣(不過define不是關鍵字)。

/*************************************************************************
> File Name: const.c
> Author:
> Mail:
> Created Time: Mon 29 Oct 2018 02:33:19 PM CST
************************************************************************/

#include<stdio.h>
#define M 3
const int N = 5;

void main(void)
{
printf("%p
",&N);
int i = M;
int j = N;
printf("%p
",&N);
int x = M;
int y = N;
printf("%p %p %p %p
",&i,&j,&x,&y);
printf("%d %d %d %d
",i,j,x,y);
}

7、不色不空的關鍵字----void

大家可以看看void 和void* ,誰是色,誰是空呢?void 表示這個是空的,什麼都沒有,比如void i; 我們定義一個空的i,這就有問題了,這個i編譯器到底要不要給他分配空間呢?就是這樣的情況,編譯器報錯了,你搞什麼飛機給我一個空的東西還想讓我給你討老婆。

但是void 不一樣,void *表示所有類型的指針,這就是色啊,女人都想入非非。

說明:既然提供了void的這兩種用法,就去運用。即函數沒返回值就將其返回值類型寫為void,函數沒有形參就將其形參寫為void。不了解編譯器默認操作時,不要依賴。即使了解其默認操作,也別依賴,因為肯定有人不了解的,這樣別人就看不懂你的代碼了。

大家看看這個是否正確

add(int a,int b)
{
return (a+b);
}

下面兩個函數就是用void*作為返回值

memcpy  

原型:extern void *memcpy(void *dest, void *src, unsigned int count);   

用法:#include   

功能:由src所指內存區域複製count個位元組到dest所指內存區域。   

說明:src和dest所指內存區域不能重疊,函數返回指向dest的指針。   

注意:與strcpy相比,memcpy並不是遇到就結束,而是一定會拷貝完n個位元組。

memset

原型:extern void *memset(void *buffer, int c, int count);

用法:#include

功能:把buffer所指內存區域的前count個位元組設置成字元c。

說明:返回指向buffer的指針。

8、return 關鍵字

return 關鍵字終止一個函數並返回一個值。

#include "stdio.h"

add(int a,int b)
{
return (a+b);
}

char * str(void)
{
char *strc = "This is c
";
return strc;
}

main(void)
{
printf("%d
",add(1,4));
printf("%s
",str);
return ;
}

看看上面這個函數的輸出,會有陷阱的

9、柔性數組

講了很多關鍵字,現在討論一個酷的東西,以後也會經常用到,柔性數組是可以自由變化長度的數組,對開發上來說是非常有用的。

C99中,結構體的最後一個元素允許是未知大小的數組,這就叫做柔性數組成員,但結構體的柔性數組成員前面必須至少一個其他成員。

#include<stdio.h>
typedef struct _SoftArray{
int len;
int array[];
}SoftArray;

int main()
{
int len = 10;

printf("The structs size is %d
",sizeof(SoftArray));
}

我們可以看出,_SoftArray結構體的大小是4,顯然,在32位操作系統下一個int型變數大小剛好為4,也就說結構體中的數組沒有佔用內存。為什麼會沒有佔用內存,我們平時用數組時不時都要明確指明數組大小的嗎?但這裡卻可以編譯通過呢?這就是我們常說的動態數組,也就是柔性數組。

#include<stdio.h>
#include<malloc.h>
typedef struct _SoftArray{
int len;
int array[];
}SoftArray;
int main()
{
int len=10,i=0;

SoftArray *p=(SoftArray*)malloc(sizeof(SoftArray)+sizeof(int)*len);
p->len=len;

for(i=0;i<p->len;i++)
{
p->array[i]=i+1;
}
for(i=0;i<p->len;i++)
{
printf("%d
",p->array[i]);
}

free(p);

return 0;
}

這代碼的作用是用柔性數組動態創建數組並輸出數組內容,這裡我就直接解釋解釋這兩句代碼

   SoftArray* p = (SoftArray*)malloc(sizeof(SoftArray) + sizeof(int) *10);
   p->len = 10;

第一句,主要是根據你要定義的數組長度和數據類型以及柔性數組本身的大小來開闢一塊內存空間給柔性數組,第二個是定義len的長度,便於確定循環列印輸出

覺得對你有用,請關注微信公眾號【嵌入式Linux】

推薦閱讀:

TAG:Linux | C/C | C(編程語言) |