http://www.cnblogs.com/Purple_Xiapei/archive/2012/05/10/2495003.htmlhtml
第一部分:基本概念及其它問答題 二、「引用」與指針的區別是什麼? 三、.h頭文件中的ifndef/define/endif 的做用? 四、#include 與 #include 「file.h」的區別? 五、描述實時系統的基本特性 六、全局變量和局部變量在內存中是否有區別?若是有,是什麼區別? 七、什麼是平衡二叉樹? 八、堆棧溢出通常是由什麼緣由致使的? 九、冒泡排序算法的時間複雜度是什麼? 十一、隊列和棧有什麼區別? 十二、不能作switch()的參數類型 1三、局部變量可否和全局變量重名? 1四、如何引用一個已經定義過的全局變量? 1五、全局變量可不能夠定義在可被多個.C文件包含的頭文件中?爲何? 1六、語句for( ;1 ;)有什麼問題?它是什麼意思? 1七、do……while和while……do有什麼區別? 1八、statac 全局變量、局部變量、函數與普通全局變量、局部變量、函數 1九、程序的內存分配 例子程序 20、解釋堆和棧的區別 一、 老是使用不常常改動的大型代碼體。 2二、關鍵字const是什麼含意? 2三、關鍵字volatile有什麼含意 並給出三個不一樣的例子。 2四、三種基本的數據模型 2五、結構與聯合有和區別? 2六、描述內存分配方式以及它們的區別? 2七、請說出const與#define 相比,有何優勢? 2八、簡述數組與指針的區別? 2九、分別寫出BOOL,int,float,指針類型的變量a 與「零」的比較語句。 30、如何判斷一段程序是由C 編譯程序仍是由C++編譯程序編譯的? 3三、嵌入式系統中常常要用到無限循環,你怎麼樣用C編寫死循環呢? 3四、位操做(Bit manipulation) void set_bit3(void) 3五、訪問固定的內存位置(Accessing fixed memory locations) 3六、中斷(Interrupts) __interrupt double compute_area (double radius) 3九、用變量a給出下面的定義 答案是: 40、解釋局部變量、全局變量和靜態變量的含義。 4二、A.c 和B.c兩個c文件中使用了兩個相同名字的static變量,編譯的時候會不會有問題?這兩個static變量會保存到哪裏(棧仍是堆或者其餘的)? 4三、一個單向鏈表,不知道頭節點,一個指針指向其中的一個節點,問如何刪除這個指針指向的節點? |
第二部分:程序代碼評價或者找錯
一、下面的代碼輸出是什麼,爲何?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts(「> 6″) : puts(「<= 6");
}
這個問題測試你是否懂得C語言中的整數自動轉換原則,我發現有些開發者懂得極少這些東西。無論如何,這無符號整型問題的答案是輸出是 ">6″。緣由是當表達式中存在有符號類型和無符號類型時全部的操做數都自動轉換爲無符號類型。所以-20變成了一個很是大的正整數,因此該表達式計算出的結果大於6。這一點對於應當頻繁用到無符號數據類型的嵌入式系統來講是豐常重要的。若是你答錯了這個問題,你也就到了得不到這份工做的邊緣。
二、評價下面的代碼片段:
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1′s complement of zero */
對於一個int型不是16位的處理器爲說,上面的代碼是不正確的。應編寫以下:
unsigned int compzero = ~0;
這一問題真正能揭露出應試者是否懂得處理器字長的重要性。在個人經驗裏,好的嵌入式程序員很是準確地明白硬件的細節和它的侷限,然而PC機程序每每把硬件做爲一個沒法避免的煩惱。
三、 C語言贊成一些使人震驚的結構,下面的結構是合法的嗎,若是是它作些什麼?
int a = 5, b = 7, c;
c = a+++b;
這個問題將作爲這個測驗的一個愉快的結尾。無論你相不相信,上面的例子是徹底合乎語法的。問題是編譯器如何處理它?水平不高的編譯做者實際上會爭論這個問題,根據最處理原則,編譯器應當能處理儘量全部合法的用法。所以,上面的代碼被處理成:
c = a++ + b;
所以, 這段代碼持行後a = 6, b = 7, c = 12。
若是你知道答案,或猜出正確答案,作得好。若是你不知道答案,我也不把這個看成問題。我發現這個問題的最大好處是這是一個關於代碼編寫風格,代碼的可讀性,代碼的可修改性的好的話題。
四、設有如下說明和定義:
typedef union {long i; int k[5]; char c;} DATE;
struct data { int cat; DATE cow; double dog;} too;
DATE max;
則語句 printf(「%d」,sizeof(struct date)+sizeof(max));的執行結果是?
答 、結果是:52。DATE是一個union, 變量公用空間. 裏面最大的變量類型是int[5], 佔用20個字節. 因此它的大小是20
data是一個struct, 每一個變量分開佔用空間. 依次爲int4 + DATE20 + double8 = 32.
因此結果是 20 + 32 = 52.
固然…在某些16位編輯器下, int多是2字節,那麼結果是 int2 + DATE10 + double8 = 20
五、請寫出下列代碼的輸出內容
#include
main()
{
int a,b,c,d;
a=10;
b=a++;
c=++a;
d=10*a++;
printf(「b,c,d:%d,%d,%d」,b,c,d);
return 0;
}
答:10,12,120
六、寫出下列代碼的輸出內容
#include
int inc(int a)
{
return(++a);
}
int multi(int*a,int*b,int*c)
{
return(*c=*a**b);
}
typedef int(FUNC1)(int in);
typedef int(FUNC2) (int*,int*,int*);
void show(FUNC2 fun,int arg1, int*arg2)
{
INCp=&inc;
int temp =p(arg1);
fun(&temp,&arg1, arg2);
printf(「%d\n」,*arg2);
}
main()
{
int a;
show(multi,10,&a);
return 0;
}
答:110
七、請找出下面代碼中的因此錯誤
說明:如下代碼是把一個字符串倒序,如「abcd」倒序後變爲「dcba」
一、#include」string.h」
二、main()
三、{
四、 char*src=」hello,world」;
五、 char* dest=NULL;
六、 int len=strlen(src);
七、 dest=(char*)malloc(len);
八、 char* d=dest;
九、 char* s=src[len];
十、 while(len–!=0)
十一、 d++=s–;
十二、 printf(「%s」,dest);
1三、 return 0;
1四、}
答:
方法1:
int main(){
char* src = 「hello,world」;
int len = strlen(src);
char* dest = (char*)malloc(len+1);//要爲\0分配一個空間
char* d = dest;
char* s = &src[len-1];//指向最後一個字符
while( len– != 0 )
*d++=*s–;
*d = 0;//尾部要加\0
printf(「%s\n」,dest);
free(dest);// 使用完,應當釋放空間,以避免形成內存匯泄露
return 0;
}
方法2:
#include
#include
main()
{
char str[]=」hello,world」;
int len=strlen(str);
char t;
for(int i=0; i {
t=str[i];
str[i]=str[len-i-1]; str[len-i-1]=t;
}
printf("%s",str);
return 0;
}
八、請問下面程序有什麼錯誤?
int a[60][250][1000],i,j,k;
for(k=0;k<=1000;k++)
for(j=0;j<250;j++)
for(i=0;i<60;i++)
a[i][j][k]=0;
答案:把循環語句內外換一下
九、請問下面程序會出現什麼狀況?
. #define Max_CB 500
void LmiQueryCSmd(Struct MSgCB * pmsg)
{
unsigned char ucCmdNum;
......
for(ucCmdNum=0;ucCmdNum {
......;
}
答案:死循環
十、如下3個有什麼區別
char * const p; //常量指針,p的值不能夠修改
char const * p;//指向常量的指針,指向的常量值不能夠改
const char *p; //和char const *p
十一、寫出下面的結果
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char *str5 = "abc";
const char *str6 = "abc";
char *str7 = "abc";
char *str8 = "abc";
cout << ( str1 == str2 ) << endl;
cout << ( str3 == str4 ) << endl;
cout << ( str5 == str6 ) << endl;
cout << ( str7 == str8 ) << endl;
結果是:0 0 1 1
解答:str1,str2,str3,str4是數組變量,它們有各自的內存空間;
而str5,str6,str7,str8是指針,它們指向相同的常量區域。
十二、如下代碼中的兩個sizeof用法有問題嗎?
void UpperCase( char str[] ) // 將 str 中的小寫字母轉換成大寫字母
{
for( size_t i=0; i if( 'a'<=str[i] && str[i]<='z' )
str[i] -= ('a'-'A' );
}
char str[] = "aBcDe";
cout << "str字符長度爲: " << sizeof(str)/sizeof(str[0]) << endl;
UpperCase( str );
cout << str << endl;
答:函數內的sizeof有問題。根據語法,sizeof如用於數組,只能測出靜態數組的大小,沒法檢測動態分配的或外部數組大小。函數外的str是一個靜態定義的數組,所以其大小爲6,函數內的str實際只是一個指向字符串的指針,沒有任何額外的與數組相關的信息,所以sizeof做用於上只將其當指針看,一個指針爲4個字節,所以返回4。
1三、寫出輸出結果
main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
輸出:2,5
*(a+1)就是a[1],*(ptr-1)就是a[4],執行結果是2,5
&a+1不是首地址+1,系統會認爲加一個a數組的偏移,是偏移了一個數組的大小(本例是5個int)
int *ptr=(int *)(&a+1);
則ptr實際是&(a[5]),也就是a+5
緣由以下:
&a是數組指針,其類型爲 int (*)[5];
而指針加1要根據指針類型加上必定的值,
不一樣類型的指針+1以後增長的大小不一樣
a是長度爲5的int數組指針,因此要加 5*sizeof(int)
因此ptr實際是a[5]
可是prt與(&a+1)類型是不同的(這點很重要)
因此prt-1只會減去sizeof(int*)
a,&a的地址是同樣的,但意思不同,a是數組首地址,也就是a[0]的地址,&a是對象(數組)首地址,a+1是數組下一元素的地址,即a[1],&a+1是下一個對象的地址,即a[5].
1四、請問如下代碼有什麼問題:
int main()
{
char a;
char *str=&a;
strcpy(str,"hello");
printf(str);
return 0;
}
沒有爲str分配內存空間,將會發生異常
問題出在將一個字符串複製進一個字符變量指針所指地址。雖然能夠正確輸出結果,但由於越界進行內在讀寫而致使程序崩潰。
char* s="AAA";
printf("%s",s);
s[0]='B';
printf("%s",s);
有什麼錯?
"AAA"是字符串常量。s是指針,指向這個字符串常量,因此聲明s的時候就有問題。
cosnt char* s="AAA";
而後又由於是常量,因此對是s[0]的賦值操做是不合法的。
1五、有如下表達式:
int a=248; b=4;int const c=21;const int *d=&a;
int *const e=&b;int const *f const =&a;
請問下列表達式哪些會被編譯器禁止?爲何?
*c=32;d=&b;*d=43;e=34;e=&a;f=0x321f;
*c 這是個什麼東東,禁止
*d 說了是const, 禁止
e = &a 說了是const 禁止
const *f const =&a; 禁止
1六、交換兩個變量的值,不使用第三個變量。
即a=3,b=5,交換以後a=5,b=3;
有兩種解法, 一種用算術算法, 一種用^(異或)
a = a + b;
b = a - b;
a = a - b;
or
a = a^b;// 只能對int,char..
b = a^b;
a = a^b;
or
a ^= b ^= a;
1七、下面的程序會出現什麼結果
.#include
#include
void getmemory(char *p)
{
p=(char *) malloc(100);
strcpy(p,」hello world」);
}
int main( )
{
char *str=NULL;
getmemory(str);
printf(「%s/n」,str);
free(str);
return 0;
}
程序崩潰,getmemory中的malloc 不能返回動態內存, free()對str操做很危險
1八、下面的語句會出現什麼結果?
char szstr[10];
strcpy(szstr,」0123456789″);
答案:長度不同,會形成非法的OS,應該改成char szstr[11];
1九、(void *)ptr 和 (*(void**))ptr的結果是否相同?
答:其中ptr爲同一個指針
.(void *)ptr 和 (*(void**))ptr值是相同的
20、問函數既然不會被其它函數調用,爲何要返回1?
int main()
{
int x=3;
printf(「%d」,x);
return 1;
}
答:mian中,c標準認爲0表示成功,非0表示錯誤。具體的值是某中具體出錯信息
2一、對絕對地址0×100000賦值且想讓程序跳轉到絕對地址是0×100000去執行
(unsigned int*)0×100000 = 1234;
首先要將0×100000強制轉換成函數指針,即:
(void (*)())0×100000
而後再調用它:
*((void (*)())0×100000)();
用typedef能夠看得更直觀些:
typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0×100000)();
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=0×10,輸出的是int,最高位爲1,是負數,因此它的值就是0×00的補碼就是128,因此輸出-128。
這兩道題都是在考察二進制向int或uint轉換時的最高位處理。
2三、分析下面的程序:
void GetMemory(char **p,int num)
{
*p=(char *)malloc(num);
}
int main()
{
char *str=NULL;
GetMemory(&str,100);
strcpy(str,」hello」);
free(str);
if(str!=NULL)
{
strcpy(str,」world」);
}
printf(「\n str is %s」,str);
getchar();
}
問輸出結果是什麼?但願你們能說說緣由,先謝謝了
輸出str is world。
free 只是釋放的str指向的內存空間,它自己的值仍是存在的.
因此free以後,有一個好的習慣就是將str=NULL.
此時str指向空間的內存已被回收,若是輸出語句以前還存在分配空間的操做的話,這段存儲空間是可能被從新分配給其餘變量的,
儘管這段程序確實是存在大大的問題(上面各位已經說得很清楚了),可是一般會打印出world來。
這是由於,進程中的內存管理通常不是由操做系統完成的,而是由庫函數本身完成的。
當你malloc一塊內存的時候,管理庫向操做系統申請一塊空間(可能會比你申請的大一些),而後在這塊空間中記錄一些管理信息(通常是在你申請的內存前面一點),並將可用內存的地址返回。可是釋放內存的時候,管理庫一般都不會將內存還給操做系統,所以你是能夠繼續訪問這塊地址的,只不過。。。。。。。。樓上都說過了,最好別這麼幹。
2四、char a[10],strlen(a)爲何等於15?運行的結果
#include 「stdio.h」
#include 「string.h」
void main()
{
char aa[10];
printf(「%d」,strlen(aa));
}
sizeof()和初不初始化,沒有關係;
strlen()和初始化有關。
char (*str)[20];/*str是一個數組指針,即指向數組的指針.*/
char *str[20];/*str是一個指針數組,其元素爲指針型數據.*/
2五、long a=0×801010;a+5=?
答:0×801010用二進制表示爲:「1000 0000 0001 0000 0001 0000」,十進制的值爲8392720,再加上5就是8392725
2六、給定結構struct A
{
char t::4;
char k:4;
unsigned short i:8;
unsigned long m;
};問sizeof(A) = ?
給定結構struct A
{
char t:4; 4位
char k:4; 4位
unsigned short i:8; 8位
unsigned long m; // 偏移2字節保證4字節對齊
}; // 共8字節
2七、下面的函數實如今一個數上加一個數,有什麼錯誤?請改正。
int add_n ( int n )
{
static int i = 100;
i += n;
return i;
}
當你第二次調用時得不到正確的結果,難道你寫個函數就是爲了調用一次?問題就出在 static上
2八、給出下面程序的答案
#include
#include
#include
#include
#include
#include
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 < cout << aa.b2 < }
答案是 -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
2九、求函數返回值,輸入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的個數是很效率很高的。
沒必要去一個一個地移位。循環次數最少。
30、分析:
struct bit
{ int a:3;
int b:2;
int c:3;
};
int main()
{
bit s;
char *c=(char*)&s;
cout< *c=0x99;
cout << s.a < 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;
當c爲有符合數時, c = 100,即 c = 4,同理 b = 3
3一、下面這個程序執行後會有什麼錯誤或者效果:
#define MAX 255
int main()
{
unsigned char A[MAX],i;//i被定義爲unsigned char
for (i=0;i<=MAX;i++)
A[i]=i;
}
解答:死循環加數組越界訪問(C/C++不進行數組越界檢查)
MAX=255
數組A的下標範圍爲:0..MAX-1,這是其一..
其二.當i循環到255時,循環內執行:
A[255]=255;
這句自己沒有問題..可是返回for (i=0;i<=MAX;i++)語句時,
因爲unsigned char的取值範圍在(0..255),i++之後i又爲0了..無限循環下去.
3二、寫出sizeof(struct name1)=,sizeof(struct name2)=的結果
struct name1{
char str;
short x;
int num;
}
struct name2{
char str;
int num;
short x;
}
sizeof(struct name1)=8,sizeof(struct name2)=12
在第二個結構中,爲保證num按四個字節對齊,char後必須留出3字節的空間;同時爲保證整個結構的天然對齊(這裏是4字節對齊),在x後還要補齊2個字節,這樣就是12字節。
3三、struct s1
{
int i: 8;
int j: 4;
int a: 3;
double b;
};
struct s2
{
int i: 8;
int j: 4;
double b;
int a:3;
};
printf("sizeof(s1)= %d\n", sizeof(s1));
printf("sizeof(s2)= %d\n", sizeof(s2));
result: 16, 24
第一個struct s1
{
int i: 8;
int j: 4;
int a: 3;
double b;
};
理論上是這樣的,首先是i在相對0的位置,佔8位一個字節,而後,j就在相對一個字節的位置,因爲一個位置的字節數是4位的倍數,所以不用對齊,就放在那裏了,而後是a,要在3位的倍數關係的位置上,所以要移一位,在15位的位置上放下,目前總共是18位,折算過來是2字節2位的樣子,因爲double是8字節的,所以要在相對0要是8個字節的位置上放下,所以從18位開始到8個字節之間的位置被忽略,直接放在8字節的位置了,所以,總共是16字節。
第二個最後會對照是否是結構體內最大數據的倍數,不是的話,會補成是最大數據的倍數
3四、在對齊爲4的狀況下
struct BBB
{
long num;
char *name;
short int data;
char ha;
short ba[5];
}*p;
p=0x1000000;
p+0x200=____;
(Ulong)p+0x200=____;
(char*)p+0x200=____;
但願各位達人給出答案和緣由,謝謝拉
解答:假設在32位CPU上,
sizeof(long) = 4 bytes
sizeof(char *) = 4 bytes
sizeof(short int) = sizeof(short) = 2 bytes
sizeof(char) = 1 bytes
因爲是4字節對齊,
sizeof(struct BBB) = sizeof(*p)
= 4 + 4 + 2 + 1 + 1/*補齊*/ + 2*5 + 2/*補齊*/ = 24 bytes (經Dev-C++驗證)
p=0x1000000;
p+0x200=____;
= 0x1000000 + 0x200*24
(Ulong)p+0x200=____;
= 0x1000000 + 0x200
(char*)p+0x200=____;
= 0x1000000 + 0x200*4
3五、找錯
Void test1()
{
char string[10];
char* str1="0123456789";
strcpy(string, str1);// 溢出,應該包括一個存放'\0'的字符string[11]
}
Void test2()
{
char string[10], str1[10];
for(I=0; I<10;I++)
{
str1[i] ='a';
}
strcpy(string, str1);// I,i沒有聲明。
}
Void test3(char* str1)
{
char string[10];
if(strlen(str1)<=10)// 改爲<10,字符溢出,將strlen改成sizeof也能夠
{
strcpy(string, str1);
}
}
3六、寫出輸出結果
void g(int**);
int main()
{
int line[10],i;
int *p=line; //p是地址的地址
for (i=0;i<10;i++)
{
*p=i;
g(&p);//數組對應的值加1
}
for(i=0;i<10;i++)
printf("%d\n",line[i]);
return 0;
}
void g(int**p)
{
(**p)++;
(*p)++;// 無效
}
輸出:
1
2
3
4
5
6
7
8
9
10
3七、寫出程序運行結果
int sum(int a)
{
auto int c=0;
static int b=3;
c+=1;
b+=2;
return(a+b+c);
}
void main()
{
int I;
int a=2;
for(I=0;I<5;I++)
{
printf("%d,", sum(a));
}
}
// static會保存上次結果,記住這一點,剩下的本身寫
輸出:8,10,12,14,16,
3八、評價代碼
int func(int a)
{
int b;
switch(a)
{
case 1: 30;
case 2: 20;
case 3: 16;
default: 0
}
return b;
}
則func(1)=?
// b定義後就沒有賦值
int a[3];
a[0]=0; a[1]=1; a[2]=2;
int *p, *q;
p=a;
q=&a[2];
則a[q-p]=a[2]
解釋:指針一次移動一個int但計數爲1
3九、請問一下程序將輸出什麼結果?
char *RetMenory(void)
{
char p[] = 「hellow world」;
return p;
}
void Test(void)
{
char *str = NULL;
str = RetMemory();
printf(str);
}
RetMenory執行完畢,p資源被回收,指向未知地址。返回地址,str的內容應是不可預測的, 打印的應該是str的地址
40、寫出輸出結果
typedef struct
{
int a:2;
int b:2;
int c:1;
}test;
test t;
t.a = 1;
t.b = 3;
t.c = 1;
printf("%d",t.a);
printf("%d",t.b);
printf("%d",t.c);
t.a爲01,輸出就是1
t.b爲11,輸出就是-1
t.c爲1,輸出也是-1
3個都是有符號數int嘛。
這是位擴展問題
01
11
1
編譯器進行符號擴展
4一、對下面程序進行分析
void test2()
{
char string[10], str1[10];
int i;
for(i=0; i<10; i++)
{
str1[i] = 'a';
}
strcpy( string, str1 );
}
解答:若是面試者指出字符數組str1不能在數組內結束能夠給3分;若是面試者指出strcpy(string, str1)調用使得從str1內存起復制到string內存起所複製的字節數具備不肯定性能夠給7分,在此基礎上指出庫函數strcpy工做方式的給10分;
str1不能在數組內結束:由於str1的存儲爲:{a,a,a,a,a,a,a,a,a,a},沒有'\0'(字符串結束符),因此不能結束
strcpy( char *s1,char *s2)他的工做原理是,掃描s2指向的內存,逐個字符付到s1所指向的內存,直到碰到'\0',由於str1結尾沒有'\0',因此具備不肯定性,不知道他後面還會付什麼東東。
正確應以下
void test2()
{
char string[10], str1[10];
int i;
for(i=0; i<9; i++)
{
str1[i] = 'a'+i; //把abcdefghi賦值給字符數組
}
str[i]='\0';//加上結束符
strcpy( string, str1 );
}
4二、分析:
int arr[] = {6,7,8,9,10};
int *ptr = arr;
*(ptr++)+=123;
printf(「 %d %d 」, *ptr, *(++ptr));
輸出:8 8
過程:對於*(ptr++)+=123;先作加法6+123,而後++,指針指向7;對於printf(「 %d %d 」, *ptr, *(++ptr));從後往前執行,指針先++,指向8,而後輸出8,緊接着再輸出8
4三、分析下面的代碼:
char *a = "hello";
char *b = "hello";
if(a= =b)
printf("YES");
else
printf("NO");
這個簡單的面試題目,我選輸出 no(對比的應該是指針地址吧),可在VC是YES 在C是NO
lz的呢,是一個常量字符串。位於靜態存儲區,它在程序生命期內恆定不變。若是編譯器優化的話,會有可能a和b同時指向同一個hello的。則地址相同。若是編譯器沒有優化,那麼就是兩個不一樣的地址,則不一樣
4四、寫出輸出結果
#include
void foo(int m, int n)
{
printf(「m=%d, n=%d\n」, m, n);
}
int main()
{
int b = 3;
foo(b+=3, ++b);
printf(「b=%d\n」, b);
return 0;
}
輸出:m=7,n=4,b=7(VC6.0)
這種方式和編譯器中得函數調用關係相關即前後入棧順序。不過不一樣
編譯器得處理不一樣。也是由於C標準中對這種方式說明爲未定義,因此
各個編譯器廠商都有本身得理解,因此最後產生得結果徹底不一樣。
由於這樣,因此碰見這種函數,咱們首先要考慮咱們得編譯器會如何處理
這樣得函數,其次看函數得調用方式,不一樣得調用方式,可能產生不一樣得
結果。最後是看編譯器優化。
4五、找出錯誤
#include string.h
main(void)
{ char *src=」hello,world」;
char *dest=NULL;
dest=(char *)malloc(strlen(src));
int len=strlen(str);
char *d=dest;
char *s=src[len];
while(len–!=0)
d++=s–;
printf(「%s」,dest);
}
找出錯誤!!
#include 「string.h」
#include 「stdio.h」
#include 「malloc.h」
main(void)
{
char *src=」hello,world」;
char *dest=NULL;
dest=(char *)malloc(sizeof(char)*(strlen(src)+1));
int len=strlen(src);
char *d=dest;
char *s=src+len-1;
while(len–!=0)
*d++=*s–;
*d=’\0′;
printf(「%s」,dest);
}
第三部分:編程題
一、讀文件file1.txt的內容(例如):
12
34
56
輸出到file2.txt:
56
34
12
#include
#include
int main(void)
{
int MAX = 10;
int *a = (int *)malloc(MAX * sizeof(int));
int *b;
FILE *fp1;
FILE *fp2;
fp1 = fopen(「a.txt」,」r」);
if(fp1 == NULL)
{printf(「error1″);
exit(-1);
}
fp2 = fopen(「b.txt」,」w」);
if(fp2 == NULL)
{printf(「error2″);
exit(-1);
}
int i = 0;
int j = 0;
while(fscanf(fp1,」%d」,&a[i]) != EOF)
{
i++;
j++;
if(i >= MAX)
{
MAX = 2 * MAX;
b = (int*)realloc(a,MAX * sizeof(int));
if(b == NULL)
{
printf(「error3″);
exit(-1);
}
a = b;
}
}
for(;–j >= 0;)
fprintf(fp2,」%d\n」,a[j]);
fclose(fp1);
fclose(fp2);
return 0;
}
二、輸出和爲一個給定整數的全部組合
例如n=5
5=1+4;5=2+3(相加的數不能重複)
則輸出
1,4;2,3。
#include
int main(void)
{
unsigned long int i,j,k;
printf(「please input the number\n」);
scanf(「%d」,&i);
if( i % 2 == 0)
j = i / 2;
else
j = i / 2 + 1;
printf(「The result is \n」);
for(k = 0; k < j; k++)
printf("%d = %d + %d\n",i,k,i - k);
return 0;
}
#include
void main()
{
unsigned long int a,i=1;
scanf(「%d」,&a);
if(a%2==0)
{
for(i=1;i printf("%d",a,a-i);
}
else
for(i=1;i<=a/2;i++)
printf(" %d, %d",i,a-i);
}
三、遞規反向輸出字符串的例子,可謂是反序的經典例程.
void inverse(char *p)
{
if( *p = = '\0' )
return;
inverse( p+1 );
printf( "%c", *p );
}
int main(int argc, char *argv[])
{
inverse("abc\0");
return 0;
}
對1的另外一種作法:
#include
void test(FILE *fread, FILE *fwrite)
{
char buf[1024] = {0};
if (!fgets(buf, sizeof(buf), fread))
return;
test( fread, fwrite );
fputs(buf, fwrite);
}
int main(int argc, char *argv[])
{
FILE *fr = NULL;
FILE *fw = NULL;
fr = fopen(「data」, 「rb」);
fw = fopen(「dataout」, 「wb」);
test(fr, fw);
fclose(fr);
fclose(fw);
return 0;
}
四、寫一段程序,找出數組中第k大小的數,輸出數所在的位置。例如{2,4,3,4,7}中,第一大的數是7,位置在4。第二大、第三大的數都是4,位置在一、3隨便輸出哪個都可。函數接口爲:int find_orderk(const int* narry,const int n,const int k)
要求算法複雜度不能是O(n^2)
謝謝!
能夠先用快速排序進行排序,其中用另一個進行地址查找
代碼以下,在VC++6.0運行經過。給分吧^-^
//快速排序
#include
usingnamespacestd;
intPartition (int*L,intlow,int high)
{
inttemp = L[low];
intpt = L[low];
while (low < high)
{
while (low < high && L[high] >= pt)
–high;
L[low] = L[high];
while (low < high && L[low] <= pt)
++low;
L[low] = temp;
}
L[low] = temp;
returnlow;
}
voidQSort (int*L,intlow,int high)
{
if (low < high)
{
intpl = Partition (L,low,high);
QSort (L,low,pl - 1);
QSort (L,pl + 1,high);
}
}
intmain ()
{
intnarry[100],addr[100];
intsum = 1,t;
cout << "Input number:" << endl;
cin >> t;
while (t != -1)
{
narry[sum] = t;
addr[sum - 1] = t;
sum++;
cin >> t;
}
sum -= 1;
QSort (narry,1,sum);
for (int i = 1; i <= sum;i++)
cout << narry[i] << '\t';
cout << endl;
intk;
cout << "Please input place you want:" << endl;
cin >> k;
intaa = 1;
intkk = 0;
for (;;)
{
if (aa == k)
break;
if (narry[kk] != narry[kk + 1])
{
aa += 1;
kk++;
}
}
cout << "The NO." << k << "number is:" << narry[sum - kk] << endl;
cout << "And it's place is:" ;
for (i = 0;i < sum;i++)
{
if (addr[i] == narry[sum - kk])
cout << i << '\t';
}
return0;
}
五、兩路歸併排序
Linklist *unio(Linklist *p,Linklist *q){
linklist *R,*pa,*qa,*ra;
pa=p;
qa=q;
R=ra=p;
while(pa->next!=NULL&&qa->next!=NULL){
if(pa->data>qa->data){
ra->next=qa;
qa=qa->next;
}
else{
ra->next=pa;
pa=pa->next;
}
}
if(pa->next!=NULL)
ra->next=pa;
if(qa->next!=NULL)
ra->next==qa;
return R;
}
六、用遞歸算法判斷數組a[N]是否爲一個遞增數組。
遞歸的方法,記錄當前最大的,而且判斷當前的是否比這個還大,大則繼續,不然返回false結束:
bool fun( int a[], int n )
{
if( n= =1 )
return true;
if( n= =2 )
return a[n-1] >= a[n-2];
return fun( a,n-1) && ( a[n-1] >= a[n-2] );
}
七、單連表的創建,把’a'–’z’26個字母插入到連表中,而且倒敘,還要打印!
方法1:
typedef struct val
{ int date_1;
struct val *next;
}*p;
void main(void)
{ char c;
for(c=122;c>=97;c–)
{ p.date=c;
p=p->next;
}
p.next=NULL;
}
}
方法2:
node *p = NULL;
node *q = NULL;
node *head = (node*)malloc(sizeof(node));
head->data = ‘ ‘;head->next=NULL;
node *first = (node*)malloc(sizeof(node));
first->data = ‘a’;first->next=NULL;head->next = first;
p = first;
int longth = ‘z’ – ‘b’;
int i=0;
while ( i<=longth )
{
node *temp = (node*)malloc(sizeof(node));
temp->data = ‘b’+i;temp->next=NULL;q=temp;
head->next = temp; temp->next=p;p=q;
i++;
}
print(head);
八、請列舉一個軟件中時間換空間或者空間換時間的例子。
void swap(int a,int b)
{
int c; c=a;a=b;b=a;
}
—>空優
void swap(int a,int b)
{
a=a+b;b=a-b;a=a-b;
}
九、outputstr所指的值爲123456789
int continumax(char *outputstr, char *inputstr)
{
char *in = inputstr, *out = outputstr, *temp, *final;
int count = 0, maxlen = 0;
while( *in != ‘\0′ )
{
if( *in > 47 && *in < 58 )
{
for(temp = in; *in > 47 && *in < 58 ; in++ )
count++;
}
else
in++;
if( maxlen < count )
{
maxlen = count;
count = 0;
final = temp;
}
}
for(int i = 0; i < maxlen; i++)
{
*out = *final;
out++;
final++;
}
*out = '\0';
return maxlen;
}
十、不用庫函數,用C語言實現將一整型數字轉化爲字符串
方法1:
int getlen(char *s){
int n;
for(n = 0; *s != '\0'; s++)
n++;
return n;
}
void reverse(char s[])
{
int c,i,j;
for(i = 0,j = getlen(s) - 1; i < j; i++,j--){
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
void itoa(int n,char s[])
{
int i,sign;
if((sign = n) < 0)
n = -n;
i = 0;
do{/*以反序生成數字*/
s[i++] = n%10 + '0';/*get next number*/
}while((n /= 10) > 0);/*delete the number*/
if(sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
方法2:
#include
using namespace std;
void itochar(int num);
void itochar(int num)
{
int i = 0;
int j ;
char stra[10];
char strb[10];
while ( num )
{
stra[i++]=num%10+48;
num=num/10;
}
stra[i] = ‘\0′;
for( j=0; j < i; j++)
{
strb[j] = stra[i-j-1];
}
strb[j] = '\0';
cout<
}
int main()
{
int num;
cin>>num;
itochar(num);
return 0;
}
十一、求組合數: 求n個數(1….n)中k個數的組合….
如:combination(5,3)
要求輸出:543,542,541,532,531,521,432,431,421,321,
#include
int pop(int *);
int push(int );
void combination(int ,int );
int stack[3]={0};
top=-1;
int main()
{
int n,m;
printf(「Input two numbers:\n」);
while( (2!=scanf(「%d%*c%d」,&n,&m)) )
{
fflush(stdin);
printf(「Input error! Again:\n」);
}
combination(n,m);
printf(「\n」);
}
void combination(int m,int n)
{
int temp=m;
push(temp);
while(1)
{
if(1==temp)
{
if(pop(&temp)&&stack[0]==n) //當棧底元素彈出&&爲可能取的最小值,循環退出
break;
}
else if( push(–temp))
{
printf(「%d%d%d 「,stack[0],stack[1],stack[2]);//§?¨ì¤@?
pop(&temp);
}
}
}
int push(int i)
{
stack[++top]=i;
if(top<2)
return 0;
else
return 1;
}
int pop(int *i)
{
*i=stack[top--];
if(top>=0)
return 0;
else
return 1;
}
十二、用指針的方法,將字符串「ABCD1234efgh」先後對調顯示
#include
#include
#include
int main()
{
char str[] = 「ABCD1234efgh」;
int length = strlen(str);
char * p1 = str;
char * p2 = str + length – 1;
while(p1 < p2)
{
char c = *p1;
*p1 = *p2;
*p2 = c;
++p1;
--p2;
}
printf("str now is %s\n",str);
system("pause");
return 0;
}
1三、有一分數序列:1/2,1/4,1/6,1/8……,用函數調用的方法,求此數列前20項的和
#include
double getValue()
{
double result = 0;
int i = 2;
while(i < 42)
{
result += 1.0 / i;//必定要使用1.0作除數,不能用1,不然結果將自動轉化成整數,即0.000000
i += 2;
}
return result;
}
int main()
{
printf("result is %f\n", getValue());
system("pause");
return 0;
}
1四、有一個數組a[1000]存放0--1000;要求每隔二個數刪掉一個數,到末尾時循環至開頭繼續進行,求最後一個被刪掉的數的原始下標位置。
以7個數爲例:
{0,1,2,3,4,5,6,7} 0-->1–>2(刪除)–>3–>4–>5(刪除)–>6–>7–>0(刪除),如此循環直到最後一個數被刪除。
方法1:數組
#include
using namespace std;
#define null 1000
int main()
{
int arr[1000];
for (int i=0;i<1000;++i)
arr[i]=i;
int j=0;
int count=0;
while(count<999)
{
while(arr[j%1000]==null)
j=(++j)%1000;
j=(++j)%1000;
while(arr[j%1000]==null)
j=(++j)%1000;
j=(++j)%1000;
while(arr[j%1000]==null)
j=(++j)%1000;
arr[j]=null;
++count;
}
while(arr[j]==null)
j=(++j)%1000;
cout< return 0;
}方法2:鏈表
#include
using namespace std;
#define null 0
struct node
{
int data;
node* next;
};
int main()
{
node* head=new node;
head->data=0;
head->next=null;
node* p=head;
for(int i=1;i<1000;i++)
{
node* tmp=new node;
tmp->data=i;
tmp->next=null;
head->next=tmp;
head=head->next;
}
head->next=p;
while(p!=p->next)
{
p->next->next=p->next->next->next;
p=p->next->next;
}
cout< data;
return 0;
}
方法3:通用算法
#include
#define MAXLINE 1000 //元素個數
/*
MAXLINE 元素個數
a[] 元素數組
R[] 指針場
suffix 下標
index 返回最後的下標序號
values 返回最後的下標對應的值
start 從第幾個開始
K 間隔
*/
int find_n(int a[],int R[],int K,int& index,int& values,int s=0) {
int suffix;
int front_node,current_node;
suffix=0;
if(s==0) {
current_node=0;
front_node=MAXLINE-1;
}
else {
current_node=s;
front_node=s-1;
}
while(R[front_node]!=front_node) {
printf(「%d\n」,a[current_node]);
R[front_node]=R[current_node];
if(K==1) {
current_node=R[front_node];
continue;
}
for(int i=0;i front_node=R[front_node];
}
current_node=R[front_node];
}
index=front_node;
values=a[front_node];
return 0;
}
int main(void) {
int a[MAXLINE],R[MAXLINE],suffix,index,values,start,i,K;
suffix=index=values=start=0;
K=2;
for(i=0;i a[i]=i;
R[i]=i+1;
}
R[i-1]=0;
find_n(a,R,K,index,values,2);
printf("the value is %d,%d\n",index,values);
return 0;
}
1五、實現strcmp
int StrCmp(const char *str1, const char *str2)
作是作對了,沒有抄搞,比較亂
int StrCmp(const char *str1, const char *str2)
{
assert(str1 && srt2);
while (*str1 && *str2 && *str1 == *str2) {
str1++, str2++;
}
if (*str1 && *str2)
return (*str1-*str2);
elseif (*str1 && *str2==0)
return 1;
elseif (*str1 = = 0 && *str2)
return -1;
else
return 0;
}
int StrCmp(const char *str1, const char *str2)
{
//省略判斷空指針(本身保證)
while(*str1 && *str1++ = = *str2++);
return *str1-*str2;
}
1六、實現子串定位
int FindSubStr(const char *MainStr, const char *SubStr)
作是作對了,沒有抄搞,比較亂
int MyStrstr(const char* MainStr, const char* SubStr)
{
const char *p;
const char *q;
const char * u = MainStr;
//assert((MainStr!=NULL)&&( SubStr!=NULL));//用斷言對輸入進行判斷
while(*MainStr) //內部進行遞增
{
p = MainStr;
q = SubStr;
while(*q && *p && *p++ == *q++);
if(!*q )
{
return MainStr - u +1 ;//MainStr指向當前起始位,u指向
}
MainStr ++;
}
return -1;
}
1七、已知一個單向鏈表的頭,請寫出刪除其某一個結點的算法,要求,先找到此結點,而後刪除。
slnodetype *Delete(slnodetype *Head,int key){}中if(Head->number==key)
{
Head=Pointer->next;
free(Pointer);
break;
}
Back = Pointer;
Pointer=Pointer->next;
if(Pointer->number==key)
{
Back->next=Pointer->next;
free(Pointer);
break;
}
void delete(Node* p)
{
if(Head = Node)
while(p)
}
1八、有1,2,….一直到n的無序數組,求排序算法,而且要求時間複雜度爲O(n),空間複雜度O(1),使用交換,並且一次只能交換兩個數.(華爲)
#include
int main()
{
int a[] = {10,6,9,5,2,8,4,7,1,3};
int len = sizeof(a) / sizeof(int);
int temp;
for(int i = 0; i < len; )
{
temp = a[a[i] - 1];
a[a[i] - 1] = a[i];
a[i] = temp;
if ( a[i] == i + 1)
i++;
}
for (int j = 0; j < len; j++)
cout<
return 0;
}
1九、寫出程序把一個鏈表中的接點順序倒排
typedef struct linknode
{
int data;
struct linknode *next;
}node;
//將一個鏈表逆置
node *reverse(node *head)
{
node *p,*q,*r;
p=head;
q=p->next;
while(q!=NULL)
{
r=q->next;
q->next=p;
p=q;
q=r;
}
head->next=NULL;
head=p;
return head;
}
20、寫出程序刪除鏈表中的全部接點
void del_all(node *head)
{
node *p;
while(head!=NULL)
{
p=head->next;
free(head);
head=p;
}
cout<<"釋放空間成功!"< }
2一、兩個字符串,s,t;把t字符串插入到s字符串中,s字符串有足夠的空間存放t字符串
void insert(char *s, char *t, int i)
{
char *q = t;
char *p =s;
if(q == NULL)return;
while(*p!='\0')
{
p++;
}
while(*q!=0)
{
*p=*q;
p++;
q++;
}
*p = '\0';
}
2二、寫一個函數,功能:完成內存之間的拷貝
memcpy source code:
270 void* memcpy( void *dst, const void *src, unsigned int len )
271 {
272 register char *d;
273 register char *s;
27
275 if (len == 0)
276 return dst;
277
278 if (is_overlap(dst, src, len, len))
279 complain3("memcpy", dst, src, len);
280
281 if ( dst > src ) {
282 d = (char *)dst + len – 1;
283 s = (char *)src + len – 1;
284 while ( len >= 4 ) {
285 *d– = *s–;
286 *d– = *s–;
287 *d– = *s–;
288 *d– = *s–;
289 len -= 4;
290 }
291 while ( len– ) {
292 *d– = *s–;
293 }
294 } else if ( dst < src ) {
295 d = (char *)dst;
296 s = (char *)src;
297 while ( len >= 4 ) {
298 *d++ = *s++;
299 *d++ = *s++;
300 *d++ = *s++;
301 *d++ = *s++;
302 len -= 4;
303 }
304 while ( len– ) {
305 *d++ = *s++;
306 }
307 }
308 return dst;
309 }
2三、公司考試這種題目主要考你編寫的代碼是否考慮到各類狀況,是否安全(不會溢出)
各類狀況包括:
1、參數是指針,檢查指針是否有效
2、檢查複製的源目標和目的地是否爲同一個,若爲同一個,則直接跳出
3、讀寫權限檢查
4、安全檢查,是否會溢出
memcpy拷貝一塊內存,內存的大小你告訴它
strcpy是字符串拷貝,遇到’\0′結束
/* memcpy ─── 拷貝不重疊的內存塊 */
void memcpy(void* pvTo, void* pvFrom, size_t size)
{
void* pbTo = (byte*)pvTo;
void* pbFrom = (byte*)pvFrom;
ASSERT(pvTo != NULL && pvFrom != NULL); //檢查輸入指針的有效性
ASSERT(pbTo>=pbFrom+size || pbFrom>=pbTo+size);//檢查兩個指針指向的內存是否重疊
while(size–>0)
*pbTo++ == *pbFrom++;
return(pvTo);
}
2四、兩個字符串,s,t;把t字符串插入到s字符串中,s字符串有足夠的空間存放t字符串
void insert(char *s, char *t, int i)
{
memcpy(&s[strlen(t)+i],&s[i],strlen(s)-i);
memcpy(&s[i],t,strlen(t));
s[strlen(s)+strlen(t)]=’\0′;
}
2五、編寫一個 C 函數,該函數在一個字符串中找到可能的最長的子字符串,且該字符串是由同一字符組成的。
char * search(char *cpSource, char ch)
{
char *cpTemp=NULL, *cpDest=NULL;
int iTemp, iCount=0;
while(*cpSource)
{
if(*cpSource == ch)
{
iTemp = 0;
cpTemp = cpSource;
while(*cpSource == ch)
++iTemp, ++cpSource;
if(iTemp > iCount)
iCount = iTemp, cpDest = cpTemp;
if(!*cpSource)
break;
}
++cpSource;
}
return cpDest;
}
2六、請編寫一個 C 函數,該函數在給定的內存區域搜索給定的字符,並返回該字符所在位置索引值。
int search(char *cpSource, int n, char ch)
{
int i;
for(i=0; i return i;
}
2七、給定字符串A和B,輸出A和B中的最大公共子串。
好比A="aocdfe" B="pmcdfa" 則輸出"cdf"
*/
//Author: azhen
#include
#include
#include
char *commanstring(char shortstring[], char longstring[])
{
int i, j;
char *substring=malloc(256);
if(strstr(longstring, shortstring)!=NULL) //若是……,那麼返回shortstring
return shortstring;
for(i=strlen(shortstring)-1;i>0; i–) //不然,開始循環計算
{
for(j=0; j<=strlen(shortstring)-i; j++){
memcpy(substring, &shortstring[j], i);
substring[i]='\0';
if(strstr(longstring, substring)!=NULL)
return substring;
}
}
return NULL;
}
main()
{
char *str1=malloc(256);
char *str2=malloc(256);
char *comman=NULL;
gets(str1);
gets(str2);
if(strlen(str1)>strlen(str2)) //將短的字符串放前面
comman=commanstring(str2, str1);
else
comman=commanstring(str1, str2);
printf(「the longest comman string is: %s\n」, comman);
}
2八、寫一個函數比較兩個字符串str1和str2的大小,若相等返回0,若str1大於
str2返回1,若str1小於str2返回-1
int strcmp ( const char * src,const char * dst)
{
int ret = 0 ;
while( ! (ret = *(unsigned char *)src – *(unsigned char *)dst) && *dst)
{
++src;
++dst;
}
if ( ret < 0 )
ret = -1 ;
else if ( ret > 0 )
ret = 1 ;
return( ret );
}
2九、求1000!的未尾有幾個0(用素數相乘的方法來作,如72=2*2*2*3*3);
求出1->1000裏,能被5整除的數的個數n1,能被25整除的數的個數n2,能被125整除的數的個數n3,
能被625整除的數的個數n4.
1000!末尾的零的個數=n1+n2+n3+n4;
#include
#define NUM 1000
int find5(int num){
int ret=0;
while(num%5==0){
num/=5;
ret++;
}
return ret;
}
int main(){
int result=0;
int i;
for(i=5;i<=NUM;i+=5)
{
result+=find5(i);
}
printf(" the total zero number is %d\n",result);
return 0;
}
30、有雙向循環鏈表結點定義爲:
struct node
{ int data;
struct node *front,*next;
};
有兩個雙向循環鏈表A,B,知道其頭指針爲:pHeadA,pHeadB,請寫一函數將兩鏈表中data值相同的結點刪除
BOOL DeteleNode(Node *pHeader, DataType Value)
{
if (pHeader == NULL) return;
BOOL bRet = FALSE;
Node *pNode = pHead;
while (pNode != NULL)
{
if (pNode->data == Value)
{
if (pNode->front == NULL)
{
pHeader = pNode->next;
pHeader->front = NULL;
}
else
{
if (pNode->next != NULL)
{
pNode->next->front = pNode->front;
}
pNode->front->next = pNode->next;
}
Node *pNextNode = pNode->next;
delete pNode;
pNode = pNextNode;
bRet = TRUE;
//不要break或return, 刪除全部
}
else
{
pNode = pNode->next;
}
}
return bRet;
}
void DE(Node *pHeadA, Node *pHeadB)
{
if (pHeadA == NULL || pHeadB == NULL)
{
return;
}
Node *pNode = pHeadA;
while (pNode != NULL)
{
if (DeteleNode(pHeadB, pNode->data))
{
if (pNode->front == NULL)
{
pHeadA = pNode->next;
pHeadA->front = NULL;
}
else
{
pNode->front->next = pNode->next;
if (pNode->next != NULL)
{
pNode->next->front = pNode->front;
}
}
Node *pNextNode = pNode->next;
delete pNode;
pNode = pNextNode;
}
else
{
pNode = pNode->next;
}
}
}
3一、編程實現:找出兩個字符串中最大公共子字符串,如」abccade」,」dgcadde」的最大子串爲」cad」
int GetCommon(char *s1, char *s2, char **r1, char **r2)
{
int len1 = strlen(s1);
int len2 = strlen(s2);
int maxlen = 0;
for(int i = 0; i < len1; i++)
{
for(int j = 0; j < len2; j++)
{
if(s1[i] == s2[j])
{
int as = i, bs = j, count = 1;
while(as + 1 < len1 && bs + 1 < len2 && s1[++as] == s2[++bs])
count++;
if(count > maxlen)
{
maxlen = count;
*r1 = s1 + i;
*r2 = s2 + j;
}
}
}
}
3二、編程實現:把十進制數(long型)分別以二進制和十六進制形式輸出,不能使用printf系列庫函數
char* test3(long num) {
char* buffer = (char*)malloc(11);
buffer[0] = ’0′;
buffer[1] = ‘x’;
buffer[10] = ‘\0′;
char* temp = buffer + 2;
for (int i=0; i < 8; i++) {
temp[i] = (char)(num<<4*i>>28);
temp[i] = temp[i] >= 0 ? temp[i] : temp[i] + 16;
temp[i] = temp[i] < 10 ? temp[i] + 48 : temp[i] + 55;
}
return buffer;
}
3三、輸入N, 打印 N*N 矩陣
好比 N = 3,打印:
1 2 3
8 9 4
7 6 5
N = 4,打印:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
解答:
1 #define N 15
int s[N][N];
void main()
{
int k = 0, i = 0, j = 0;
int a = 1;
for( ; k < (N+1)/2; k++ )
{
while( j < N-k ) s[i][j++] = a++; i++; j--;
while( i < N-k ) s[i++][j] = a++; i--; j--;
while( j > k-1 ) s[i][j--] = a++; i–; j++;
while( i > k ) s[i--][j] = a++; i++; j++;
}
for( i = 0; i < N; i++ )
{
for( j = 0; j < N; j++ )
cout << s[i][j] << '\t';
cout << endl;
}
}
2 define MAX_N 100
int matrix[MAX_N][MAX_N];
/*
*(x,y):第一個元素的座標
* start:第一個元素的值
* n:矩陣的大小
*/
void SetMatrix(int x, int y, int start, int n) {
int i, j;
if (n <= 0) //遞歸結束條件
return;
if (n == 1) { //矩陣大小爲1時
matrix[x][y] = start;
return;
}
for (i = x; i < x + n-1; i++) //矩陣上部
matrix[y][i] = start++;
for (j = y; j < y + n-1; j++) //右部
matrix[j][x+n-1] = start++;
for (i = x+n-1; i > x; i–) //底部
matrix[y+n-1][i] = start++;
for (j = y+n-1; j > y; j–) //左部
matrix[j][x] = start++;
SetMatrix(x+1, y+1, start, n-2); //遞歸
}
void main() {
int i, j;
int n;
scanf(「%d」, &n);
SetMatrix(0, 0, 1, n);
//打印螺旋矩陣
for(i = 0; i < n; i++) {
for (j = 0; j < n; j++)
printf("%4d", matrix[i][j]);
printf("\n");
}
}
3四、斐波拉契數列遞歸實現的方法以下:
int Funct( int n )
{
if(n==0) return 1;
if(n==1) return 1;
retrurn Funct(n-1) + Funct(n-2);
}
請問,如何不使用遞歸,來實現上述函數?
請教各位高手!
解答:int Funct( int n ) // n 爲非負整數
{
int a=0;
int b=1;
int c;
if(n==0) c=1;
else if(n==1) c=1;
else for(int i=2;i<=n;i++) //應該n從2開始算起
{
c=a+b;
a=b;
b=c;
}
return c;
}
解答:
如今大多數系統都是將低字位放在前面,而結構體中位域的申明通常是先聲明高位。
100 的二進制是 001 100 100
低位在前 高位在後
001----s3
100----s2
100----s1
因此結果應該是 1
若是先申明的在低位則:
001----s1
100----s2
100----s3
結果是 4
一、原題跟little-endian,big-endian沒有關係
二、原題跟位域的存儲空間分配有關,究竟是從低字節分配仍是從高字節分配,從Dev C++和VC7.1上看,都是從低字節開始分配,而且連續分配,中間不空,不像譚的書那樣會留空位
三、原題跟編譯器有關,編譯器在未用堆棧空間的默認值分配上有所不一樣,Dev C++未用空間分配爲
01110111b,VC7.1下爲11001100b,因此在Dev C++下的結果爲5,在VC7.1下爲1。
注:PC通常採用little-endian,即高高低低,但在網絡傳輸上,通常採用big-endian,即高低低高,華爲是作網絡的,因此可能考慮big-endian模式,這樣輸出結果可能爲4
3五、判斷一個字符串是否是迴文
int IsReverseStr(char *aStr)
{
int i,j;
int found=1;
if(aStr==NULL)
return -1;
j=strlen(aStr);
for(i=0;i if(*(aStr+i)!=*(aStr+j-i-1))
{
found=0;
break;
}
return found;
}
3六、Josephu 問題爲:設編號爲1,2,… n的n我的圍坐一圈,約定編號爲k(1<=k<=n)的人從1開始報數,數到m 的那我的出列,它的下一位又從1開始報數,數到m的那我的又出列,依次類推,直到全部人出列爲止,由此產生一個出隊編號的序列。
數組實現:
#include
#include
int Josephu(int n, int m)
{
int flag, i, j = 0;
int *arr = (int *)malloc(n * sizeof(int));
for (i = 0; i < n; ++i)
arr[i] = 1;
for (i = 1; i < n; ++i)
{
flag = 0;
while (flag < m)
{
if (j == n)
j = 0;
if (arr[j])
++flag;
++j;
}
arr[j - 1] = 0;
printf("第%4d個出局的人是:%4d號\n", i, j);
}
free(arr);
return j;
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
printf("最後勝利的是%d號!\n", Josephu(n, m));
system("pause");
return 0;
}
鏈表實現:
#include
#include
typedef struct Node
{
int index;
struct Node *next;
}JosephuNode;
int Josephu(int n, int m)
{
int i, j;
JosephuNode *head, *tail;
head = tail = (JosephuNode *)malloc(sizeof(JosephuNode));
for (i = 1; i < n; ++i)
{
tail->index = i;
tail->next = (JosephuNode *)malloc(sizeof(JosephuNode));
tail = tail->next;
}
tail->index = i;
tail->next = head;
for (i = 1; tail != head; ++i)
{
for (j = 1; j < m; ++j)
{
tail = head;
head = head->next;
}
tail->next = head->next;
printf(「第%4d個出局的人是:%4d號\n」, i, head->index);
free(head);
head = tail->next;
}
i = head->index;
free(head);
return i;
}
int main()
{
int n, m;
scanf(「%d%d」, &n, &m);
printf(「最後勝利的是%d號!\n」, Josephu(n, m));
system(「pause」);
return 0;
}
3七、已知strcpy函數的原型是:
char * strcpy(char * strDest,const char * strSrc);
1.不調用庫函數,實現strcpy函數。
2.解釋爲何要返回char *。
解說:
1.strcpy的實現代碼
char * strcpy(char * strDest,const char * strSrc)
{
if ((strDest==NULL)||(strSrc==NULL)) ]
throw 「Invalid argument(s)」; //[2]
char * strDestCopy=strDest; ]
while ((*strDest++=*strSrc++)!=’\0′); ]
return strDestCopy;
}
錯誤的作法:
[1]
(A)不檢查指針的有效性,說明答題者不注重代碼的健壯性。
(B)檢查指針的有效性時使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),說明答題者對C語言中類型的隱式轉換沒有深入認識。在本例中char *轉換爲bool便是類型隱式轉換,這種功能雖然靈活,但更多的是致使出錯機率增大和維護成本升高。因此C++專門增長了bool、true、false三個關鍵字以提供更安全的條件表達式。
(C)檢查指針的有效性時使用((strDest==0)||(strSrc==0)),說明答題者不知道使用常量的好處。直接使用字面常量(如本例中的0)會減小程序的可維護性。0雖然簡單,但程序中可能出現不少處對指針的檢查,萬一出現筆誤,編譯器不能發現,生成的程序內含邏輯錯誤,很難排除。而使用NULL代替0,若是出現拼寫錯誤,編譯器就會檢查出來。
[2]
(A)return new string(「Invalid argument(s)」);,說明答題者根本不知道返回值的用途,而且他對內存泄漏也沒有警戒心。從函數中返回函數體內分配的內存是十分危險的作法,他把釋放內存的義務拋給不知情的調用者,絕大多數狀況下,調用者不會釋放內存,這致使內存泄漏。
(B)return 0;,說明答題者沒有掌握異常機制。調用者有可能忘記檢查返回值,調用者還可能沒法檢查返回值(見後面的鏈式表達式)。妄想讓返回值肩負返回正確值和異常值的雙重功能,其結果每每是兩種功能都失效。應該以拋出異常來代替返回值,這樣能夠減輕調用者的負擔、使錯誤不會被忽略、加強程序的可維護性。
[3]
(A)忘記保存原始的strDest值,說明答題者邏輯思惟不嚴密。
[4]
(A)循環寫成while (*strDest++=*strSrc++);,同[1](B)。
(B)循環寫成while (*strSrc!=’\0′) *strDest++=*strSrc++;,說明答題者對邊界條件的檢查不力。循環體結束後,strDest字符串的末尾沒有正確地加上’\0′。
第四部分:附加部分
一、位域 :
有些信息在存儲時,並不須要佔用一個完整的字節, 而只需佔幾個或一個二進制位。例如在存放一個開關量時,只有0和1 兩種狀態, 用一位二進位便可。爲了節省存儲空間,並使處理簡便,C語言又提供了一種數據結構,稱爲「位域」或「位段」。所謂「位域」是把一個字節中的二進位劃分爲幾個不一樣的區域, 並說明每一個區域的位數。每一個域有一個域名,容許在程序中按域名進行操做。 這樣就能夠把幾個不一樣的對象用一個字節的二進制位域來表示。1、位域的定義和位域變量的說明位域定義與結構定義相仿,其形式爲:
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、位域的使用位域的使用和結構成員的使用相同,其通常形式爲: 位域變量名?位域名 位域容許用各類格式輸出。
main(){
struct bs
{
unsigned a:1;
unsigned b:3;
unsigned c:4;
} bit,*pbit;
bit.a=1;
bit.b=7;
bit.c=15;
pri
改錯:
#include
int main(void) {
int **p;
int arr[100];
p = &arr;
return 0;
}
解答:
搞錯了,是指針類型不一樣,
int **p; //二級指針
&arr; //獲得的是指向第一維爲100的數組的指針
#include
int main(void) {
int **p, *q;
int arr[100];
q = arr;
p = &q;
return 0;
}
轉自:http://hi.baidu.com/qianlizhiwai01/blog/item/9d2ef0f4490370f07709d73f.html
文件描述符&文件指針
在linux系統中把設備和普通文件也都看作是文件,要對文件進行操做就必須先打開文件,打開文件後會獲得一個文件描述符,它是一個很小的正整數,是一個索引值。
內核會爲每個運行中的進程在進程控制塊pcb中維護一個打開文件的記錄表,每個表項都有一個指針指向打開的文件,上邊的索引值是記錄表的索引值。
文件描述符的優勢:兼容POSIX標準,許多系統調用都依賴於它;缺點是不能移植到unix以外的系統上去。
文件指針:c語言中使用的是文件指針而不是文件描述符來做爲I/O的句柄,文件指針指向進程的用戶空間中一個FILE結構的數據結構,FILE結構裏主要包括一個I/O緩衝區和一個文件描述符,而文件描述符值是文件描述符表中的一個索引,從某種意義上將文件指針就是句柄的句柄(在Window中文件描述符被稱爲文件句柄)。
文件指針的優勢:是c語言中的通用格式,便於移植。
既然FILE結構中含有文件描述符,那麼能夠使用fopen來得到文件指針,而後從文件指針獲取文件描述符,文件描述符應該是惟一的,而文件指針卻不是惟一的,但指向的對象是惟一的。
C語言文件指針域文件描述符之間能夠相互轉換
int fileno(FILE * stream)
FILE * fdopen(int fd, const char * mode)
FILE的結構
struct _iobuf {
char *_ptr; //緩衝區當前指針
int _cnt;
char *_base; //緩衝區基址
int _flag; //文件讀寫模式
int _file; //文件描述符
int _charbuf; //緩衝區剩餘本身個數
int _bufsiz; //緩衝區大小
char *_tmpfname;
};
typedef struct _iobuf FILE;
open和fopen的區別
| open | fopen |
|----------------------------+----------------------------- |
| open返回一個文件描述符 | 返回FILE * |
| 無緩衝 | 有緩衝 |
| 與write()和read()配合使用 | 與fwrite()和fread()配合使用 |
int fd = open("TAGS", O_RDONLY);
FILE * fp = fopen("TAGS", "r");
printf("%d %d\n", fd, fp->_file);
能夠從文件指針中獲取文件描述符
fileno(fp) 和fp->_file效果是同樣的
FILE * fp = fdopen(fd, "r") 根據文件描述符加上訪問模式能夠獲得文件指針
2系統調用&庫函數
3 線程&進程
4 文件的類型有哪些,用途
Linux文件通常是用一些相關的應用程序建立,好比圖像工具、文檔工具、歸檔工具... .... 或 cp工具等。linux文件的刪除方式是用rm 命令。
Linux文件類型;
Linux文件類型和Linux文件的文件名所表明的意義是兩個不一樣的概念。咱們經過通常應用程序而建立的好比file.txt、file.tar.gz ,這些文件雖然要用不一樣的程序來打開,但放在Linux文件類型中衡量的話,大可能是常規文件(也被稱爲普通文件)。
Linux文件類型常見的有:普通文件、目錄、字符設備文件、塊設備文件、符號連接文件等;如今咱們進行一個簡要的說明;
1 普通文件
[root@localhost ~]# ls -lh install.log
-rw-r--r-- 1 root root 53K 03-16 08:54 install.log
我 們用 ls -lh 來查看某個文件的屬性,能夠看到有相似 -rw-r--r-- ,值得注意的是第一個符號是 - ,這樣的文件在Linux中就是普通文件。這些文件通常是用一些相關的應用程序建立,好比圖像工具、文檔工具、歸檔工具... .... 或 cp工具等。這類文件的刪除方式是用rm 命令;
2 目錄
[root@localhost ~]# ls -lh
總計 14M
-rw-r--r-- 1 root root 2 03-27 02:00 fonts.scale
-rw-r--r-- 1 root root 53K 03-16 08:54 install.log
-rw-r--r-- 1 root root 14M 03-16 07:53 kernel-6.15-1.2025_FC5.i686.rpm
drwxr-xr-x 2 1000 users 4.0K 04-04 23:30 mkuml-2004.07.17
drwxr-xr-x 2 root root 4.0K 04-19 10:53 mydir
drwxr-xr-x 2 root root 4.0K 03-17 04:25 Public
當 咱們在某個目錄下執行,看到有相似 drwxr-xr-x ,這樣的文件就是目錄,目錄在Linux是一個比較特殊的文件。注意它的第一個字符是d。建立目錄的命令能夠用 mkdir 命令,或cp命令,cp能夠把一個目錄複製爲另外一個目錄。刪除用rm 或rmdir命令。
3 字符設備或塊設備文件
如時您進入/dev目錄,列一下文件,會看到相似以下的;
[root@localhost ~]# ls -la /dev/tty
crw-rw-rw- 1 root tty 5, 0 04-19 08:29 /dev/tty
[root@localhost ~]# ls -la /dev/hda1
brw-r----- 1 root disk 3, 1 2006-04-19 /dev/hda1
咱們看到/dev/tty的屬性是 crw-rw-rw- ,注意前面第一個字符是 c ,這表示字符設備文件。好比貓等串口設備
咱們看到 /dev/hda1 的屬性是 brw-r----- ,注意前面的第一個字符是b,這表示塊設備,好比硬盤,光驅等設備;
這個種類的文件,是用mknode來建立,用rm來刪除。目前在最新的Linux發行版本中,咱們通常不用本身來建立設備文件。由於這些文件是和內核相關聯的。
4 套接口文件
當咱們啓動MySQL服務器時,會產生一個mysql.sock的文件。
[root@localhost ~]# ls -lh /var/lib/mysql/mysql.sock
srwxrwxrwx 1 mysql mysql 0 04-19 11:12 /var/lib/mysql/mysql.sock
注意這個文件的屬性的第一個字符是 s。咱們瞭解一下就好了。
5 符號連接文件
[root@localhost ~]# ls -lh setup.log
lrwxrwxrwx 1 root root 11 04-19 11:18 setup.log -> install.log
當 咱們查看文件屬性時,會看到有相似 lrwxrwxrwx,注意第一個字符是l,這類文件是連接文件。是經過ln -s 源文件名 新文件名。上面是一個例子,表示setup.log是install.log的軟連接文件。怎麼理解呢?這和Windows操做系統中的快捷方式有點相 似。
符號連接文件的建立方法舉例;
[root@localhost ~]# ls -lh kernel-6.15-1.2025_FC5.i686.rpm
-rw-r--r-- 1 root root 14M 03-16 07:53 kernel-6.15-1.2025_FC5.i686.rpm
[root@localhost ~]# ln -s kernel-6.15-1.2025_FC5.i686.rpm kernel.rpm
[root@localhost ~]# ls -lh kernel*
-rw-r--r-- 1 root root 14M 03-16 07:53 kernel-6.15-1.2025_FC5.i686.rpm
lrwxrwxrwx 1 root root 33 04-19 11:27 kernel.rpm -> kernel-6.15-1.2025_FC5.i686.rpm
5 虛擬地址&線性地址&物理地址,他們之間如何轉化
邏輯地址(Logical Address) 是指由程序產生的與段相關的偏移地址部分。例如,你在進行C語言指針編程中,能夠讀取指針變量自己值(&操做),實際上這個值就是邏輯地址,它是相對於你當前進程數據段的地址,不和絕對物理地址相干。只有在Intel實模式下,邏輯地址才和物理地址相等(由於實模式沒有分段或分頁機制,Cpu不進行自動地址轉換);邏輯也就是在Intel 保護模式下程序執行代碼段限長內的偏移地址(假定代碼段、數據段若是徹底同樣)。應用程序員僅需與邏輯地址打交道,而分段和分頁機制對您來講是徹底透明的,僅由系統編程人員涉及。應用程序員雖然本身能夠直接操做內存,那也只能在操做系統給你分配的內存段操做。
線性地址(Linear Address) 是邏輯地址到物理地址變換之間的中間層。程序代碼會產生邏輯地址,或者說是段中的偏移地址,加上相應段的基地址就生成了一個線性地址。若是啓用了分頁機制,那麼線性地址能夠再經變換以產生一個物理地址。若沒有啓用分頁機制,那麼線性地址直接就是物理地址。Intel 80386的線性地址空間容量爲4G(2的32次方即32根地址總線尋址)。
物理地址(Physical Address) 是指出如今CPU外部地址總線上的尋址物理內存的地址信號,是地址變換的最終結果地址。若是啓用了分頁機制,那麼線性地址會使用頁目錄和頁表中的項變換成物理地址。若是沒有啓用分頁機制,那麼線性地址就直接成爲物理地址了。
虛擬內存(Virtual Memory) 是指計算機呈現出要比實際擁有的內存大得多的內存量。所以它容許程序員編制並運行比實際系統擁有的內存大得多的程序。這使得許多大型項目也可以在具備有限內存資源的系統上實現。一個很恰當的比喻是:你不須要很長的軌道就可讓一列火車從上海開到北京。你只須要足夠長的鐵軌(好比說3千米)就能夠完成這個任務。採起的方法是把後面的鐵軌馬上鋪到火車的前面,只要你的操做足夠快並能知足要求,列車就能象在一條完整的軌道上運行。這也就是虛擬內存管理須要完成的任務。在Linux 0.11內核中,給每一個程序(進程)都劃分了總容量爲64MB的虛擬內存空間。所以程序的邏輯地址範圍是0x0000000到0x4000000。
有時咱們也把邏輯地址稱爲虛擬地址。由於與虛擬內存空間的概念相似,邏輯地址也是與實際物理內存容量無關的。
邏輯地址與物理地址的「差距」是0xC0000000,是因爲虛擬地址->線性地址->物理地址映射正好差這個值。這個值是由操做系統指定的。
虛擬地址到物理地址的轉化方法是與體系結構相關的。通常來講有分段、分頁兩種方式。以如今的x86 cpu爲例,分段分頁都是支持的。Memory Mangement Unit負責從虛擬地址到物理地址的轉化。邏輯地址是段標識+段內偏移量的形式,MMU經過查詢段表,能夠把邏輯地址轉化爲線性地址。若是cpu沒有開啓分頁功能,那麼線性地址就是物理地址;若是cpu開啓了分頁功能,MMU還須要查詢頁表來將線性地址轉化爲物理地址:
邏輯地址 ----(段表)---> 線性地址 — (頁表)—> 物理地址
不一樣的邏輯地址能夠映射到同一個線性地址上;不一樣的線性地址也能夠映射到同一個物理地址上;因此是多對一的關係。另外,同一個線性地址,在發生換頁之後,也可能被從新裝載到另一個物理地址上。因此這種多對一的映射關係也會隨時間發生變化。
6 符號連接&硬連接
7 編譯&連接&解析&彙編
8 同步I/O&異步I/O
二 技術
1 多線程模型
2 進程和線程的優缺點、用途,並舉例說明
3 線程同步方式,各自用途
4 進程同步方式及各自優缺點
5 進程間通訊方式,各自用途
6 描述操做系統在提升系統性能和系統吞吐量採用的內存分配技術
7 以32爲系統4G進程空間爲例,分析進程空間的佈局
8 操做系統中大量使用緩存技術,描述Linux系統緩存的分類及各自的用途
9 請列舉進程調度中可能影響進程優先級的因素
10最新Linux內核(2.6.20)頁表分爲幾級,各級的含義及位數
三 設計
1共有4個線程,其中兩個線程負責從各自的文件讀取數據到公共的緩衝區,另兩個線程從緩衝區讀取數據做不一樣的處理(加和乘運算)。兩個數據文件的格式以下:
1.dat文件內容以下:
1 2
3 4
5 6
7 8
2.dat文件內容以下:
11 22
33 44
55 66
77 88
2 不用system函數實現Linux系統下time命令.
3 建立兩個進程A,B,其中A是父進程,B是子進程,A讀取文本文件內容,並傳給子進程B,B進程收到數據後將其打印輸出,這裏子進程B中用命令more來完成數據的接收和打印。【生產者消費者模型】
4 實現sleep庫函數。
5 約10億qq用戶,每一個用戶能夠設定100字符之內的我的簽名,請設計一個服務器如何高效支持用戶對簽名的查詢,平均每秒有1000個用戶請求。
1) Linux中主要有哪幾種內核鎖?
Linux的同步機制從2.0到2.6以來不斷髮展完善。從最初的原子操做,到後來的信號量,從大內核鎖到今天的自旋鎖。這些同步機制的發展伴隨Linux從單處理器到對稱多處理器的過渡;
伴隨着從非搶佔內核到搶佔內核的過分。Linux的鎖機制愈來愈有效,也愈來愈複雜。
Linux的內核鎖主要是自旋鎖和信號量。
自旋鎖最多隻能被一個可執行線程持有,若是一個執行線程試圖請求一個已被爭用(已經被持有)的自旋鎖,那麼這個線程就會一直進行忙循環——旋轉——等待鎖從新可用。要是鎖未被爭用,請求它的執行線程便能馬上獲得它而且繼續進行。自旋鎖能夠在任什麼時候刻防止多於一個的執行線程同時進入臨界區。
Linux中的信號量是一種睡眠鎖。若是有一個任務試圖得到一個已被持有的信號量時,信號量會將其推入等待隊列,而後讓其睡眠。這時處理器得到自由去執行其它代碼。當持有信號量的進程將信號量釋放後,在等待隊列中的一個任務將被喚醒,從而即可以得到這個信號量。
信號量的睡眠特性,使得信號量適用於鎖會被長時間持有的狀況;只能在進程上下文中使用,由於中斷上下文中是不能被調度的;另外當代碼持有信號量時,不能夠再持有自旋鎖。
Linux 內核中的同步機制:原子操做、信號量、讀寫信號量和自旋鎖的API,另一些同步機制,包括大內核鎖、讀寫鎖、大讀者鎖、RCU (Read-Copy Update,顧名思義就是讀-拷貝修改),和順序鎖。
2) Linux中的用戶模式和內核模式是什麼含意?
MS-DOS等操做系統在單一的CPU模式下運行,可是一些類Unix的操做系統則使用了雙模式,能夠有效地實現時間共享。在Linux機器上,CPU要麼處於受信任的內核模式,要麼處於受限制的用戶模式。除了內核自己處於內核模式之外,全部的用戶進程都運行在用戶模式之中。
內核模式的代碼能夠無限制地訪問全部處理器指令集以及所有內存和I/O空間。若是用戶模式的進程要享有此特權,它必須經過系統調用向設備驅動程序或其餘內核模式的代碼發出請求。另外,用戶模式的代碼容許發生缺頁,而內核模式的代碼則不容許。
在2.4和更早的內核中,僅僅用戶模式的進程能夠被上下文切換出局,由其餘進程搶佔。除非發生如下兩種狀況,不然內核模式代碼能夠一直獨佔CPU:
(1) 它自願放棄CPU;
(2) 發生中斷或異常。
2.6內核引入了內核搶佔,大多數內核模式的代碼也能夠被搶佔。
3) 怎樣申請大塊內核內存?
在Linux內核環境下,申請大塊內存的成功率隨着系統運行時間的增長而減小,雖然能夠經過vmalloc系列調用申請物理不連續但虛擬地址連續的內存,但畢竟其使用效率不高且在32位系統上vmalloc的內存地址空間有限。因此,通常的建議是在系統啓動階段申請大塊內存,可是其成功的機率也只是比較高而已,而不是100%。若是程序真的比較在乎這個申請的成功與否,只能退用「啓動內存」(Boot Memory)。下面就是申請並導出啓動內存的一段示例代碼:
void* x_bootmem = NULL;
EXPORT_SYMBOL(x_bootmem);
unsigned long x_bootmem_size = 0;
EXPORT_SYMBOL(x_bootmem_size);
static int __init x_bootmem_setup(char *str)
{
x_bootmem_size = memparse(str, &str);
x_bootmem = alloc_bootmem(x_bootmem_size);
printk(「Reserved %lu bytes from %p for x\n」, x_bootmem_size, x_bootmem);
return 1;
}
__setup(「x-bootmem=」, x_bootmem_setup);
可見其應用仍是比較簡單的,不過利弊老是共生的,它不可避免也有其自身的限制:
內存申請代碼只能鏈接進內核,不能在模塊中使用。
被申請的內存不會被頁分配器和slab分配器所使用和統計,也就是說它處於系統的可見內存以外,即便在未來的某個地方你釋放了它。
通常用戶只會申請一大塊內存,若是須要在其上實現複雜的內存管理則須要本身實現。
在不容許內存分配失敗的場合,經過啓動內存預留內存空間將是咱們惟一的選擇。
4) 用戶進程間通訊主要哪幾種方式?
(1)管道(Pipe):管道可用於具備親緣關係進程間的通訊,容許一個進程和另外一個與它有共同祖先的進程之間進行通訊。
(2)命名管道(named pipe):命名管道克服了管道沒有名字的限制,所以,除具備管道所具備的功能外,它還容許無親緣關係進程間的通訊。命名管道在文件系統中有對應的文件名。命名管道經過命令mkfifo或系統調用mkfifo來建立。
(3)信號(Signal):信號是比較複雜的通訊方式,用於通知接受進程有某種事件發生,除了用於進程間通訊外,進程還能夠發送信號給進程自己;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數sigaction(實際上,該函數是基於BSD的,BSD爲了實現可靠信號機制,又可以統一對外接口,用sigaction函數從新實現了signal函數)。
(4)消息(Message)隊列:消息隊列是消息的連接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程能夠向隊列中添加消息,被賦予讀權限的進程則能夠讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩衝區大小受限等缺
(5)共享內存:使得多個進程能夠訪問同一塊內存空間,是最快的可用IPC形式。是針對其餘通訊機制運行效率較低而設計的。每每與其它通訊機制,如信號量結合使用,來達到進程間的同步及互斥。
(6)信號量(semaphore):主要做爲進程間以及同一進程不一樣線程之間的同步手段。
(7)套接字(Socket):更爲通常的進程間通訊機制,可用於不一樣機器之間的進程間通訊。起初是由Unix系統的BSD分支開發出來的,但如今通常能夠移植到其它類Unix系統上:Linux和System V的變種都支持套接字。
5) 經過夥伴系統申請內核內存的函數有哪些?
在物理頁面管理上實現了基於區的夥伴系統(zone based buddy system)。對不一樣區的內存使用單獨的夥伴系統(buddy system)管理,並且獨立地監控空閒頁。相應接口alloc_pages(gfp_mask, order),_ _get_free_pages(gfp_mask, order)等。
6) 經過slab分配器申請內核內存的函數有?
7) Linux的內核空間和用戶空間是如何劃分的(以32位系統爲例)?
8) vmalloc()申請的內存有什麼特色?
9) 用戶程序使用malloc()申請到的內存空間在什麼範圍?
10) 在支持並使能MMU的系統中,Linux內核和用戶程序分別運行在物理地址模式仍是虛擬地址模式?
11) ARM處理器是經過幾級也表進行存儲空間映射的?
12) Linux是經過什麼組件來實現支持多種文件系通的?
13) Linux虛擬文件系統的關鍵數據結構有哪些?(至少寫出四個)
14) 對文件或設備的操做函數保存在那個數據結構中?
15) Linux中的文件包括哪些?
16) 建立進程的系統調用有那些?
17) 調用schedule()進行進程切換的方式有幾種?
18) Linux調度程序是根據進程的動態優先級仍是靜態優先級來調度進程的?
19) 進程調度的核心數據結構是哪一個?
20) 如何加載、卸載一個模塊?
21) 模塊和應用程序分別運行在什麼空間?
22) Linux中的浮點運算由應用程序實現仍是內核實現?
23) 模塊程序可否使用可連接的庫函數?
24) TLB中緩存的是什麼內容?
25) Linux中有哪幾種設備?
26) 字符設備驅動程序的關鍵數據結構是哪一個?
27) 設備驅動程序包括哪些功能函數?
28) 如何惟一標識一個設備?
29) Linux經過什麼方式實現系統調用?
30) Linux軟中斷和工做隊列的做用是什麼?
昨天去了深信服面試linux開發工程師。現將面試過程總結一下: 1.malloc和new的區別 2.解釋一下位域 3.c語言的地址對齊是怎麼回事 4.寫一個相似於printf的函數,能夠接受任意多個參數 5.使用memcp函數的時候,有什麼要注意的 6.說一下你知道的排序算法 7.有兩個單鏈表,用什麼方法能夠快速的知道他們有沒有相同的部分 8.執行make命令的時候怎麼給makefile傳遞參數 9.解釋一下殭屍進程 10.殭屍進程怎麼回收 11.在linux系統上怎麼列出全部的殭屍進程 12.怎麼列出linux上全部在運行的進程 13.用shell命令怎麼將全部進程的名字寫到一個文件裏面 14.使用shell命令查找當前目錄中全部包含txt字符串的文件,而且刪除他們 15.c語言中,main方法返回值的意義 16.說說你知道的排序算法 17.如何比較兩個浮點數是否相等 18.浮點數是怎麼存儲的 19.fork函數和wait函數的做用 20.解釋一下殭屍進程是怎麼產生的,而後怎麼回收的。用什麼命令能夠查看當前全部的殭屍進程