全局變量
即定義在所有函數之外(一般都定義在main函數之前,#include頭文件下邊)的變量,這個是與局部變量(本地變量)所不同的,它可以在任何函數中使用
下面舉個例子
例1:
# include <stdio.h>
int i;
int * p;
int main(void) {
printf("全局變量 i 的值爲%d\n", i);
printf("全局變量 p 的值爲%s\n", p);
return 0;
}
輸出結果爲:
沒有初始化的全局變量會被賦予0這個值,如果是指針,會被賦予NULL,這點通過上面程序的結果即可發現;對全局變量進行初始化時,最好直接用編譯時刻已知的值(即數字或常變量等),但最好只是用數字而不要使用其他方法
它的初始化發生在main函數之前
如果在函數內部有變量與全局變量名相同,則在該函數中,全局變量會被覆蓋
例2:
# include <stdio.h>
int i;
//int * p;
void f();
int main(void) {
i = 9;
printf("在%s函數中 i 的值爲%d\n", __func__, i);
// printf("全局變量 p 的值爲%s\n", p);
f();
return 0;
}
void f() {
int i = 3;
printf("在%s函數中 i 的值爲%d\n", __func__, i);
}
輸出結果爲:
由上結果可以證明
另外,通過上面的例子可以發現,在printf中放一個 %s ,而再輸出的後面,對應的加一個__func__,則會輸出該語句所在的函數的名字
上面的例子是在局部函數中定義相同變量名,覆蓋了全局變量,下面舉個全局變量的使用範圍例子
例3:
# include <stdio.h>
int i;
//int * p;
void k();
void f();
int main(void) {
i = 9;
printf("在%s函數中 i 的值爲%d\n", __func__, i);
// printf("全局變量 p 的值爲%s\n", p);
f();
printf("在%s函數中 i 的值爲%d\n", __func__, i);
return 0;
}
void f() {
// int i = 3;
printf("在%s函數中 i 的值爲%d\n", __func__, i);
k();
printf("在%s函數中 i 的值爲%d\n", __func__, i);
}
void k() {
i = 1000;
printf("在%s函數中 i 的值爲%d\n", __func__, i);
}
輸出結果爲:
靜態局部變量
就是在定義局部變量時在其類型前邊加上static修飾符
當函數離開時,靜態局部變量會繼續存在(不會被釋放內存)並保持其值
靜態局部變量的初始化只會在第一次進入這個函數時做,以後進入此函數時會保持上次離開的值
舉例說明
例4:
# include <stdio.h>
int i;
//int * p;
void f(int);
int main(void) {
i = 9;
printf("在%s函數中 i 的值爲%d\n", __func__, i);
// printf("全局變量 p 的值爲%s\n", p);
f(i);
i += 1;
printf("在%s函數中,改變後的 i 的值爲%d\n", __func__, i);
f(i);
return 0;
}
void f(int t) {
static int s = t;
printf("在%s函數中 s 的值爲%d\n", __func__, s);
}
輸出結果爲:
由上可以發現,第一次i的值爲9時,將其傳入f函數中,賦值給static int 變量s,輸出s的值也爲9,而當i的值改變爲10時,再次傳入f函數,將其值賦給s,但輸出的s的值仍爲9
下面再試一下,看靜態變量、全局變量和局部變量分配的內存的地址在哪兒
例5:
# include <stdio.h>
int i;
//int * p;
void f(int);
int main(void) {
i = 9;
printf("在%s函數中 i 的值爲%d\n", __func__, i);
// printf("全局變量 p 的值爲%s\n", p);
f(i);
i += 1;
printf("在%s函數中,改變後的 i 的值爲%d\n", __func__, i);
f(i);
return 0;
}
void f(int t) {
int j = 0;
static int s = t;
printf("全局變量i的地址爲: &i = %p\n", &i);
printf("局部靜態變量的s地址爲: &s = %p\n", &s);
printf("局部變量j的地址爲: &j = %p\n", &j);
printf("在%s函數中 s 的值爲%d\n", __func__, s);
}
輸出結果爲:
由上面的結果可以發現,局部靜態變量的地址有很大差距,而它反而和全局變量分配的內存的地址相近,說明靜態局部變量實際上是特殊的全局變量,而且它們位於相同的內存區域,static在這裏是局部作用域的意思,即其函數內部可訪問
宏
以#開頭的是編譯預處理指令,它不是C語言的語句,但它是不可或缺的
#define是定義了一個宏,下面給出格式:
#define 名字 值
這三者之間只有兩個空格,值的後邊沒有空格,不要加,不然很可能出錯
注意,結尾是沒有分號的,因爲它不是語句,名字必須是單詞或字母,值可以隨意(數字表達式皆可)
它是在編譯器進行編譯之前被處理的
下面舉個例子
例6:
# include <stdio.h>
# define N 5
int main(void) {
int a = 10;
printf("a*N=%d\n", a * N);
return 0;
}
輸出結果爲:
由上可知,N的值爲5,宏就是這樣,通過#define,讓N與5等價,即在下面的程序中凡是出現N的地方,可以將它替換成5,這樣做,是爲了在寫較大的程序時,使用一些數字,或許可能遺忘數字的含義,所以起一個名字替換該數字,而且,要是多次用到該數字,如果想改,需要在代碼中一個個的找到,然後修改,使用宏以後,只需在程序最前邊修改一次即可(注:宏定義一般是大寫的字母或單詞)
如果一個宏的值中有其它的宏,已久會被替換
如果一個宏的值超過一行,最後需要加\
宏後面出現的註釋不會被替換
宏不單單可以定義數字,也可以定義表達式,但定義表達式時需要注意一點,首先舉個例子
例7:
# include <stdio.h>
# define S "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
ssssssssssssssssssssss" //這是定義一個字符串,沒什麼用,只爲舉例 說明
# define M 3
# define N 2
# define Q M+N //這幾個宏的值沒什麼用,只爲舉例說明
int main(void) {
int a = 10;
printf("Q=%d\n", Q);
printf("Q*Q=%d\n", Q*Q);
printf("S=%s\n", S);
return 0;
}
輸出結果爲:
請看下面的輸出結果,Q*Q按說應該是5*5=25,但結果卻是11?這是因爲,它是這麼算的Q=M+N,則Q*Q=M+N*M+N,即3+2*3+2,結果爲11,要向達到預期效果,定義第三個宏時後面需要加括號
當然,這只是個簡單的例子,實際上可以進一步的延伸,這裏就不多說了
除了自己定義宏之外,還有一些預定義宏(不需要我們再定義了),如:__FILE__,__LINE__,__DATE__,__TIME__ (注意:它們的兩邊都是兩個下劃線 _ )
這些都是什麼意思呢?
舉個例子
例8:
# include <stdio.h>
int main(void) {
printf("__FILE__是:%s\n", __FILE__);
printf("__LINE__是:%d\n", __LINE__);
printf("__DATE__是:%s\n", __DATE__);
printf("__TIME__是:%s\n", __TIME__);
return 0;
}
輸出結果爲:
由結果可推測一下(其實第二個也未必能猜出),第一個是源程序文件的路徑
第二個是該行代碼所在的行號,第三個是輸出日期,第四個是輸出當前時間
其實,還可以定義帶參數的宏,這個用起來也有點兒像函數
帶參數的宏一定要加括號,整個值也要加括號,每個參數出現的地方都要加括號
例:9
# include <stdio.h>
# define max(a,b) (a>b?a:b) //後邊是表達式的話,最好帶着括號
int main(void) {
int i = 4, j = 7; //僅僅是舉例
printf("較大的數爲:%d\n", max(i,j));
return 0;
}
輸出結果爲:
這是個帶參數的宏,它的後邊是一個三目運算符,表示a和b兩個值中的較大的數,看printf中的max(i,j),是不是像個函數調用?其實這裏功能一樣
【所有代碼均在windows系統下dev c++下運行通過】
(如有錯誤,敬請指正)