西郵Linux面試題總結

static關鍵字

  • static修飾局部變量

生命週期:靜態變量在程序運行之前創建,在程序的整個運行期間始終存在,直到程序結束。

注意: 靜態變量,只改變了它的存儲類型(即生命週期),並沒有改變它的作用域,變量b還是只能在test函數內部使用。
靜態本地變量, 具有全局的生存期, 只初始化一次, 離開函數後仍然存在, 具有函數內的局部作用域.
靜態本地變量實際上是特殊的全局變量, 都位於相同的內存區域, 即使在聲明時未賦初值, 也會默認初始化爲0

  • static修飾全局變量

相當於私有的全局變量, 作用域只限於該源文件.
默認情況下, 一個全局變量是可以供多個源文件共享的, 也就是說多個源文件中同名的全局變量都代表着同一個變量.

static與函數

  • 外部函數 : 如果在當前文件中定義的函數允許其他文件訪問, 調用, 就成爲外部函數, C語言規定, 不允許有同名的外部函數.
  • 內部函數 : 如果在當前文件中定義的函數不允許其他文件訪問, 調用, 只能在內部使用, 就稱爲內部函數. C語言規定不同的源文件可以有同名的內部函數, 並且互不干擾.
    所以如果要定義外部函數完整的定義是要加上extern關鍵字. 不過因爲默認情況下, 所有的函數就是外部函數, 所以一般簡化.
    static修飾函數
    1.定義內部函數
static void test() {
	printf("dsf");
}

2.聲明內部函數

 1 #include <stdio.h>
 2 
 3 static void test();
 4 
 5 int main(int argc, const char * argv[])
 6 {
 7     test();
 8     return 0;
 9 }
10 
11 static void test() {
12     printf("調用了test函數");
13 }

在第11行定義了一個test函數,這是一個內部函數,接着在第3行對test函數進行提前聲明,然後就可以在第7行可以調用test()函數了
總結 :

  • static在定義函數時, 在函數的最左邊加上static可以把該函數聲明爲內部函數, 這樣該函數就只能在其定義所在的文件中使用. 如果在不同的文件中有同名的內部函數, 則互不干擾.
  • static也可以用來聲明一個內部函數.

1>宏與typedef的區別:
(1)宏只是簡單的替換, 而typedef可以看成是徹底的"封裝"
例:#define X int*
X a, b;
只有a是指針
typedef int* X
X a, b;
a, b都是指針
(2)可以用其他類型說明符對宏定義的類型進行拓展, 而typedef不可以
例:#define X int
unsigned X a; 可以

typedef X int; unsigned X a;不可以
2>#用來將宏參數轉換爲字符串, 也就是在宏參數的開頭和末尾添加引號, 例如:

#define A(x) #x

printf("%s", A(hello, world));
// 將會被展開爲
printf("%s", "hello, world");

3>##稱爲連接符, 用來將宏參數和其他的串連接起來. 例如:

#define A(a, b) a##e##b
#define B(a, b) a##b##00

printf("%f\n", A(1, 2));
printf("%d\n", B(11, 22));
// 將會被展開爲
printf("%f\n", 1e2);
printf("%d\n", 112200);

4>如果一個宏的值中有其他宏的名字, 而宏定義中無#或##, 則會先進行宏參數的展開, 再展開當前宏.例如:

#define YEAR 2018
#define LEVELONE(x) "XiyouLinux "#x"\n" 
#define LEVELTWO(x) LEVELONE(x)
#define MULTIPLY(x,y) x*y
int main(int argc, char *argv[])
{
int x = MULTIPLY(1 + 2, 3);
printf("%d\n", x);
printf(LEVELONE(YEAR));
printf(LEVELTWO(YEAR));
}

%zu 輸出 size_t型, size_t 在庫中被typedef爲unsigned int 無符號整型

浮點數的存儲

十進制8.25的二進制是1000.01
->1.000012^3(3指數, 00001尾數)
浮點數中1.x
2^y也就是說1是固定的,2也是不變的,所以在內存中不用存儲,在內存中只存有x和y也就是尾數和指數,當然還有符號位.
在這裏插入圖片描述

浮點數在內存中無法精確存儲, float的有效位數只有7位(包括整數部分和小數部分), 超過7位之後均爲無效數字, double的有效位數有16位, 超過16位之後均爲無效數字.
判斷浮點數是否相同:由於精度問題, 不可將浮點變量用 == 或 != 與數字比較, 應該設法轉化爲>= 或 <= 之類的形式
例:要判斷float類型的變量i與0的比較
if ((x >= -0.00001) && (x <= 0.00001)) {
printf(“相等\n”);
}
也就是
if (fabs(i-0) < 1e-6) {
printf(“相等\n”);
}

printf, scanf的返回值

printf返回輸出的字符的個數
scanf返回正確讀取到的字符的個數
如果輸入數據與指定格式不符,則會產生輸入錯誤。遇到輸入錯誤,scanf函數會立即終止,返回已經成功讀取的數據的個數。
所以,通過scanf函數的返回值和指定輸入數據的個數(由格式符決定)的比較,可以判斷數據輸入是否成功。

大小端

個人pc一般都是小端序, 數據在內存中存放時低字節對應低地址, 高字節對應高地址.
讀取時從高地址往低地址讀取.
當定義一個變量時,系統就會爲這個變量分配一定的存儲空間。

1 int main()
2 {
3     char a = 'A'; 
4     
5     int b = 10;
6     
7     return 0;
8 }

1> 在64bit環境下,系統爲變量a、b分別分配1個字節、4個字節的存儲單元。也就是說:

  • 變量b中的10是用4個字節來存儲的,4個字節共32位,因此變量b在內存中的存儲形式應該是0000 0000 0000 0000 0000 0000 0000 1010。

  • 變量a中的’A’是用1個字節來存儲的,1個字節共8位,變量a在內存中的存儲形式是0100 0001
    2> 上述變量a、b在內存中的存儲情況大致如下表所示:
    在這裏插入圖片描述

  • 從圖中可以看出,變量b佔用了內存地址從ffc1~ffc4的4個字節,變量a佔用了內存地址爲ffc5的1個字節。每個字節都有自己的地址,其實變量也有地址。變量存儲單元的第一個字節的地址就是該變量的地址。變量a的地址是ffc5,變量b的地址是ffc1。

  • 內存尋址是從大到小的, 也就是說做什麼事都會先從內存地址較大的字節開始,因此係統會優先分配地址值較大的字節給變量。由於是先定義變量a、後定義變量b,因此你會看到變量a的地址ffc5比變量b的地址ffc1大。

  • 注意看表格中變量b存儲的內容,變量b的二進制形式是:0000 0000 0000 0000 0000 0000 0000 1010。由於內存尋址是從大到小的,所以是從內存地址最大的字節開始存儲數據,存放順序是ffc4 -> ffc3 -> ffc2 -> ffc1,所以把前面的0000 0000都放在ffc2~ffc4中,最後面的八位0000 1010放在ffc1中。

gcc 的編譯過程

.c ->.i -> .s -> .o -> a.out

  • 預處理:刪除註釋, 宏展開和宏替換, 處理條件編譯, 文件包含等, 生成.i文件
  • 編譯:生成彙編文件.s, 生成相應的彙編代碼, 會檢查語法等
  • 彙編:彙編代碼對應生成機器碼, 叫做目標文件.o
  • 鏈接:所有目標文件和庫在一起鏈接, 在這個過程可以找到函數的定義從而修正假的函數地址, 得到可執行文件.

負數的二進制形式

1 int main()
2 {
3     int b = -10;
4     return 0;
5 } 

在第3行定義了一個整型變量,它的值是-10。-10在內存中怎樣存儲的呢?其實任何數值在內存中都是以補碼的形式存儲的

  • 正數的補碼與原碼相同。比如9的原碼和補碼都是1001
  • 負數的補碼等於它正數的原碼取反後再+1。

那麼-10的補碼計算過程如下:
1> 先算出10的二進制形式:0000 0000 0000 0000 0000 0000 0000 1010

2> 對10的二進制進行取反:1111 1111 1111 1111 1111 1111 1111 0101

3> 對取反後的結果+1:1111 1111 1111 1111 1111 1111 1111 0110

因此,整數-10在內存中的二進制形式是:1111 1111 1111 1111 1111 1111 1111 0110

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章