您好,登錄后才能下訂單哦!
這篇文件繼續講解 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 = ;
i ++ ;
printf( "i = %d\n" , i);
}
void func2 ( void )
{
j = ;
j ++ ;
printf( "j = %d\n" , j);
}
int main ( int argc, char * argv[])
{
int k = ;
for (k = ; k < 10 ; k ++ )
{
func1();
func2();
printf( "\n" );
}
return ;
}
大家運行上面代碼加深下對 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" , & N);
int i = M;
int j = N;
printf( "%p\n" , & N);
int x = M;
int y = N;
printf( "%p %p %p %p\n" , & i, & j, & x, & y);
printf( "%d %d %d %d\n" ,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 并不是遇到 '\0' 就結束,而是一定會拷貝完 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\n" ;
return strc;
}
main( void )
{
printf( "%d\n" ,add( 1 , 4 ));
printf( "%s\n" ,str);
return ;
}
看看上面這個函數的輸出,會有陷阱的
9 、柔性數組
講了很多關鍵字,現在討論一個酷的東西,以后也會經常用到,柔性數組是可以自由變化長度的數組,對開發上來說是非常有用的。
C99 中,結構體的最后一個元素允許是未知大小的數組,這就叫做柔性數組成員,但結構體的柔性數組成員前面必須至少一個其他成員。
#include <stdio.h>
typedef struct _SoftArray{
int len;
int array[];
}SoftArray;
int main ()
{
int len = 10 ;
printf( "The struct's size is %d\n" , 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 = ;
SoftArray * p = (SoftArray * )malloc( sizeof (SoftArray) +sizeof ( int ) * len);
p -> len = len;
for (i = ;i < p -> len;i ++ )
{
p -> array[i] = i + 1 ;
}
for (i = ;i < p -> len;i ++ )
{
printf( "%d\n" ,p -> array[i]);
}
free(p);
return ;
}
這代碼的作用是用柔性數組動態創建數組并輸出數組內容,這里我就直接解釋解釋這兩句代碼
SoftArray * p = (SoftArray * )malloc( sizeof (SoftArray) + sizeof ( int ) * 10 );
p -> len = 10 ;
第一句,主要是根據你要定義的數組長度和數據類型以及柔性數組本身的大小來開辟一塊內存空間給柔性數組,第二個是定義 len 的長度,便于確定循環打印輸出
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。