1. volatile 關鍵字在 C++ 中的性能和 C 的一樣?
作用是一樣的,但是其內部實現原理可能不同。
2. scanf 格式化輸入是怎么賦值的?
由於scanf輸入的數據個數是不定的,從鍵盤輸入的數據會進入緩沖流,然后將輸入的數據賦值給scanf的參數。
3. 下面代碼的作用?
void func(const char* input, char* output, unsigned int outLen) { int buf[256] = {0}; while( *input ) { buf[*input++]++; } if( output && outLen ) { int i = 0; for(i=0; i<256; i++) { if( buf[i] ) { *output++ = (char)i; } } *output = 0; } }
// 輸入input為“aaabbbccddddd”,得到output為abcd
1、下面的程序輸出什么?為什么?(某 CPU 公司面試題)
int main(int argc, char* argv[])
{
unsigned char a = 0xA5;
unsigned char b = ~a >> 4 + 1;
printf("%d\n", b);
return 0;
}
(分析:第一個坑:運算符優先級,+的優先級大於>>;第二個坑:當小類型變量和整型做運算的時候,會轉化為int類型。
這個題,將得到的int型的結果再截斷,最后答案:250)
6. 寫程序判斷一個數是否是 2 的 N 次方! (某 CPU 公司面試題)
(分析:
2的1次方:0000 0001
2的2次方:0000 0010
2的3次方:0000 0100
那么,如果一個數是2的N次方,那么這個數的二進制就只有一個1.
假設X這個數是2的N次方,(X-1 & X)必然等於0!!!
)
7. 有 2 個數組保存着 100 以內的自然數,編程求出兩個數組的交集(兩個數組中同時出現的自然數)。
#include <iostream> using namespace std; int main() { int a[] = {1, 2, 3, 4, 5, 6}; int b[] = {3, 5, 7, 9}; int buf[100] = {0}; for(int i=0; i<sizeof(a)/sizeof(*a); i++) { buf[a[i]]++; } for(int i=0; i<sizeof(b)/sizeof(*b); i++) { if( buf[b[i]] ) cout << b[i] << endl; } return 0; }
8. 面試時如何被問及期望的薪水,該如何回答?
(打聽公司底薪+500)
9. 職場新人應該注意些什么問題?
(如果有的師傅不屌你,那么你就需要主動問師傅有什么雜活可以幫你做的,給師傅節省了時間,打好了關系,他才會也才有時間教你。不要坐着耗下去,因為即便跳槽也一樣。)
1.有一個問題: C++定義一個空類,編譯器都會做些什么??
(例如:定義Test空類
class Test { };
1:如果類中沒有數據成員,那么編譯器會給這個類分配固定的大小,VS是1,不可能是0
2:編譯器會在類中放入構造函數、拷貝構造函數、賦值運算符、析構函數。
Test() {};
Test(const Test& obj) {};
Test& operator= (const Test& obj) {};
~Test() {};
)
2.二階構造法【為解決半成品的構造對象】
(使用場所:當在構造函數里面申請資源,並且這個資源可能申請失敗的時候
分析:相當於將申請資源的步驟分為兩個階段:
1:第一階段申請不會出錯的資源(類對象資源)
2:第二階段申請可能出錯的資源,並進行判斷,出錯返回NULL,否則返回完整對象
這樣就有效地避免了半成品對象的產生!!!
)
4.對於一個給定的字符串,我們需要在線性(也就是O(n))的時間里對它做一些變形。
首先這個字符串中包含着一些空格,就像"Hello World"一樣,然后我們要做的是把這個字符串中
由空格隔開的單詞反序,同時反轉每個字符的大小寫。
比如"Hello World"變形后就變成了"wORLD hELLO"。
(分析:
步驟1:先將大小寫字母反轉;
步驟2:再將整個字符串反轉;
步驟3:最后以空格為分界,每段的字符串再反轉一次。
代碼如下:
#include <iostream> using namespace std; // 字符大小寫反轉 char change_char(char ch) { char ret = ch; if (('a' < ch) && (ch < 'z')) { ret = 'A' + ch - 'a'; } else if (('A' < ch) && (ch < 'Z')) { ret = 'a' + ch - 'A'; } return ret; } // 字符串反轉 void reverse(char s[], int index, int len) { int i = index; // 第一個位置下標 int j = index + len - 1; // 最后一個位置下標 while ( i < j ) { char t = s[i]; s[i] = s[j]; s[j] = t; i++; j--; } } void solution(char s[]) { int len = strlen(s); int i = 0; // 空間換時間 int* space = (int*)malloc(len * sizeof(int)); // 用來記錄空格的位置 int* index = (int*)malloc(len * sizeof(int)); // 用來記錄空格之后第一個字符的位置 int j = 0; int k = 0; for (i = 0; i<len; i++) { s[i] = change_char(s[i]); } cout << "1th: " << s << endl; reverse(s, 0, len); cout << "2th: " << s << endl; for (i = 0; i<len; i++) { if (s[i] == ' ') { space[j] = i; // 記錄第一個空格下標位置 index[j] = k; // 記錄第一個字符下標位置,就是起始的字符位置0 j = j + 1; k = i + 1; } } // 最后字符串的結束符當做空格處理,所以需要再寫一次 space[j] = i; index[j] = k; for (i = 0; i <= j; i++) { reverse(s, index[i], space[i] - index[i]); } free(space); free(index); } int main() { char str[] = "Hello World!"; cout << "before: " << str << endl; solution(str); cout << "after: " << str << endl; return 0; }
1、GetMemory函數用於申請一片內存空間,要使用二重指針。
void GetMemory2(char **p, int num) { *p = (char *)malloc(sizeof(char) * num); } void main(void) { char *str=NULL; GetMemory=(&str); strcpy(str,"hello world"); printf(str); }
2、列舉幾種進程的同步機制,並比較其優缺點。
答案: 原子操作 信號量機制 自旋鎖 管程,會合,分布式系統
3、進程之間通信的途徑
答案:共享存儲系統消息傳遞系統管道:以文件系統為基礎
4、進程死鎖的原因
答案:資源競爭及進程推進順序非法
5、死鎖的 4個必要條件
答案:互斥、請求保持、不可剝奪、環路
6、死鎖的處理
答案:鴕鳥策略、預防策略、避免策略、檢測與解除死鎖
7、操作系統中進程調度策略有哪幾種?
答案:FCFS(先來先服務),優先級,時間片輪轉,多級反饋
8、(void *)ptr 和 (*(void**))ptr 的結果是否相同?(其中 ptr為同一個指針)
答案:(void *)ptr 和 (*(void**))ptr值是相同的
9、要對絕對地址 0x100000 賦值,我們可以用 (unsigned int*)0x100000 = 1234; 那么要是
想讓程序跳轉到絕對地址是 0x100000 去執行,應該怎么做?
答案:*((void (*)( ))0x100000 ) ( );
首先要將 0x100000 強制轉換成函數指針,即: (void (*)())0x100000
然后再調用它: *((void (*)())0x100000)();
用 typedef 可以看得更直觀些:
typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0x100000)();
(但是在gcc里面編譯會出錯,但是寫成:
typedef void(*FuncType)();
FuncType pf = (FuncType)0x10000;
pf();
可以編譯通過,只不過段錯誤。)
10、
11、
微軟亞洲技術中心的面試題!!!
1.進程和線程的差別。
線程是指進程內的一個執行單元,也是進程內的可調度實體.
與進程的區別:
(1)調度:線程作為調度和分配的基本單位,進程作為擁有資源的基本單位
(2)並發性:不僅進程之間可以並發執行,同一個進程的多個線程之間也可並發執行
(3)擁有資源:進程是擁有資源的一個獨立單位,線程不擁有系統資源,但可以訪問隸屬於進程的資源.
(4)系統開銷:在創建或撤消進程時,由於系統都要為之分配和回收資源,導致系統的開銷明顯大於創建或撤消線程時的開銷。
2.測試方法
人工測試:個人復查、抽查和會審
機器測試:黑盒測試和白盒測試
unsigned short A = 10;
printf("~A = %u\n", ~A);
char c=128;
printf("c=%d\n",c);
輸出多少?並分析過程
第一題,~A =0xfffffff5,int值 為-11,但輸出的是uint。所以輸出4294967285
第二題,c=0x80,以char的8位字節來看,最高位為1,是負數。所以在內存的存儲方式為補碼(補碼 = 反碼 + 1)所以~(10000000-1)=-128
這兩道題都是在考察二進制向int或uint轉換時的最高位處理。
#include<iostream.h>
#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
typedef struct AA
{
int b1:5;
int b2:2;
}AA;
void main()
{
AA aa;
char cc[100];
strcpy(cc,"0123456789abcdefghijklmnopqrstuvwxyz");
memcpy(&aa,cc,sizeof(AA));
cout << aa.b1 <<endl;
cout << aa.b2 <<endl;
}
答案是 -16和1
首先sizeof(AA)的大小為4,b1和b2分別占5bit和2bit.
經過strcpy和memcpy后,aa的4個字節所存放的值是:
0,1,2,3的ASC碼,即00110000,00110001,00110010,00110011
所以,最后一步:顯示的是這4個字節的前5位,和之后的2位
分別為:10000,和01
因為int是有正負之分 所以:答案是-16和1
求函數返回值,輸入x=9999;
int func ( x )
{
int countx = 0;
while ( x )
{
countx ++;
x = x&(x-1);
}
return countx;
}
結果呢?
知道了這是統計9999的二進制數值中有多少個1的函數,且有
9999=9×1024+512+256+15
9×1024中含有1的個數為2;
512中含有1的個數為1;
256中含有1的個數為1;
15中含有1的個數為4;
故共有1的個數為8,結果為8。
1000 - 1 = 0111,正好是原數取反。這就是原理。
用這種方法來求1的個數是很效率很高的。
不必去一個一個地移位。循環次數最少。
struct bit
{ int a:3;
int b:2;
int c:3;
};
int main()
{
bit s;
char *c=(char*)&s;
cout<<sizeof(bit)<<endl;
*c=0x99;
cout << s.a <<endl <<s.b<<endl<<s.c<<endl;
int a=-1;
printf("%x",a);
return 0;
}
輸出為什么是
4
1
-1
-4
ffffffff
因為0x99在內存中表示為 100 11 001 , a = 001, b = 11, c = 100
當c為有符合數時, c = 100, 最高1為表示c為負數,負數在計算機用補碼表示,所以c = -4;同理
b = -1;
位域 :
有些信息在存儲時,並不需要占用一個完整的字節, 而只需占幾個或一個二進制位。例如在存放一個開關量時,只有0和1 兩種狀態, 用一位二進位即可。為了節省存儲空間,並使處理簡便,C語言又提供了一種數據結構,稱為“位域”或“位段”。所謂“位域”是把一個字節中的二進位划分為幾個不同的區域, 並說明每個區域的位數。每個域有一個域名,允許在程序中按域名進行操作。 這樣就可以把幾個不同的對象用一個字節的二進制位域來表示。一、位域的定義和位域變量的說明位域定義與結構定義相仿,其形式為:
struct 位域結構名
{ 位域列表 };
其中位域列表的形式為: 類型說明符 位域名:位域長度
例如:
struct bs
{
int a:8;
int b:2;
int c:6;
};
位域變量的說明與結構變量說明的方式相同。 可采用先定義后說明,同時定義說明或者直接說明這三種方式。例如:
struct bs
{
int a:8;
int b:2;
int c:6;
}data;
說明data為bs變量,共占兩個字節。其中位域a占8位,位域b占2位,位域c占6位。對於位域的定義尚有以下幾點說明:
1. 一個位域必須存儲在同一個字節中,不能跨兩個字節。如一個字節所剩空間不夠存放另一位域時,應從下一單元起存放該位域。也可以有意使某位域從下一單元開始。例如:
struct bs
{
unsigned a:4
unsigned :0 /*空域*/
unsigned b:4 /*從下一單元開始存放*/
unsigned c:4
}
在這個位域定義中,a占第一字節的4位,后4位填0表示不使用,b從第二字節開始,占用4位,c占用4位。
2. 由於位域不允許跨兩個字節,因此位域的長度不能大於一個字節的長度,也就是說不能超過8位二進位。
3. 位域可以無位域名,這時它只用來作填充或調整位置。無名的位域是不能使用的。例如:
struct k
{
int a:1
int :2 /*該2位不能使用*/
int b:3
int c:2
};
從以上分析可以看出,位域在本質上就是一種結構類型, 不過其成員是按二進位分配的。
(2)strcpy能把strSrc的內容復制到strDest,為什么還要char * 類型的返回值?
答:為了實現鏈式表達式。 // 2分
例如 int length = strlen( strcpy( strDest, “hello world”) );
編寫類String的構造函數、析構函數和賦值函數
已知類String的原型為:
class String
{
public:
String(const char *str = NULL);// 普通構造函數
String(const String &other); // 拷貝構造函數
~ String(void); // 析構函數
String & operate =(const String &other);// 賦值函數
private:
char *m_data; // 用於保存字符串
};
請編寫String的上述4個函數。
標准答案:
// String的析構函數
String::~String(void) // 3分
{
delete [] m_data;
// 由於m_data是內部數據類型,也可以寫成 delete m_data;
}
// String的普通構造函數
String::String(const char *str) // 6分
{
if(str==NULL)
{
m_data = new char[1]; // 若能加 NULL 判斷則更好
*m_data = ‘\0’;
}
else
{
int length = strlen(str);
m_data = new char[length+1]; // 若能加 NULL 判斷則更好
strcpy(m_data, str);
}
}
// 拷貝構造函數
String::String(const String &other) // 3分
{
int length = strlen(other.m_data);
m_data = new char[length+1]; // 若能加 NULL 判斷則更好
strcpy(m_data, other.m_data);
}
// 賦值函數
String & String::operate =(const String &other) // 13分
{
// (1) 檢查自賦值 // 4分
if(this == &other)
return *this;
// (2) 釋放原有的內存資源 // 3分
delete [] m_data;
// (3)分配新的內存資源,並復制內容 // 3分
int length = strlen(other.m_data);
m_data = new char[length+1]; // 若能加 NULL 判斷則更好
strcpy(m_data, other.m_data);
// (4)返回本對象的引用 // 3分
return *this;
}
請問下面程序有沒有錯誤為什么?
(段錯誤: char *strcpy(char *dest, const char *src);是拷貝字符串的函數,s只是字符數組,在結尾沒有’\0’字符。)
(strlen(a) = 255;因為char類型的范圍為-256~255,當i=256時,a[256] = 1 0000 0000b, 產生截斷,a[256]= 0,對於strlen來看相當於結束符。)