1、局部變量和靜態(tài)變量的區(qū)別
-
普通局部變量和靜態(tài)局部變量區(qū)別
-
-
存儲位置:
-
-
-
-
普通局部變量存儲在棧上
-
靜態(tài)局部變量存儲在靜態(tài)存儲區(qū)
-
-
-
-
生命周期:
-
-
-
-
當(dāng)函數(shù)執(zhí)行完畢時,普通局部變量會被銷毀
-
靜態(tài)局部變量的生命周期則是整個程序運行期間,即使函數(shù)調(diào)用結(jié)束,靜態(tài)局部變量的值也會被保留
-
-
-
-
初始值:
-
-
-
-
普通局部變量在每次函數(shù)調(diào)用時都會被初始化,它們的初始值是不確定的,除非顯式地進(jìn)行初始化
-
靜態(tài)局部變量在第一次函數(shù)調(diào)用時會被初始化,然后保持其值不變,直到程序結(jié)束
-
-
-
#include <stdio.h> ? void normal_func() { ? ?int i = 0; ? ?i++; ? ?printf("局部變量 i = %d\n", i); } ? void static_func() { ? ?static int j = 0; ? ?j++; ? ?printf("static局部變量 j = %d\n", j); } ? int main() { ? ?// 調(diào)用3次normal_func() ? ?normal_func(); ? ?normal_func(); ? ?normal_func(); ? ? ?// 調(diào)用3次static_func() ? ?static_func(); ? ?static_func(); ? ?static_func(); ? ? ?return 0; }
-
運行結(jié)果:
-
局部變量 i = 1 局部變量 i = 1 局部變量 i = 1 static局部變量 j = 1 static局部變量 j = 2 static局部變量 j = 3
2、預(yù)處理
-
C語言對源程序處理的四個步驟:預(yù)處理、編譯、匯編、鏈接。
-
預(yù)處理
-
-
宏定義展開、頭文件展開、條件編譯,這里并不會檢查語法
-
-
編譯
-
-
檢查語法,將預(yù)處理后文件編譯生成匯編文件
-
-
匯編
-
-
將匯編文件生成目標(biāo)文件(二進(jìn)制文件)
-
-
鏈接
-
-
將目標(biāo)文件鏈接為可執(zhí)行程序
-
gcc -E hello.c -o hello.i //處理文件包含,宏和注釋 gcc -S hello.i -o hello.s //編譯為匯編文件 gcc -c hello.s -o hello.o //經(jīng)匯編后為二進(jìn)制的機器指令 gcc hello.o -o hello ? ? //鏈接所用的到庫 ? 1 預(yù)處理:預(yù)處理相當(dāng)于根據(jù)預(yù)處理命令組裝成新的 C 程序,不過常以 i 為擴(kuò)展 名。 2 編 譯:將得到的 i 文件翻譯成匯編代碼 .s 文件。 3 匯 編:將匯編文件翻譯成機器指令,并打包成可重定位目標(biāo)程序的 O 文件。 該文件是二進(jìn)制文件,字節(jié)編碼是機器指令。 4 鏈 接:將引用的其他 O 文件并入到我們程序所在的 o 文件中,處理得到最終 的可執(zhí)行文件
-
-
C編譯器提供的預(yù)處理功能主要包括:
-
文件包含 #include
-
宏定義 #define
-
條件編譯 #if #endif ……
-
3、文件包含處理
-
文件包含處理
-
指一個源文件可以將另外一個文件的全部內(nèi)容包含進(jìn)來
-
C語言提供了#include命令用來實現(xiàn)文件包含的操作
-
-
#include< > 與 #include ""的區(qū)別
-
<> 表示系統(tǒng)直接按系統(tǒng)指定的目錄檢索
-
"" 表示系統(tǒng)先在 "" 指定的路徑(沒寫路徑代表當(dāng)前路徑)查找頭文件,如果找不到,再按系統(tǒng)指定的目錄檢索
-
4、宏定義
-
在預(yù)編譯時將宏名替換成字符串的過程稱為"宏展開"(也叫宏替換)。
-
宏名一般用大寫,以便于與變量區(qū)別
-
宏定義不作語法檢查,只有在編譯被宏展開后的源程序才會報錯
-
宏定不要不要行末加分號
-
#define PI 3.14 #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define FUNC(a) func(a) ? void func(int a) { ? ?int b = a; } ? int main() { ? ?double a = PI; ? ?int temp = MAX(1, 2+3); ? ?FUNC(10); ? ? ?return 0; }
5、條件編譯
一般情況下,源程序中所有的行都參加編譯。但有時希望對部分源程序行只在滿足一定條件時才編譯,即對這部分源程序行指定編譯條件。
防止頭文件被重復(fù)包含
#ifndef _SOMEFILE_H #define _SOMEFILE_H ? //需要聲明的變量、函數(shù) //宏定義 //結(jié)構(gòu)體 ? #endif
軟件裁剪
同樣的C源代碼,條件選項不同可以編譯出不同的可執(zhí)行程序:
#include <stdio.h> ? // #define A 有注釋,沒有注釋,觀察運行結(jié)果 #define A ? int main() { #ifdef A ? ?printf("這是大寫操作\n"); #else ? ?printf("這是小寫操作\n"); #endif ? ? ?return 0; }
6、遞歸
-
函數(shù)遞歸調(diào)用:
-
函數(shù)可以調(diào)用函數(shù)本身(不要用main()調(diào)用main(),不是不能這么做,而是不建議,往往得不到你想要的結(jié)果)。
-
-
遞歸的優(yōu)點
-
遞歸給某些編程問題提供了最簡單的方法。
-
-
遞歸的缺點
-
一個有缺陷的遞歸會很快耗盡計算機的資源,遞歸的程序難以理解和維護(hù)
-
?
7、普通函數(shù)調(diào)用
#include <stdio.h> ? void fun_b(int b) { printf("b = %d\n", b); ? return; } ? void func_a(int a) { fun_b(a - 1); ? printf("a = %d\n", a); } ? int main(void) { func_a(2); ? ?printf("main\n"); ? return 0; }
運行順序:
-
結(jié)論:
-
先調(diào)用,后返回(棧結(jié)構(gòu))
-
調(diào)用誰,返回誰的位置
-
運行結(jié)果:
b = 1 a = 2 main
8、函數(shù)遞歸調(diào)用
?
#include <stdio.h> ? //0的階乘是1 1的階乘1 ? return 1 //n! =(n-1)!*n //(n-1)! = (n-2)!*(n-1) //n = 1 ? ? // 遞歸函數(shù)計算階乘 int factorial(int n) { ? ?if (n == 0 || n == 1) { ? ? ? ?return 1; ? } else { ? ? ? ?return n * factorial(n - 1); ? } } ? int main() { ? ?int n; ? ?printf("請輸入一個整數(shù):"); ? ?scanf("%d", &n); ? ? ?// 調(diào)用遞歸函數(shù)計算階乘并輸出結(jié)果 ? ?int result = factorial(n); ? ?printf("%d 的階乘是 %d\n", n, result); ? ? ?return 0; } ?
運行順序:
9、大小端驗證
所謂的大端模式,是指數(shù)據(jù)的低位(就是權(quán)值較小的后面那幾位)保存在內(nèi)存的高地址中,而數(shù)據(jù)的高位,保存在內(nèi)存的低地址中,這樣的存儲模式有點兒類似于把數(shù)據(jù)當(dāng)作字符串順序處理:地址由小向大增加,而數(shù)據(jù)從高位往低位放; 所謂的小端模式,是指數(shù)據(jù)的低位保存在內(nèi)存的低地址中,而數(shù) 據(jù)的高位保存在內(nèi)存的高地址中,這種存儲模式將地址的高低和數(shù)據(jù)位權(quán)有效地結(jié)合起來,高地址部分權(quán)值高,低地址部分權(quán)值低,和我們的邏輯方法一致。 ? 1)大端模式: ? 低地址 -----------------> 高地址 ? 0x12 | 0x34 | 0x56 | 0x78 ? 2)小端模式: ? 低地址 ------------------> 高地址 ? 0x78 | 0x56 | 0x34 | 0x12
#include <stdio.h> #include <stdint.h> ? int check_endianness() { ? ?uint32_t temp = 0x44332211; // 4個字節(jié),32位 ? ?uint8_t * p = NULL; ?// 8位 ? ? ?p = (uint8_t *)&temp; ?// 只取uint8_t的長度 ? ?printf("%#x\n", *p); ? ?printf("%#x\n", p[0]); // *p 和 p[0]等價 ? ? ?uint16_t * p1 = (uint16_t *)&temp; ? ?printf("*p1 = %#x\n", *p1); ? ? ?if (*p == 0x11 ) { ? ? ? ?return 0; // 0是小端 ? } else { ? ? ? ?return 1; // 大端 ? } } ? int main() { ? ?int res = check_endianness(); ? ?if (res == 0) { ? ? ? ?printf("小端\n"); ? } else { ? ? ? ?printf("大端\n"); ? } ? ? ?return 0; }
10、大小端轉(zhuǎn)換
#include <stdio.h> ? int changeBigEndian(int data) { ? ? ?return (data >> 24 & 0x000000ff) | ? ? ? (data >> 8 & 0x0000ff00) | ? ? ? (data << 8 & 0x00ff0000) | ? ? ? (data << 24 & 0xff000000); } ? int main() { ? ? ? ?int mem = 0x44332211; ? ? ?printf("%0x\n", changeBigEndian(mem)); ? ?return 0; } ?
11、二分查找
#include <stdio.h> ? // 二分查找函數(shù) int binarySearch(int arr[], int size, int target) { ? ?int left = 0; ? ?int right = size - 1; ? ?while (left <= right) { ? ? ? ?int mid = left + (right - left) / 2; ? ? ? ?if (arr[mid] == target) { ? ? ? ? ? ?return mid; // 找到目標(biāo)元素,返回索引 ? ? ? } else if (arr[mid] < target) { ? ? ? ? ? ?left = mid + 1; // 在右半部分繼續(xù)查找 ? ? ? } else { ? ? ? ? ? ?right = mid - 1; // 在左半部分繼續(xù)查找 ? ? ? } ? } ? ?return -1; // 目標(biāo)元素不存在,返回-1 } ? int main() { ? ?int arr[] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19}; ? ?int size = sizeof(arr) / sizeof(arr[0]); ? ?int target = 11; ? ?int index = binarySearch(arr, size, target); ? ?if (index != -1) { ? ? ? ?printf("目標(biāo)元素 %d 在數(shù)組中的索引為 %d\n", target, index); ? } else { ? ? ? ?printf("目標(biāo)元素 %d 不在數(shù)組中\(zhòng)n", target); ? } ? ?return 0; } ?
12、什么是指針,在什么地方使用的
指針(Pointer)是一種特殊的變量類型,它用于存儲內(nèi)存地址。指針的實質(zhì)就是內(nèi)存“地址”。 使用范圍: 動態(tài)內(nèi)存分配:指針常用于動態(tài)分配內(nèi)存,例如使用 malloc()、calloc() 或 new 分配內(nèi)存,并使用指針來管理和訪問分配的內(nèi)存塊。 ? 數(shù)組和字符串:數(shù)組名本身就是指向數(shù)組第一個元素的指針,在函數(shù)參數(shù)傳遞、數(shù)組訪問等場景中經(jīng)常用到指針。 ? 函數(shù)指針:函數(shù)指針是指向函數(shù)的指針變量,可以用來在運行時動態(tài)確定調(diào)用的函數(shù),或者將函數(shù)作為參數(shù)傳遞給其他函數(shù)。 ……
13、函數(shù)指針是什么
函數(shù)指針是指向函數(shù)的指針變量,它存儲了函數(shù)的地址,可以用來調(diào)用該函數(shù)。在 C 語言中,函數(shù)名可以視為函數(shù)在內(nèi)存中的地址,因此可以將函數(shù)名賦值給函數(shù)指針變量,從而實現(xiàn)通過函數(shù)指針來調(diào)用函數(shù)。
#include <stdio.h> ? int getData(int a, int b) { ? ?return a + b; } ? int main() { ? ? ?int(*func)(int, int); ? ? ?func = getData; ? ?printf("%d\n", func(5, 8)); ? ? ?return 0; } ?
復(fù)議:指針函數(shù)
-
指針函數(shù)是一個返回指針的函數(shù)。它的返回值是一個指針,指向某種數(shù)據(jù)類型的內(nèi)存地址。
-
指針函數(shù)通常用于動態(tài)內(nèi)存分配、返回數(shù)組、返回字符串等場景。
int* create_array(int size) { ? ?int* arr = malloc(size * sizeof(int)); // 動態(tài)分配內(nèi)存 ? ?return arr; }
14、聲明和定義的區(qū)別
-
聲明告訴編譯器,某個名稱(如變量、函數(shù)、類等)存在,但不分配內(nèi)存空間或提供實現(xiàn)細(xì)節(jié)。
-
聲明通常包括名稱和類型信息,以及可能的參數(shù)列表。
-
聲明可以出現(xiàn)在函數(shù)或變量的定義之前,以便在使用之前提供有關(guān)名稱的信息。
int add(int a, int b);
-
定義不僅聲明了名稱的存在,還為其分配了內(nèi)存空間或提供了實現(xiàn)細(xì)節(jié)。
-
對于變量,定義會分配內(nèi)存空間;對于函數(shù),定義會提供函數(shù)體的實現(xiàn)。
-
每個定義都是一個聲明,但不是每個聲明都是一個定義。
// 函數(shù)定義 int add(int a, int b) { ? ?return a + b; }
15、extern關(guān)鍵字是干什么用
用來修飾全局變量,全局變量本身是全局可用的,但是由于文件是單個完成編譯,并且編譯是自上而下的,所以說,對于不是在本范圍內(nèi)定義的全局變量,要想使用必須用 extern 進(jìn)行聲明,如果不加上 extern ,就會造成重定義。文章來源:http://www.zghlxwxcb.cn/news/detail-860156.html
注意,經(jīng) extern 聲明的變量,不可以再初始化。文章來源地址http://www.zghlxwxcb.cn/news/detail-860156.html
16、位運算
#include <stdio.h> #include <inttypes.h> ? int main() { ? ?// 將變量a的第2位設(shè)置為1,其他位保持不變 ? ?uint8_t a = 0b10110011; // 0xb3; ? ?a |= (1 << 2); ? ? ? ? ?// 或者 x = x | (1 << 2); ? ?printf("%02x\n", a); ? ?// b7, 10110111 ? ? ?// 將變量b的第2位、第6位設(shè)置為1,其他位保持不變 ? ?uint8_t b = 0b10110011; // 0xb3; ? ?b |= (1 << 2 | 1 << 6); ? ?printf("%02x\n", b); ? ?// f7,11110111 ? ? ?// 將變量c的第5位設(shè)置為0,其他位保持不變 ? ?uint8_t c = 0b10110011; ?// 0xb3; ? ?c &= ~(1 << 5); ? ?printf("%02x\n", c); ? ?// 93,10010011 ? ? ?// 將變量d的第0~3位設(shè)置為0,其他位保持不變 ? ?uint8_t d = 0b11111111; ?// 0xff; ? ?d &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3); ? ?printf("%02x\n", d); ? ?// f0,11110000 ? ? ?// 將變量e的第2位取反,其他位保持不變 ? ?uint8_t e = 0b10110011; ?// 0xb3; ? ?e ^= (1 << 2); ? ?printf("%02x\n", e); ? ?// b7, 10110111 ? ? ?return 0; }
17、說說什么是野指針,怎么產(chǎn)生的,如何避免
野指針是指向"垃圾"內(nèi)存的指針,也就是說,它的值是不確定的。野指針通常由以下幾種情況產(chǎn)生: ? 未初始化的指針:如果你聲明了一個指針變量但沒有給它賦值,那么它就是一個野指針。例如:int *ptr;。 已刪除的指針:如果你使用delete或free刪除了一個指針,但沒有將它設(shè)置為NULL,那么它就成了一個野指針。例如: 超出作用域的指針:如果你返回了一個函數(shù)內(nèi)部的局部變量的地址,那么這個地址在函數(shù)返回后就不再有效,因此返回的指針就是一個野指針。 ? ? 初始化: ptr = NULL; ?
18、堆和棧有什么區(qū)別?
- 棧區(qū)(stack) ? - - 棧是一種先進(jìn)后出的內(nèi)存結(jié)構(gòu),由編譯器自動分配釋放,存放函數(shù)的參數(shù)值、返回值、局部變量等。在程序運行過程中實時加載和釋放,因此,局部變量的生存周期為申請到釋放該段??臻g。 ? - 堆區(qū)(heap) ? - - 堆是一個大容器,它的容量要遠(yuǎn)遠(yuǎn)大于棧,但沒有棧那樣先進(jìn)后出的順序。用于動態(tài)內(nèi)存分配。堆在內(nèi)存中位于BSS區(qū)和棧區(qū)之間。一般由程序員分配和釋放,若程序員不釋放,程序結(jié)束時由操作系統(tǒng)回收。
到了這里,關(guān)于C語言--基礎(chǔ)面試真題的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!