第一題、宏解析問題
#define MUL(a,b) a * b
printf("%d", MUL(1 + 2, 1 + 2));
考察的是宏解析後的代碼是什麼樣子的:
MUL(a,b) 解析後爲 a * b
a 轉換爲 1 + 2 、b轉換爲 1 + 2
a * b 轉換爲 1 + 2 * 1 + 2 = 1 + 2 + 2 = 5
第二題、字節對齊問題 32位環境下定義結構體
#pragma pack(1)
struct stu1
{
char a;
short s;
int i;
char str[5];
void* p;
};
#pragma pack()
一字節對齊:
struct stu1
char a; //1
short s; //2
int i; //4
char str[5]; //5
void* p; //4
stu1 的結構體長度爲 16個字節
pragma pack(4)
struct stu2
{
char a;
short s;
int i;
char str[5];
void* p;
};
#pragma pack()
四字節對齊:
struct stu2
char a; [char(1) - - -] char 和 short 合併佔3個字節,因下一個爲4個字節的int,所以補位 1字節爲 4字節
short s; [char(1) short(2) 補位(1)] 爲 4字節對齊
int i; [int(4)] 爲4字節
char str[5]; [char(5) 補位(3)] 爲8字節
void* p; [void*(4)] 爲4字節
stu2 的結構體長度爲 20個字節
(備註:什麼時候用1字節,什麼時候用4字節:
當使用互聯網傳輸的時候用1字節對齊結構能夠得到精簡且數據規整的格式
當內部調用運算時需要提高效率的時候用4字節對齊,4字節對齊不如1字節對齊節約空間,但是會提高讀取效率。
有些平臺:一個int型如果存放在偶地址開始的地方,那麼一個讀週期就可以讀出32bit
而如果存放在奇地址開始的地方,就需要2個讀週期,並對兩次讀出的結果高低字節進行拼湊才能得到該32bit數據。
除了 float、double 以外:
如果是1字節對齊,32位CPU與內存的最小交換數據爲1字節/次,所以 1 字節的變量是線程安全的。
如果是4字節對齊,32位CPU與內存的最小交換數據爲4字節/次,所以 4 字節的變量是線程安全的。)
Lock();
int count = g_test/1024;
int mod= g_test%1024;
UnLock();
//如果不想加鎖 可以寫成
int temp = g_test;
int count = temp/1024;
int mod = temp%1024;
第三題、關於靜態變量 static int i 在全局區的解讀
int fun(int x)
{
static int i = 0;
if (x <= i)
{
i = -x;
}
else
{
i = x;
}
return i;
}
void main()
{
int a = fun(5);
int b = fun(3);
}
答案:fun(5); 輸出爲:_5_
fun(3);輸出爲:_-3_
解析路由:
int fun(int x) //第一輪 [1][ x = 5,i = 0] ↓ 第二輪 [2][x = 3 , i = 5] ↓
static int i = 0;
if (x <= i) // [1][ 5 <= 0 false ] ↓ [2][ 3 <= 5 true ] ↓
i = -x; // [2] [ i = -3 ]
else // [1] [ 5 > 0 ]
i = x; // [1] [ i = 5 ]
return i; // [1][i = 5] ↓ [2][ i = -3] ↓
第四題、關於數組定位 字節定位
int a[5] = {1,2,3,4,5};
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
[解析:a 的字節長度 等於 sizeof(int[5])] 指針位移的長度也爲 sizeof(int(5))個字節
// *(a+1) 1, 2, 3, 4, 5
// a ↑
//a -> sizeof is 5 * int = 20
// 1, 2, 3, 4, 5 []
// ----------------
// &a
// 1, 2, 3, 4, 5 |_______________
// &a + 1
int ptr = (int*)(&a + 1);
//1, 2, 3, 4, 5 | a, b, c, d, e
// ↑
// ptr
//1, 2, 3, 4, 5 | a, b, c, d, e
// ↑ ↑
// ptr - 1
printf("%d,%d", *(a + 1), *(ptr - 1));
輸出:*(a + 1) = 2、*(ptr - 1) = 5
第五題、多態虛擬繼承[掌握調用順序]
class A
{
public:
A(){ a = 5; }
virtual void fun(int x){ a += x; }
void fun2(int x){ a -= x; }
int a;
};
class B :public A
{
public:
B(){ a = 10;}
void fun(int x){ a -= x; }// fun 已實現 A的 fun 重載
virtual void fun2(int x){ a += x; }
};
void main()
{
A* p = new B;
p->fun(1); // 調用順序 B : fun()
printf("%d", p->a); // 9
p->fun2(3); //調用順序 A:fun2 [因爲操作對象是A::fun2 沒有和B::fun2 建立繼承關係]
printf("%d", p->a); // 6
delete p;
p = NULL;
}
解析:virtual 是向下繼承的 ↓
//a fun 無繼承關係
//b virtual fun↓ 開始繼承關係
//c fun 繼承於b
//d fun 繼承於b
創建者爲B、調用對象爲A,fun(1)由於繼承關係,使用B::fun繼承於A::fun重載作爲函數對象;
fun2(3)由於多態是向下繼承的,類A::fun2 與 B::fun2 是兩個獨立的函數,由A對象操作則使用A的調用;
第六題、遍歷斷鏈問題
調用存在運行bug,鏈路在遍歷的過程中當前節點需要被刪除,要把it移動到下一個節點,刪除上一段數據
#include <map>
std::map<int, int> maplint;
void main()
{
for (int i = 0; i < 10; ++i)
{
maplint[i] = i;
}
for (std::map<int, int>::iterator it = maplint.begin(); it != maplint.end(); ++it)
{
printf("%d\n", it->second);
if (it->second % 2 == 0)
{
//std::map<int, int>::iterator ierase = it;//需要保留上一位
//++it; //推移
//maplint.erase(ierase); //刪除上一個位置
//命題改寫 返回it,再執行it+1,再執行 maplint.erase,到下一輪 再執行返回 it + 1
maplint.erase(it++);//0 -> 位移 1 遍歷到 2; 2 -> 位移 3 遍歷到 4 等等
}
}
}
第七題、C語言標準函數strcpy的實現
char * _strcpy(char* dst,const char* src)
{
//原始寫法
//char* tmp = dst;
//while ((*dst++ = *src++) != '\0');
//return tmp;
//清晰寫法
char* tmp = dst;
while (*src != '\0')
{
*dst = *src;
src++;
dst++;
}
return tmp;
}
void main()
{
char src[] = "administrator";
char dst[20] = "";
_strcpy(dst, src);
}
第八題、有序數組的二分查找算法(長度爲len的升序數組arr中查找x)
int searchBin(int arr[], int len, int x)
{
int low, high, mid;
low = 0;
high = len - 1;
while (low <= high)
{
mid = (low + high)/2;
//printf("%d\n",mid);
if (x == arr[mid])
{
return mid;
}
else if (x < arr[mid])
{
high = mid - 1;
}
else
{
low = mid + 1;
}
}
return -1;
}
void main()
{
int arr[10] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int step = searchBin(arr, 10, 0);
}
解析:主要考的內容:從中定位,向上或向下查找
mid爲指向數據的位置,根據數據的大小受low 和 high 的擠壓移向上或向下的位置。
當指向某一個數據時,若該數比指向數小,排指向到最大的數據。
若該數比指向數大,排指向到最小的數據,直到找到後返回。