![程序員面試分類模擬3_第1頁](http://file4.renrendoc.com/view14/M02/1F/3B/wKhkGWZb9EWAQcC1AAJIIWhcYYM212.jpg)
![程序員面試分類模擬3_第2頁](http://file4.renrendoc.com/view14/M02/1F/3B/wKhkGWZb9EWAQcC1AAJIIWhcYYM2122.jpg)
![程序員面試分類模擬3_第3頁](http://file4.renrendoc.com/view14/M02/1F/3B/wKhkGWZb9EWAQcC1AAJIIWhcYYM2123.jpg)
![程序員面試分類模擬3_第4頁](http://file4.renrendoc.com/view14/M02/1F/3B/wKhkGWZb9EWAQcC1AAJIIWhcYYM2124.jpg)
![程序員面試分類模擬3_第5頁](http://file4.renrendoc.com/view14/M02/1F/3B/wKhkGWZb9EWAQcC1AAJIIWhcYYM2125.jpg)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
程序員面試分類模擬3論述題1.
一些結(jié)構(gòu)聲明中的冒號和數(shù)字是什么意思正確答案:C語言的結(jié)構(gòu)體可以實現(xiàn)位段,它的定義形式是在一個定義的結(jié)構(gòu)體成員后面加上冒號,然后是該成員所占的位數(shù)。位段的結(jié)構(gòu)體成員(江南博哥)必須是int或者unsignedint類型,不能是其他類型。位段在內(nèi)存中的存儲方式是由具體的編譯器決定的。
首先,定義位段的長度不能大于存儲單元的長度。存儲單元是指該位段的類型大小,不是計算機的存儲單元字節(jié)。其次,一個位段如果不能放在一個存儲單元里,那么它會把這個存儲單元中剩余的空間閑置,而從下一個存儲單元開始存儲下一個位段,即一個位段不能存儲在兩個存儲單元內(nèi),位段在一個存儲單元中的存儲是緊湊的。再次,位段名缺省時稱作無名位段,無名位段的存儲空間通常不用,而位段長度為0位表示下一個位段存儲在一個新的存儲單元中,位段長度為0的時候位段名必須缺省(不能定義位段名)。最后,一個結(jié)構(gòu)體中既可以定義位段成員也可以同時定義一般的結(jié)構(gòu)體成員。這個時候,一般成員不和位段存儲在同一個存儲單元中。
程序示例分析如下:
#include<stdio.h>
typedefstruct
{
inta:2:
intb:2:
intc:1:
}test;
intmain()
{
testt;
t.a=1;
t.b=3;
t.c=1;
printf("%d\n%%d\n%d\n",t.a,t.b,t.c);
return0;
}
程序輸出結(jié)果:
1
-1
-1
由于a占兩位,而a被賦值為1,二進制就是01,因此%d輸出的時候輸出1;b也占了兩位,賦值為3,二進制也就是11,由于使用了%d輸出,表示的是將這個b作為有符號int型來輸出,這樣的話二進制的11將會有一位被認為是符號位,并且兩位的b也會被擴展為int類型,也就是4字節(jié),即32位。其實a也做了這種擴展,只是擴展符號位的時候,由于數(shù)字在計算機中存儲都是補碼形式,因此擴展符號位的時候正數(shù)用0填充高位,負數(shù)則用1填充高位。因此對于a來說,輸出的時候被擴展為00000000000000000000000000000001,也就是1,而b則擴展為11111111111111111111111111111111,也就是-1了,c的顯示也是這樣的。
2.
最有效的計算2乘以8的方法是什么正確答案:2<<3。
雖然直接進行乘法操作符運算也可以進行2與8的相乘,但是該種方法并非最優(yōu),通過移位方法會比較高效。因為將一個數(shù)左移n位,相當于乘以了2的n次方。因此,一個數(shù)乘以8,而8是2的3次方,所以只要該數(shù)左移3位即可實現(xiàn)乘以8的目的。
常規(guī)的乘法運算也可以實現(xiàn),但CPU直接支持位運算,效率最高,所以操作2乘以8的最有效的方法是2<<3。
引申:如何快速求取一個整數(shù)的7倍?
相比移位運算,如果直接使用乘法運算符的話,則執(zhí)行效率相對比較慢,所以快速的方法就是將這個乘法轉(zhuǎn)換成加減法和移位操作。由于移位運算相當于乘法運算或除法運算,左移相當于乘法運算,右移運算相當于除法運算,所以此時可以先將此整數(shù)左移3位(相當于將數(shù)字乘以8),然后再減去原值,即(x<<3)-x就獲得了x的7倍。此處需要注意的是,由于-的優(yōu)先級高于<<,所以不能去掉括號,否則結(jié)果不正確。
3.
如何實現(xiàn)位操作求兩個數(shù)的平均值正確答案:一般而言,求解平均數(shù)的方法就是將兩者相加,然后除以2,以變量x與y為例,兩者的平均數(shù)為(x+y)/2。
但是采用上述方法,會存在一個問題,當兩個數(shù)比較大時,如兩者的和大于了機器位數(shù)能夠表示的最大值,可能會存在數(shù)據(jù)溢出的情況,而采用位運算方法則可以避免這一問題,(x&y)+((x^y)>>1)方式表達的意思都是求解變量x與y的平均數(shù),而且位運算相比除法運算,效率更高。
對于表達式(x&y)+((x^y)>>1),x&y表示的是取出x與y二進制位數(shù)中都為‘1’的所有位,x^y表示的是x與y中有一個為‘1’的所有位,右移1位相當于執(zhí)行除以2運算。整個表達式實際上可以分為兩部分,第一部分是都為‘1’的部分,因為相同,所以直接相加即可;而第二部分是x為‘1’、y為‘0’的部分,以及y為‘1’、x為‘0’的部分,兩部分加起來再除以2,然后跟前面的相加就可以表示兩者的平均數(shù)了。
以下述示例為例。
#include<stdio.h>
intmain()
{
intx=2147483647,Y=2147483647;
printf("%d\n",(x+y)/2);
printf("%d\n",(x&y)+((x^y)>>1));
return0;
}
在32位機器下,程序輸出結(jié)果如下:
-1
2147483647
程序的輸出正好驗證了這一算法的可行性。
引申:如何利用位運算計算數(shù)的絕對值?
以x為負數(shù)為例來分析。因為在計算機中,數(shù)字都是以補碼的形式存放的,求負數(shù)的絕對值,應(yīng)該是不管符號位,執(zhí)行按位取反,末位加1操作即可。
對于一個負數(shù),將其右移31位后會變成0xfffffff,而對于一個正數(shù)而言,右移31位則為0x00000000,而0ffffffff^x+x=-1,因為1011^1111=0100,任何數(shù)與1111異或,其實質(zhì)都是把x的0和1進行顛倒計算。如果用變量Y表示x右移31位,(x^y)-y則表示的是x的絕對值。
程序示例如下:
#include<stdio.h>
intMyAbs(intx)
{
inty;
y=x>>31;
return(x^y)-y;∥此處還可以寫為(x+y)^y
}
intmain()
{
printf("%d\n",MyAbs(2));
printf("%d\n",MyAbs(-2));
return0;
}
程序輸出結(jié)果:
2
2
上例中,在函數(shù)MyAbs中,對局部變量y進行賦值時,由于是對X進行右移31位,如果x為正數(shù),則y=0;如果x為負數(shù),則y=-1。
4.
unsignedinti=3;printf("%u\n",i*-1)輸出為多少正確答案:運行如下程序:
#include<stdio.h>
intmain()
{
unsignedinti=3;
printf("%u\n",i*-1);
return0;
}
程序輸出結(jié)果:
4294967293
在32位機器中,i*-1的值為4294967293。在32位機器中,無符號int的值域是[0,4294967295],有符號int的話,值域是[-2147483648,2147483647],兩個值域的個數(shù)都是4294967296個,即
[0,4294967295]=[0,2147483647]U[2147483648,4294967295]
有符號int的[-2147483648,-1]對應(yīng)于無符號int的[2147483648,4294967295]區(qū)域,兩個區(qū)域的值是一一映射關(guān)系。所以,-1對應(yīng)4294967295,-2對應(yīng)4294967294,-3對應(yīng)4294967293。
引申:unsignedshortA=10;printf("~A=%u\n",~A);輸出是什么?
因為A為無符號短整型變量,值為10,在32位機器中,轉(zhuǎn)換為二進制為00000000000000000000000000001010,對A取反操作,所以~A的二進制位為11111111111111111111111111110101,十六進制表示即為0xFFFFFFF5,而如果將該數(shù)轉(zhuǎn)換為符號整型的話則為-11,因為輸出的是無符號整型,無符號整型的范圍為0~4294967295,而0xFFFFFFF5轉(zhuǎn)換為無符號十進制整型為4294967285。
所以程序的輸出結(jié)果為4294967285。
5.
如何求解整型數(shù)的二進制表示中1的個數(shù)正確答案:求解整型數(shù)的二進制表示中1的個數(shù)有以下兩種方法:
方法一,程序代碼如下:
#include<stdio.h>
intfunc(intx)
{
intcountx=0;
while(x)
{
countx++;
x=x&(x-1);
}
returncountx;
}
intmain()
{
printf("%d\n",func(9999));
return0;
}
程序輸出結(jié)果:
8
在上例中,函數(shù)func()的功能是將x轉(zhuǎn)化為二進制數(shù),然后計算該二進制數(shù)中含有的1的個數(shù)。首先以9為例來分析,9的二進制為1001,8的二進制為1000,兩者執(zhí)行&操作之后結(jié)果為1000,此時1000再與0111(7的二進制位)執(zhí)行&操作之后結(jié)果為0。
為了理解這個算法的核心,需要理解以下兩個操作:
1)當一個數(shù)被減1時,它最右邊的那個值為1的bit將變?yōu)?,同時其右邊的所有的bit都會變成1。
2)“&=”,位與并賦值操作。去掉已經(jīng)被計數(shù)過的1,并將該值重新設(shè)置給n。這個算法循環(huán)的次數(shù)是bit位為1的個數(shù)。也就說,有幾個bit為1,循環(huán)幾次,對bit為1比較稀疏的數(shù)來說,性能很好。例如,0x10000000循環(huán)一次就可以。
方法二,判斷每個數(shù)的二進制表示中每一位是否為1,如果為1,就在count上加1,而循環(huán)的次數(shù)是常數(shù),即n的位數(shù)。但該方法有一個缺陷,就是在1比較稀疏的時候效率會比較低。
程序示例如下:
#include<stdio.h>
intfunc(unsignedintn)
{
intcount=0;
while(n)
{
count+=n&0xlu;
n>>=1;
}
returncount;
}
intmain()
{
printf("%d\n",func(9999));
return0;
}
程序輸出結(jié)果:
8
需要注意的是,上例中,0xlu表示的是十六進制的無符號數(shù)1。
6.
不能用sizeof()函數(shù),如何判斷操作系統(tǒng)是16位還是32位的正確答案:如果沒有強調(diào)不許使用sizeof,一般可以使用sizeof計算字節(jié)長度來判斷操作系統(tǒng)的位數(shù),如在32位機器上,sizeof(int)=4,而在16位機器上,sizeof(int)=2。除此之外,還有以下兩種方法。
方法一:一般而言,機器位數(shù)不同,其表示的數(shù)字的最大值也不同,根據(jù)這一特性,可以判斷操作系統(tǒng)的位數(shù)。
例如,運行如下代碼:
#include<stdio.h>
intmain()
{
inti=65536;
printf("%d\n",i);
intj=65535;
printf("%d\n",j);
return0;
}
由于16位機器下,無法表示這么大的數(shù),會出現(xiàn)越界情況,所以程序輸出為
0
-1
而在32位機器下,則會正常輸出,程序輸出為
65536
65535
之所以會有區(qū)別,是因為在16位機器下,能夠表示的最大數(shù)為65535,所以會存在最高位溢出的情況。當變量的值為65536時,輸出為0;當變量的值為65535時,輸出為-1。而在32位機器上,則不會出現(xiàn)溢出的情況,所以輸出為正常輸出。
方法二:對0值取反,不同位數(shù)下的0值取反,其結(jié)果不一樣。例如,在32位機器下,按位取反運算,結(jié)果為11111111111111111111111111111111。運行如下代碼:
#include<stdio.h>
intmain()
{
unsignedinta=~0:
if(a>65536)
printf("32位\n");
else
printf("16位\n");
return0;
}
程序輸出為
32位
7.
嵌入式編程中,什么是大端?什么是小端正確答案:采用小端模式的CPU對操作數(shù)的存放方式是從低字節(jié)到高字節(jié),而大端模式對操作數(shù)的存放方式是從高字節(jié)到低字節(jié)。例如,16位寬的數(shù)0x1234在小端模式CPU內(nèi)存中的存放方式(假設(shè)從地址0x4000開始存放)見下表1,而在大端模式CPU內(nèi)存中的存放方式見表2。表10x1234在小端模式CPU內(nèi)存中的存放方式內(nèi)存地址存放內(nèi)容0x40000x340x40010x12表20x1234在大端模式CPU內(nèi)存中的存放方式內(nèi)存地址內(nèi)存內(nèi)容0x40000x120x40010x3432位寬的數(shù)0x12345678在小端模式CPU內(nèi)存中的存放方式(假設(shè)從地址0x4000開始存放)見表3,而在大端模式CPU內(nèi)存中的存放方式見表4。表30x12345678在小端模式CPU內(nèi)存中的存放方式內(nèi)存地址存放內(nèi)容0x40000x780x40010x560x40020x340x40030x12表40x12345678在大端模式CPU內(nèi)存中的存放方式內(nèi)存地址存放內(nèi)容0x40000x120x40010x340x40020x560x40030x78
以如下程序為例。
#include<stio.h>
structmybitfields
{
unsignedshorta:4;
unsignedshortb:5;
unsignedshortc:7;
}test;
intmain()
{
inti;
test.a=2;
test.b=3;
test.c=0;
i=*((short*)&test);
printf("%d\n",i);
return0;
}
程序輸出結(jié)果:
50
上例中sizeof(test)=2,上例的聲明方式是把一個short(也就是一塊16位內(nèi)存)分成3部分,各部分的大小分別是4位、5位、7位,賦值語句i=*((short*)&test)就是把上面的16位內(nèi)存轉(zhuǎn)換成short類型進行解釋。
變量a的二進制表示為0000000000000010,取其低四位是0010。變量b的二進制表示為0000000000000011,取其低五位是00011。變量c的二進制表示為0000000000000000,取其低七位是0000000。
x86機是小端(修改分區(qū)表時要注意)模式,單片機一般為大端模式。小端一般是低位字節(jié)在高位字節(jié)的前面,也就是低位在內(nèi)存地址低的一端,可以這樣記(小端→低位→在前→與正常邏輯順序相反),所以合成后得到0000000000110010,即十進制的50。
程序示例如下:
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
intmain()
{
unsignedintuiVa1_1=0x12345678;
unsignedintuiVa1_2=0;
unsignedcharaucVal[4]={0x12,0x34,0x56,0x78};
unsignedshortusVa1_1=0;
unsignedshortusVa1_2=0;
memcpy(&uiVa1_2,aucVa1,sizeof(uiVa1_2));
usVa1_1=(unsignedshort)uiVa1_1;∥在這兒截斷,都取得的是低位
usVa1_2=(unsignedshort)uiVa1_2;∥在這兒截斷
printf("usVa1_1:%x\n",usVa1_1);∥這兒又轉(zhuǎn)化回來
printf("usVa1_2:%x\n",usVa1_2);∥這邊又轉(zhuǎn)化回來
return0;
}
小端模式是低地址存放低字節(jié),高地址存放高字節(jié),結(jié)構(gòu)如圖所示。
小端模式的存放方式
在內(nèi)存里面測試機是小端,地址由小到大。
uiVa1_1:78563412
uiVa1_2:12345678
結(jié)果如下:
5678
3412
再例如:
charp[4]={0x01,00,0x01,00};
printf("%d",*(int*)p);
內(nèi)存布局如下:
0x01
0x00
0x01
0x00
如果用char*指針訪問,那么一個元素占一個字節(jié);如果用int*指針訪問,那么一個元素占4個字節(jié)。這樣又涉及大小端的問題。
小端的時候把數(shù)據(jù)解釋成0x00010001,所以是65537。
大端的時候把數(shù)據(jù)解釋成0x01000100。
程序?qū)嵗缦拢?/p>
#include<stdio.h>
#include<string.h>
typedefstructAA
{
intb1:5;
intb2:5;
}AA;
intmain()
{
AAaa;
charcc[100];
strcpy(cc,"0123456789abcdefghijklnmopqrstuvwxyz");
memcpy(&aa,cc,sizeof(AA));
printf("%d\n",aa.b1);
printf("%d\n",aa.b2);
printf("%d\n",sizeof(aa));
return0;
}
上述代碼輸出結(jié)果:
-16
9
4
首先看sizeof是4,就是如果出現(xiàn)int,至少為4的倍數(shù),如果只有char,就以char為準。字符0的ASCⅡ為48,所以為00110000;字符1的ASCⅡ為49,所以為00110001,然后考慮到大端小端,就是0000110010001100,前5位為b1,反過來,就是10000,由于是補碼,所以為-16,接著的5位為01001,即9。
引申:如何判斷計算機處理器是大端還是小端?
程序示例如下:
#include<stdio.h>
intcheckCPU()
{
{
unionw
{
inta;
charb;
}c;
c.a=1;
return(c.b==1);
}
}
intmain()
{
if(checkCPU())
printf("小端\n");
else
printf("大端\n");
return0;
}
編者的處理器為Intel處理器,因為Intel處理器一般都是小端模式,所以此時輸出為小端
上述代碼中,如果處理器是大端的,則返回0;如果是小端,則返回1。聯(lián)合體union的存放順序是所有成員都從低地址開始存放,如果能夠通過改代碼知道CPU對內(nèi)存采用小端還是大端模式讀寫,一定會令面試官刮目相看。
還可以通過指針地址來判斷,由于在32位計算機系統(tǒng)中,short占兩個字節(jié),char占1個字節(jié),所以可以采用如下做法實現(xiàn)該判斷。
#include<stdio.h>
intcheckCPU()
{
unsignedshortusData=0x1122;
unsignedchar*pucData=(unsignedchar*)&usData;
return(*pucData==0x22);
}
intmain()
{
if(checkCPU())
printf("小端\n");
else
printf("大端\n");
return0;
}
程序輸出為
小端
8.
考慮n位二進制數(shù),有多少個數(shù)中不存在兩個相鄰的1正確答案:當n=1時,滿足條件的二進制數(shù)為0、1,一共兩個數(shù);當n=2時,滿足條件的二進制數(shù)有00、01、10,一共3個數(shù);當n=3時,滿足條件的二進制數(shù)有000、001、010、100、101,一共5個數(shù)。對n位二進制數(shù),設(shè)所求結(jié)果為a(n),對于第n位的值,分為0或者1兩種情況:
1)第n位為0,則有a(n-1)個數(shù)。
2)第n位為1,則要滿足沒有兩個相鄰為1的條件,第n-1位為0,有a(n-2)個數(shù),因此得到結(jié)論a(n)=a(n-1)+a(n-2)。
通過觀察2)中的表達式可以發(fā)現(xiàn),式子滿足斐波拉契數(shù)列,而求解斐波拉契數(shù)列一般有兩種方法:遞歸方法與非遞歸方法,本題只將遞歸的方法寫出來,有興趣的讀者可以自己編寫非遞歸的斐波拉契數(shù)列求解方法。
程序代碼示例如下:
#include<stdio.h>
longFibonacci(inti)
{
if(i==1|i==2)
return1;
else
return(Fibonacci(i-1)+Fibonacci(i-2));
}
intmain()
{
printf("%1d\n",Fibonacci(7));
return0;
}
程序輸出結(jié)果:
13
9.
不用除法操作符如何實現(xiàn)兩個正整數(shù)的除法正確答案:在回答本問題前,先學習一些有關(guān)位運算的知識。
1)常用的等式:-n=~(n-1)=~n+1。
2)獲取整數(shù)n的二進制中最后一個1:n&(-n)或者n&~(n-1)。例如,n=010100,則-n=101100,n&(-n)=000100。
3)去掉整數(shù)n的二進制中最后一個1:n&(n-1),如n=010100,n-1=010011,n&(n-1)=010000。
一般求解兩個正整數(shù)的除法問題時,首先考慮到的就是采用除法運算,但是除了除法運算外,還有其他方法可以執(zhí)行除法運算。
方法一,可以根據(jù)除法運算的原理進行減法操作,對除數(shù)循環(huán)減被除數(shù),減一次結(jié)果加一,直到剛好減為0或余數(shù)小于被除數(shù)為止。程序示例如下:
#include<stdio.h>
intdiv(inta,intb)
{
intresult=0;
if(b==0)
{
printf("除數(shù)不能為0\n");
returnresult;
}
while(a>=b)
{
result++;
a=a+b;
}
returnresult;
}
intmain()
{
printf("%d\n",div(10,3));
return0;
}
程序輸出結(jié)果:
3
這個算法每次都以一倍的被除數(shù)進行疊加,算法效率并不高,尤其是當a很大,b很小時,效率會非常低。
方法二,遞歸法求解。以100/3為例,方法一提出的方法分別比較97,94,91,...,4,1,-2,最后余數(shù)為-2,退出while循環(huán),整個算法需要比較34次。如果每次采用將比較數(shù)翻倍的比較方法,則算法效率能夠得到極大優(yōu)化。
程序示例如下:
#include<stdio.h>
intMyDiv(inta,intb)
{
intk=0;
intc=b;
intres=0;
if(b==0)
printf("除數(shù)不能為0\n");
if(a<b)
return0;
for(;a>=c;c<<=1,k++)
if(a-c<b)
return1<<k;
returnMyDiv(a-(c>>1),b)+(1<<(k-1));
}
intmain()
{
printf("%d\n",MyDiv(100,3));
return0;
}
程序輸出結(jié)果:
33
方法三:采用移位操作實現(xiàn),位操作的效率一般都比較高效。程序示例如下:
#include<stdio.h>
intdiv(constintx,constinty)
{
intleft_num=x;
intresult=0;
while(left_num>=y)
{
intmulti=1;
while(y*multi<=(left_num>>1))
{
multi=multi<<1;
}
result+=multi;
left_num-=y*multi;
}
returnresult;
}
intmain()
{
printf("%d\n”,div(10,3));
return0;
}
程序輸出結(jié)果:
3
引申1:如何只用邏輯運算實現(xiàn)加法運算?
實現(xiàn)兩個正整數(shù)相加,一般直接使用加號運算符即可??紤]到題目中的要求,與上例中的方法二類似,也可以通過移位操作符來進行正整數(shù)的加法運算。例如,5與7求和,轉(zhuǎn)換為二進制求和為101與111求和,其二進制結(jié)果為1100。對于二進制的加法而言,1+1=0,1+0=1,0+1=1,0+0=0,通過對比位運算中的異或方法,不難發(fā)現(xiàn),此方法與位運算中的異或類似。那么第一個數(shù)值就是:tempNum1=num1^num2;0+0的進位是0,1+0的進位是0,只有1+1的進位有效,該思路與位運算的&運算相似,即只有1&1=1,所以可以先num1&num2,由于進位是進到高一位的,與<<運算很相似,同時num1和num2相互&之后,如果結(jié)果為0,那么就不存在進位,運算完成,所以可以用遞歸的思想實現(xiàn)。程序示例如下所示:
#include<stdio.h>
intadd(intnum1,intnum2)
{
if(0==num2)
returnnum1;
intsumTemp=num1^num2;
intcarry=(num1&num2)<<1;
returnadd(sumTemp,carry);
}
intmain()
{
printf("%d\n",add(100,200));
return0;
}
程序輸出結(jié)果:
300
將遞歸思想轉(zhuǎn)換為非遞歸思想之后,就成了另外一種思路,程序示例如下:
#include<stdio.h>
intadd(intnum1,intnum2)
{
intsum=0;
intnum3=0;
intnum4=0;
while((num1&num2)>0)
{
num3=num1^num2;
num4=num1&num2;
num1=num3;
num2=num4<<1;
}
sum=num1^num2;
returnsum;
}
intmain()
{
printff(%d\n",add(100,200));
return0;
}
程序輸出結(jié)果:
300
引申2:如何只用邏輯運算實現(xiàn)乘法運算?
先看一個實例:1011*1010,因為二進制運算的特殊性,可以將該乘法運算表達式拆分為兩個運算,1011*0010與1011*1000的和,而對于二進制的運算,左移1位,等價于乘以0010,左移3位,等價于乘以1000,所以兩者的乘積為10110與1011000的和,即為1101110。
因而乘法可以通過一系列移位和加法完成。最后一個1可通過b&~(b-1)求得,可通過b&(b-1)去掉,為了高效地得到左移的位數(shù),可提前計算一個map,算法如下:
#include<iostream>
#include<map>
usingnamespacestd;
intmultiply(inta,intb)
{
boolneg=(b<0);
if(b<0)
b=-b;
intsum=0;
map<int,int>bit_map;
for(inti=0;i<32;i++)
bit_map.insert(pair<int,int>(1<<i,i));
while(b>0)
{
intlast_bit=bit_map[b&~(b-1)];
sum+=(a<<last_bit);
b&=b-1;
}
if(neg)
sum=-sum;
returnsum;
}
intmain()
{
prinff("%d\n",multiply(3,5));
return0;
}
程序輸出結(jié)果:
15
函數(shù)是程序的基本組成單位,利用函數(shù),不僅能夠?qū)崿F(xiàn)程序的模塊化,而且簡單、直觀,能極大地提高程序的易讀性和可維護性,所以將程序中的一些計算或操作抽象成函數(shù)以供隨時調(diào)用,理解函數(shù)的執(zhí)行原理以及應(yīng)用是一個優(yōu)秀程序員應(yīng)該具備的基本能力。
10.
怎么樣寫一個接受可變參數(shù)的函數(shù)正確答案:C語言中支持函數(shù)調(diào)用的參數(shù)為變參形式。例如,printf()這個函數(shù),它的函數(shù)原型是intprintf(constchar*fofmat,...),它除了有一個參數(shù)format固定以外,后面跟的參數(shù)的個數(shù)和類型都是可變的,可以有以下多種不同的調(diào)用方法:
1)printf("%d",i);
2)printf("%s",s);
3)printf("thenumberis%d,stringis:%s",i,s);
printf()函數(shù)是一個有著變參的庫函數(shù),在C語言中,程序員也可以根據(jù)實際的需求編寫變參函數(shù)。如下程序示例代碼,實現(xiàn)了一個變參函數(shù)add2(),該函數(shù)實現(xiàn)多參數(shù)求和運算。
#include<stdio.h>
intadd2(charnum,...)
{
intsum=0;
intindex=0;
int*p=NULL;
p=(int*)&num+1;
for(;index<(int)num;++index)
{
sum+=*p++;
}
returnsum;
}
intmain()
{
inti=1;
intj=2;
intk=3;
printf("%d\n",add2(3,i,j,k));
return0;
};
程序輸出結(jié)果:
6
11.
函數(shù)指針與指針函數(shù)有什么區(qū)別正確答案:指針函數(shù)是指帶指針的函數(shù),本質(zhì)上是一個函數(shù),函數(shù)返回類型是某一類型的指針。其形式一般如下:
類型標識符*函數(shù)名(參數(shù)列表)
例如,int*f(x,y),它的意思是聲明一個函數(shù)f(x,y),該函數(shù)返回類型為int型指針。
而函數(shù)指針是指向函數(shù)的指針變量,即本質(zhì)是一個指針變量,表示的是一個指針,它指向的是一個函數(shù)。其形式一般如下:
類型說明符(*函數(shù)名)(參數(shù))
例如,int(*pf)(intx),它的意思就是聲明一個函數(shù)指針,而pf=func則是將func函數(shù)的首地址賦值給指針。
下面為一個函數(shù)指針的實例。
#include<stdio.h>
#defineNULL0
#defineASGN1
#defineMUL2
intasgn(int*a,intb)
{
return*a=b;
}
intmul(int*a,intb)
{
return*a*b;
}
int(*func(intop))(int*,int)
{
switch(op)
{
caseASGN;
return&asgn;
caseMUL;
return&mul;
default:
returnNULL;
}
returnNULL;
}
intmain()
{
inti=0xFEED,j=0xBEEF;
printf("%x、n",func(ASGN)(&i,j));
printf("%x\n",func(MUL)(&i,j));
printf("%x,%x\n,i,j);
return0;
}
程序輸出結(jié)果:
beef
8e67a321
beef,beef
引申:數(shù)組指針/指針數(shù)組、函數(shù)模板/模板函數(shù)、類模板,模板類、指針常量,常量指針分別有什么區(qū)別?
(1)數(shù)組指針/指針數(shù)組
數(shù)組指針就是指向數(shù)組的指針,它表示的是一個指針,它指向的是一個數(shù)組,它的重點是指針。例如,int(*pa)[8]聲明了一個指針,該指針指向了一個有8個int型元素的數(shù)組。
#include<stdio.h>
intmain()
{
int(*p)[4];
inta[3][4]={{1,2,3,4},{5,6,7,8),{9,10,11,12});
p=&a[0];
for(inti=0;i<12;i++)
printf("%d",(*p)[i]);
printf("\n");
return0:
}
程序輸出結(jié)果:
123456789101112
指針數(shù)組就是指針的數(shù)組,表示的是一個數(shù)組,它包含的元素是指針,它的重點是數(shù)組。例如,int*ap[8]聲明了一個數(shù)組,該數(shù)組的每一個元素都是int型的指針。
#include<stdio.h>
intmain()
{
int*p[4];
inta[4]={1,2,3,4};
p[0]=&a[0];
p[1]=&a[1];
p[2]=&a[2];
p[3]=&a[3];
for(inti=0;i<4;i++)
printf("%d",*p[i]);
printf("\n");
return0;
}
程序輸出結(jié)果:
1234
(2)函數(shù)模板/模板函數(shù)
函數(shù)模板是對一批模樣相同的函數(shù)的說明描述,它不是某一個具體的函數(shù);而模板函數(shù)則是將函數(shù)模板內(nèi)的“數(shù)據(jù)類型參數(shù)”具體化后得到的重載函數(shù)(就是由模板而來的函數(shù))。簡單地說,函數(shù)模板是抽象的,而模板函數(shù)則是具體的。
函數(shù)模板減少了程序員輸入代碼的工作量,是C++中功能最強的特性之一,是提高軟件代碼重用性的重要手段之一。函數(shù)模板的形式一般如下:
template<模板類型形參表>
<返回值類型><函數(shù)名>(模板函數(shù)形參表)
{
∥函數(shù)體
}
其中<模板函數(shù)形參表>的類型可以是任何類型,包括基本數(shù)據(jù)類型和類類型。需要注意的是,函數(shù)模板并不是一個實實在在的函數(shù),它是一組函數(shù)的描述,并不能直接執(zhí)行,需要實例化為模板函數(shù)后才能執(zhí)行,而一旦數(shù)據(jù)類型形參實例化以后,就會產(chǎn)生一個實實在在的模板函數(shù)了。
(3)類模板/模板類
類模板與函數(shù)模板類似,將數(shù)據(jù)類型定義為參數(shù),描述了代碼類似的部分類的集合,具體化為模板類后,可以用于生成具體的對象。
template<類型參數(shù)表>
class<類名>
{
∥類說明體
};
template<類型形參表>
<返回類型><類名><類型名表>::<成員函數(shù)1>(形參表)
{
∥成員函數(shù)定義體
}
其中<類型形參表>與函數(shù)模板中的<類型形參表>意義類似,而類模板本身不是一個真實的類,只是對類的一種描述,必須用類型參數(shù)將其實例化為模板類后,才能用來生成具體的對象。簡而言之,類是對象的抽象,而類模板就是類的抽象。
具體而言,C++中引入模板類主要有以下5個方面的好處:
1)可用來創(chuàng)建動態(tài)增長和減小的數(shù)據(jù)結(jié)構(gòu)。
2)它是類型無關(guān)的,因此具有很高的可復(fù)用性。
3)它在編譯時而不是運行時檢查數(shù)據(jù)類型,保證了類型安全。
4)它是與平臺無關(guān)的,可移植性強。
5)可用于基本數(shù)據(jù)類型。
(4)指針常量/常量指針
指針常量是指定義的指針只能在定義的時候初始化,之后不能改變其值。其格式為
[數(shù)據(jù)類型][*][const][指針常量名稱]
例如:char*constp1;int*constp2;
const位于指針聲明符"*"的右側(cè),這說明聲明的對象是一個常量,而對象的數(shù)據(jù)類型是指針。所以第一句定義了一個只讀的字符型指針p1;第二句定義了一個只讀的整型指針p2。常指針的值不能改變,但是其指向的內(nèi)容卻可以改變。
#include<stdio.h>
intmain()
{
chara[5]="abed";
charb[5]="efgh";
char*constp1=a;
char*constp2=b;
printf("BeforeChange:\n");
printf("a:%s\nb:%s\n",a,b);
*p1='1';
b[0]='2';
∥p1=p2;
printf("AfterChange:\n");
printf("a:%s\nb:%s\n",a,b);
return0;
}
程序的輸出結(jié)果如下:
BeforeChange:
a:abcd
b:efgh
AfterChange:
a:1bcd
b:2fgh
上例中,如果去掉注釋行,執(zhí)行p1=p2操作,則編譯會出錯:errorC3892:"p1":不能給常量賦值(VS2005)。指針所指向的內(nèi)存地址不能更改,指針的值只能在定義的時候初始化,其他地方不能更改。
常量指針是指向常量的指針,因為常量指針指向的對象是常量,因此這個對象的值是不能夠改變的。定義的格式如下:
[數(shù)據(jù)類型][const][*][常量指針名稱];或[const][數(shù)據(jù)類型][*][常量指針名稱];
例如:intconst*p;constint*p;
程序示例如下:
#include<stdio.h>
intmain()
{
chara[5]="abcd";
charb[5]="efgh";
constchar*p1=a;
constchar*p2=b;
printf("BeforeChange:\n");
printf("a:%s\nb:%s\np1:%s\n",a,b,p1);
a[0]='1';
p1=p2;
∥*p2='2';
printf("AfterChange:\n");
printf("a:%s\nb:%s\np1:%s\n",a,b,p1);
return0;
}
程序的輸出結(jié)果:
BeforeChange:
a:abcd
b:efgh
p1:abcd
AfterChanged:
a:1bcd
b:efgh
p1:efgh
上例中,如果去掉注釋行,執(zhí)行*p2='2'操作,則編譯會出錯:errorC3892:"p2":不能給常量賦值。
需要注意的是,指針常量強調(diào)的是指針的不可改變性,而常量指針強調(diào)的是指針對其所指對象的不可改變性,它所指向的對象的值是不能通過常量指針來改變的。對于字符串“abc”,可以這樣獲取其地址:&("abc");
12.
C++函數(shù)傳遞參數(shù)的方式有哪些正確答案:當進行函數(shù)調(diào)用時,要填入與函數(shù)形式參數(shù)個數(shù)相同的實際參數(shù),在程序運行過程中,實際參數(shù)(簡稱實參)就會將參數(shù)值傳遞給相應(yīng)的形式參數(shù)(簡稱形參),然后在函數(shù)中實現(xiàn)對數(shù)據(jù)的處理和返回。C++函數(shù)傳遞參數(shù)的方式一般有以下4種:
(1)值傳遞
當進行值傳遞時,就是將實參的值復(fù)制到形參中,而形參和實參不是同一個存儲單元,所以函數(shù)調(diào)用結(jié)束后,實參的值不會發(fā)生改變。程序示例如下:
#include<iostream>
usingnamespacestd;
voidswap(inta,intb)
{
inttemp;
temp=a;
a=b;
b=temp;
cout<<a<<","<<b<<endl;
}
intmain()
{
intx=1;
inty=2;
swap(x,y);
cout<<X<<","<<Y<<endl;
return0;
}
程序的輸出結(jié)果:
2,1
1,2
也就是說,在進行函數(shù)調(diào)用的時候只交換了形參的值,而并未交換實參的值,形參值的改變并沒有改變實參的值。
(2)指針傳遞
當進行指針傳遞時,形參是指針變量,實參是一個變量的地址,調(diào)用函數(shù)時,形參(指針變量)指向?qū)崊⒆兞繂卧?。這種方式還是“值傳遞”,只不過實參的值是變量的地址而已。在函數(shù)中改變的不是實參的值,而是實參地址所指向的變量的值。
程序示例如下:
#include<iostream>
usingnamespacestd;
voidswap(int*a,int*b)
{
inttemp;
temp=*a;
*a=*b;
*b=temp;
cout<<*a<<"."<<*b<<endl;
}
intmain()
{
intx=1;
inty=2;
swap(&x,&y);
cout<<x<<","<<y<<endl;
return0;
}
程序的輸出結(jié)果:
2,1
2,1
也就是說,在進行函數(shù)調(diào)用時,不僅交換了形參的值,而且交換了實參的值。
(3)傳引用
實參地址傳遞到形參,使形參的地址取實參的地址,從而使形參與實參共享同一單元的方式。
程序代碼示例如下:
#include<iostream>
usingnamespacestd;
voidswap(int&a,int&b)
{
inttemp;
temp=a;
a=b;
b=temp;
cout<<*a<<","<<*b<<endl;
}
intmain()
{
intx=1;
inty=2;
swap(x,y);
cout<<x<<","<<y<<endl;
return0;
}
程序的輸出結(jié)果:
2,1
2,1
(4)全局變量傳遞
這里的“全局”變量并不見得就是真正的全局的,所有代碼都可以直接訪問的,只要這個變量的作用域足夠這兩個函數(shù)訪問就可以了,比如一個類中的兩個成員函數(shù)可以使用一個成員變量實現(xiàn)參數(shù)傳遞,或者使用static關(guān)鍵字定義,或者使用namespace進行限制等,而這里的成員變量在這種意義上就可以稱為“全局”變量。當然,可以使用一個類外的真正的全局變量來實現(xiàn)參數(shù)傳遞,但有時并沒有必要,從工程上講,作用域越小越好。
全局變量傳遞的優(yōu)點是效率高,但它對多線程的支持不好,如果兩個進程同時調(diào)用同一個函數(shù),而通過全局變量進行傳遞參數(shù),該函數(shù)就不能總是得到想要的結(jié)果。
13.
重載與覆蓋有什么區(qū)別正確答案:重載是指函數(shù)不同的參數(shù)表,對同名函數(shù)的名稱做修飾,然后這些同名函數(shù)就成了不同的函數(shù)(至少對于編譯器來說是這樣的)。在同一可訪問區(qū)域內(nèi)被聲明的幾個具有不同參數(shù)列(參數(shù)的類型、個數(shù)、順序不同)的同名函數(shù),程序會根據(jù)不同的參數(shù)列來確定具體調(diào)用哪個函數(shù)。對于重載函數(shù)的調(diào)用,在編譯期間就已經(jīng)確定,是靜態(tài)的,它們的地址在編譯期間就綁定了與多態(tài)無關(guān)。注意,重載不關(guān)心函數(shù)的返回值類型。
1)doublecalculate(double);
2)doublecalculate(double,double);
3)doublecalculate(double,int);
4)doublecalculate(int,double);
5)doublecalculate(int);
6)floatcalculate(float);
7)floatcalculate(double);
7個同名函數(shù)calculate,1)、2)、3)、4)、5)、6)中任兩個均構(gòu)成重載,6)和7)也能構(gòu)成重載,而1)和7)卻不能構(gòu)成重載,因為1)和7)的參數(shù)相同。
成員函數(shù)被重載的特征如下:
1)相同的范圍(在同一個類中)。
2)函數(shù)名字相同。
3)參數(shù)不同。
4)virtual關(guān)鍵字可有可無。
覆蓋是指派生類中存在重新定義基類的函數(shù),其函數(shù)名、參數(shù)列、返回值類型必須同父類中的相對應(yīng)被覆蓋的函數(shù)嚴格一致,覆蓋函數(shù)和被覆蓋函數(shù)只有函數(shù)體不同,當派生類對象調(diào)用子類中該同名函數(shù)時會自動調(diào)用子類中的覆蓋版本,而不是父類中的被覆蓋函數(shù)版本,它和多態(tài)真正相關(guān)。當子類重新定義了父類的虛函數(shù)后,父類指針根據(jù)賦給它的不同的子類指針,動態(tài)地調(diào)用屬于子類的該函數(shù),這樣的函數(shù)調(diào)用在編譯期間是無法確定的(調(diào)用的子類的虛函數(shù)的地址無法給出)。因此,這樣的函數(shù)地址是在運行期綁定的。
覆蓋的特征如下:
1)不同的范圍(分別位于派生類與基類)。
2)函數(shù)名字相同。
3)參數(shù)相同。
4)基類函數(shù)必須有virtual關(guān)鍵字。
重載與覆蓋的區(qū)別如下:
1)覆蓋是子類和父類之間的關(guān)系,是垂直關(guān)系;重載是同一個類中方法之間的關(guān)系,是水平關(guān)系。
2)覆蓋只能由一個方法,或只能由一對方法產(chǎn)生關(guān)系;方法的重載是多個方法之間的關(guān)系。
3)覆蓋要求參數(shù)列表相同;重載要求參數(shù)列表不同。
4)覆蓋關(guān)系中,調(diào)用方法體是根據(jù)對象的類型(對象對應(yīng)存儲空間類型)來決定的,重載關(guān)系是根據(jù)調(diào)用時的實參表與形參表來選擇方法體的。
程序示例如下:
#include<iostream>
usingnamespacestd;
classBase
{
public:
voidf(intx)
{
cout<<"Base::f(int)"<<x<<endl;
}
voidf(floatx)
{
cout<<"Base::f(float)"<<x<<endl;
}
virtualvoidg(void)
{
cout<<"Base::g(void)"<<endl;
}
};
classDerived:publicBase
{
public:
virtualvoidg(void)
{
cout<<"Derived::g(void)"<<endl;
}
};
intmain()
{
Derivedd;
Base*pb=&d;
pb->f(42);
pb->f(3.14f);
pb->g();
return0;
}
程序輸出結(jié)果:
Base::f(int)42
Base::f(float)3.14
Derived::g(void)
上例中,函數(shù)Base::f(int)與Base::f(float)相互重載,而Base::g(void)被Derived::g(void)覆蓋。
隱藏是指派生類的函數(shù)屏蔽了與其同名的基類函數(shù),規(guī)則如下:
1)如果派生類的函數(shù)與基類的函數(shù)同名,但是參數(shù)不同,則不論有無virtual關(guān)鍵字,基類的函數(shù)都將被隱藏。
2)如果派生類的函數(shù)與基類的函數(shù)同名,并且參數(shù)也相同,但是基類函數(shù)沒有virtual關(guān)鍵字,此時基類的函數(shù)被隱藏。
在調(diào)用一個類的成員函數(shù)時,編譯器會沿著類的繼承鏈逐級地向上查找函數(shù)的定義,如果找到了就停止查找了。所以,如果一個派生類和一個基類都存在同名(暫且不論參數(shù)是否相同)的函數(shù),而編譯器最終選擇了在派生類中的函數(shù),那么就說這個派生類的成員函數(shù)“隱藏”了基類的成員函數(shù),也就是說它阻止了編譯器繼續(xù)向上查找函數(shù)的定義。
回到隱藏的定義中,前面已經(jīng)說了有virtual關(guān)鍵字,并且派生類函數(shù)與基類函數(shù)同名,同參數(shù)函數(shù)構(gòu)成覆蓋的關(guān)系,因此隱藏的關(guān)系只有如下的可能:
1)必須分別位于派生類和基類中。
2)必須同名。
3)參數(shù)不同的時候本身已經(jīng)不構(gòu)成覆蓋關(guān)系了,所以此時是否是virtual函數(shù)已經(jīng)不重要了。
當參數(shù)相同的時候就要看是否有virtual關(guān)鍵字了,有的話就是覆蓋關(guān)系,沒有的時候就是隱藏關(guān)系了。
程序代碼示例如下:
#include<iostream>
usingnamespacestd;
classBase
{
public:
virtualvoidf(floatx)
{
cout<<"Base::f(float)"<<x<<endl;
}
voidg(floatx)
{
cout<<"Base::g(float)"<<x<<endl;
}
voidh(floatx)
{
cout<<"Base::h(float)"<<x<<endl;
}
};
classDerived:publicBase
{
public:
virtualvoidf(floatx)
{
cout<<"Derived::f(float)"<<x<<endl;
}
voidg(intx)
{
cout<<"Derived::g(int)"<<x<<endl;
}
voidh(floatx)
{
cout<<"DeriVed::h(float)"<<x<<endl;
}
};
intmain()
{
Derivedd;
Base*pb=&d;
Derived*pd=&d;
pb->f(3.14f);
pd->f(3.14f);
pb->g(3.14f);
pd->h(3.14f);
return0;
}
程序輸出結(jié)果:
Derived::f(float)3.14
Derived::f(float)3.14
Base::g(float)3.14
Derived::h(float)3.14
上例中,函數(shù)DeriVed::f(float)覆蓋了Base::f(float),函數(shù)Dervied::g(int)隱藏了Base::g(float),而不是重載;函數(shù)Derived::h(float)隱藏了Base::h(float),而不是覆蓋。
14.
是否可以通過絕對內(nèi)存地址進行參數(shù)賦值與函數(shù)調(diào)用正確答案:同一個數(shù)可以通過不同的方式表達出來,對于函數(shù)的訪問,變量的賦值除了直接對變量賦值以外,還可以通過絕對內(nèi)存地址進行參數(shù)賦值與函數(shù)調(diào)用。
1)通過地址修改變量的值。
intx;
int*p;
printf("%x\n",&x);
p=(int*)0x0012ff60;
*p=3;
printf("%d\n",x);
程序的輸出結(jié)果:
12ff60
3
程序首先輸出變量X所在地址為十六進制的0x12ff60(本來應(yīng)該為8位的十六進制數(shù),高位為0則省略掉),然后定義一個指針變量,讓它指向該地址,通過指針變量的值來修改變量x的值。
示例代碼如下:
int*ptr=(int*)0xa4000000;
*ptr=0xaabb;
printf("%d\n",*ptr);
以上程序會崩潰,因為這樣做會給一個指針分配一個隨意的地址,很危險,所以這種做法是不允許的。
2)通過地址調(diào)用函數(shù)的執(zhí)行。
#include<iostream>
usingnamespacestd;
typedefvoid(*FuncPtr)();
voidp()
{
printf("MOP\n");
}
intmain()
{
void(*ptr)();
p();
printf("%x\n",p);
ptr=(void(*)())0x4110f0;
ptr();∥函數(shù)指針執(zhí)行
((void(*)())0x4110f0)();
((FuncPtr)0x4110f0)();
return0;
}
程序執(zhí)行結(jié)果如下:
MOP
4110f0
MOP
MOP
MOP
首先定義一個ptr的函數(shù)指針,第一次通過函數(shù)名調(diào)用函數(shù),輸出Mop,打印函數(shù)的入口地址,函數(shù)的入口地址為4110f0。然后給函數(shù)指針ptr賦地址值為P的入口地址,調(diào)用ptr,輸出Mop。接著的過程不通過函數(shù)指針直接執(zhí)行,仍然使用P的入口地址調(diào)用,輸出為MOP。最后是通過typedef調(diào)用的直接執(zhí)行。
函數(shù)名稱、代碼都是放在代碼段的,因為放在代碼段,每次會跳到相同的地方,但參數(shù)會壓棧,所以函數(shù)只根據(jù)函數(shù)名來獲取入口地址,與參數(shù)和返回值無關(guān)。無論參數(shù)和返回值如何不同,函數(shù)入口地址都是一個地方。
對以下程序進行分析:
#include<stdio.h>
intp(inta,intb)
{
return3;
}
intmain()
{
printf("%x\n",p);
inta=p(2,3);
prinftf("%d\n",p);
intb=p(4,5);
printf("%x\n",p);
return0;
}
程序輸出結(jié)果如下:
411159
4264281
411159
十六進制的411159轉(zhuǎn)換成十進制的值為4264281。程序中打印的P的入口地址,無論P是否調(diào)用函數(shù),入口地址都沒有改變。
分析如下代碼:
#include<stdio.h>
intp(inta,intb)
{
return((a>b)?a:b);
}
intmain()
{
int(*ptr)(int,int);
ptr=(int(*)(int,int))0x411159;
intc=ptr(5,6);
printf("%d\n",c);
return0;
}
程序輸出結(jié)果:
6
通過函數(shù)指針調(diào)用有返回值和參數(shù)的函數(shù),不使用函數(shù)名,而是用函數(shù)入口地址調(diào)用。
函數(shù)存放在內(nèi)存的代碼區(qū)域內(nèi),也有地址,一個函數(shù)在編譯時被分配一個入口地址,將這個入口地址稱為函數(shù)的指針,函數(shù)的地址就是函數(shù)的名字。函數(shù)指針不能指向不同類型或是帶不同形參的函數(shù)。
15.
默認構(gòu)造函數(shù)是否可以調(diào)用單參數(shù)構(gòu)造函數(shù)正確答案:默認構(gòu)造函數(shù)不可以調(diào)用單參數(shù)的構(gòu)造函數(shù)。程序示例如下:
#include<iostream>
usingnamespacestd;
classA
{
public:
A()
{
A(0);
Print();
}
A(intj):i(j)
{
printf("CallA(intj)\n");
}
voidPrint()
{
printf("CallPrint()\n");
}
inti;
};
intmain()
{
Aa;
cout<<a.i<<endl;
return0;
}
程序輸出結(jié)果:
CallA(intj)
CallPrint()
-858993460
以上代碼希望默認構(gòu)造函數(shù)調(diào)用帶參構(gòu)造函數(shù),可是卻未能實現(xiàn)。因為在默認構(gòu)造函數(shù)內(nèi)部調(diào)用帶參的構(gòu)造函數(shù)屬用戶行為而非編譯器行為,它只執(zhí)行函數(shù)調(diào)用,而不會執(zhí)行其后的初始化表達式。只有在生成對象時,初始化表達式才會隨相應(yīng)的構(gòu)造函數(shù)一起調(diào)用。
16.
C++中函數(shù)調(diào)用有哪幾種方式正確答案:編譯器一般使用堆棧實現(xiàn)函數(shù)調(diào)用。堆棧是存儲器的一個區(qū)域,嵌入式環(huán)境有時需要程序員自己定義一個數(shù)組作為堆棧。Windows為每個線程自動維護一個堆棧,堆棧的大小可以設(shè)置。編譯器使用堆棧來存放每個函數(shù)的參數(shù)、局部變量等信息。
由于函數(shù)調(diào)用經(jīng)常會被嵌套,在同一時刻,堆棧中會存儲多個函數(shù)的信息,每個函數(shù)又占用一個連續(xù)的區(qū)域,一個函數(shù)占用的區(qū)域常被稱為幀(frame),編譯器是從高地址開始使用堆棧的,在多線程(任務(wù))環(huán)境,CPU的堆棧指針指向的存儲器區(qū)域就是當前使用的堆棧。切換線程的一個重要工作,就是將堆棧指針設(shè)為當前線程的堆棧棧頂?shù)刂?。不同CPU,不同編譯器的堆棧布局、函數(shù)調(diào)用方法都可能不同,但堆棧的基本概念是一樣的。
當一個函數(shù)被調(diào)用時,進程內(nèi)核對象為其在進程的地址空間的堆棧部分分配一定的棧內(nèi)存給該函數(shù)使用,函數(shù)堆棧功能如下:
1)在進入函數(shù)之前,保存“返回地址”和環(huán)境變量。返回地址是指該函數(shù)結(jié)束后,從進入該函數(shù)之前的那個地址繼續(xù)執(zhí)行下去。
2)在進入函數(shù)之后,保存實參或?qū)崊?fù)制、局部變量。
函數(shù)原型:[連接規(guī)范]函數(shù)類型[調(diào)用約定]函數(shù)名參數(shù)列表{......}
調(diào)用約定:調(diào)用約定是決定函數(shù)實參或?qū)崊?fù)制進入和退出函數(shù)堆棧的方式以及函數(shù)堆棧釋放的方式,簡單地講就是實參或?qū)崊?fù)制入棧、出棧、函數(shù)堆棧釋放的方式。在Win32下有以下4種調(diào)用:
①_cdecl:它是C/C++的默認調(diào)用方式。實參是以參數(shù)列表從右依次向左入棧,出棧相反,函數(shù)堆棧由調(diào)用方來釋放,主要用在那些帶有可變參數(shù)的函數(shù)上,對于傳送參數(shù)的內(nèi)存棧是由調(diào)用者來維護的。另外,在函數(shù)名修飾約定方面也有所不同。由于每~個調(diào)用它的函數(shù)都包含清空堆棧的代碼,所以產(chǎn)生的可執(zhí)行文件大小會比調(diào)用_stdcall函數(shù)的大。
②_stdcall:它是WINAPI的調(diào)用約定,其實COM接口等只要是申明定義接口都要顯示指定其調(diào)用約定為_stdcall。實參以參數(shù)列表從右依次向左入棧,出棧相反。函數(shù)堆棧是由被調(diào)用方自己釋放的。但是若函數(shù)含有可變參數(shù),那么即使顯示指定了_stdcall,編譯器也會自動把其改變成_cdecl。
③_thiscall:它是類的非靜態(tài)成員函數(shù)默認的調(diào)用約定,其不能用在含有可變參數(shù)的函數(shù)上,否則編譯會出錯。實參以參數(shù)列表從右依次向左入棧,出棧相反。函數(shù)堆棧是由被調(diào)用方自己釋放的。但是類的非靜態(tài)成員函數(shù)內(nèi)部都隱含有一個this指針,該指針不是存放在函數(shù)堆棧上,而是直接存放在CPU寄存器上。
④_fastcall:陜速調(diào)用。它們的實參并不是存放在函數(shù)堆棧上,而是直接存放在CPU寄存器上,所以不存在入棧、出棧、函數(shù)堆棧釋放。
需要注意的是,全局函數(shù)或類靜態(tài)成員函數(shù),若沒指定調(diào)用,約定默認是_cdecl或是IDE設(shè)置的。
17.
什么是可重入函數(shù)?C語言中如何寫可重入函數(shù)正確答案:可重入函數(shù)是指能夠被多個線程“同時”調(diào)用的函數(shù),并且能保證函數(shù)結(jié)果正確性的函數(shù)。
在C語言中編寫可重入函數(shù)時,盡量不要使用全局變量或靜態(tài)變量,如果使用了全局變量或靜態(tài)變量,就需要特別注意對這類變量訪問的互斥。一般采用以下幾種措施來保證函數(shù)的可重入性:信號量機制、關(guān)調(diào)度機制、關(guān)中斷機制等方式。
需要注意的是,不要調(diào)用不可重入的函數(shù),當調(diào)用了不可重入的函數(shù)時,會使該函數(shù)也變?yōu)椴豢芍厝氲暮瘮?shù)。一般驅(qū)動程序都是不可重入的函數(shù),因此在編寫驅(qū)動程序時一定要注意重入的問題。
18.
inta[2][2]={{1},{2,3}},則a[0][1]的值是多少正確答案:要弄清楚這個問題,需要弄清楚二維數(shù)組默認初始化的問題。原則上來說,二維數(shù)組在內(nèi)存中既可以按行存儲,也可以按列存儲,但一般是按行存放的,即先存儲第一行的數(shù)組元素的值,再按順序存放第二行的數(shù)組元素的值,以此類推。以數(shù)組a[m][n]為例,按行存儲,在內(nèi)存中的結(jié)構(gòu)如下:
a[0][0]→a[0][1]→a[0][2]→a[0][3]→a[1][0]→a[1][1]→a[1][2]→a[1][3]→a[2][0]→a[2][1]→...
而對二維數(shù)組的初始化,其初始化的形式如下:
數(shù)據(jù)類型數(shù)組名[整常量表達式][整常量表達式]={初始化數(shù)據(jù)};在{}中給出各數(shù)組元素的初值,各初值之間用逗號分開。把{}中的初值依次賦給各數(shù)組元素。
二維數(shù)組的初始化一般有兩種方式,第一種方式是按行來執(zhí)行(如intarray[2][3]={{0,0,1),{1,0,0));),而第二種方式是把數(shù)值寫在一塊(如intarray[2][3]={0,0,1,1,0,0);)。
此時存在一種情況,即只對部分元素進行初始化,當數(shù)組中非零元素比較少時,則可以對部分元素賦初值,未賦值的元素自動為0。例如,inta[3][4]={{1),{5),{9}},則數(shù)組中各值可以表示如下:
1000
5000
9000
再例如inta[4][5]={{1,2},{},{0,1,3}},則各值表示如下:
12000
00000
01300
00000
再例如inta[2][3]={{5,6},{7,8}},則二維數(shù)組的存儲如下:
560
780
再例如inta[2][3]={5,6,7,8},則按行存儲格式如下:
567
800
本題中,由于數(shù)組a是一個二維數(shù)組,第一行第一個數(shù)是1,第二行第一個數(shù)是2,第二個數(shù)是3,其他位置數(shù)的值自動補0,所以第一行第二個數(shù)也就是a[0][1],它的值是0。
有一種情況比較特殊,如果對二維數(shù)組的所有元素都賦值,則數(shù)組的第一維的長度可以不指定,但第二維的長度不能省。
例如,intm[][3]={1,2,3,4,5,6,7,8,9},則默認第一維的維數(shù)值為3。
intm[][3]={1,2,3,4,5,6,7},則默認第一維的維數(shù)值為3,其中m[2][1]=m[2][2]=0。
intm[][3]={{0,0,2},{},{0,2}},則默認第一維的維數(shù)值為3。
需要注意的是,只有在進行帶有初始化數(shù)據(jù)的數(shù)組說明時,才允許省略第一維長度,在僅僅進行說明數(shù)組而未進行初始化數(shù)組元素時省略長度是錯誤的,因為編譯系統(tǒng)無法預(yù)知數(shù)組大小,如inta[][4]就是錯誤的。
19.
如何合法表示二維數(shù)組正確答案:對于數(shù)組a[3][4]、*(a[1]+1)、*(&a[1][1])、(*(a+1))[1]和*(a+5)四種表示方法中,哪個不能表示a[1][1]?
第一個可以,因為a[1]是第一行的地址,a[1]+1偏移一個單位然后解引用取值,得到a[1][1]。第二個也可以,[]優(yōu)先級高,a[1][1]取地址再取值。第三個a+1相當于&a[1],所以+(a+1)=a[1],因此+*(a+1)[1]=a[1][1]。第四個a+5相當于&a[5],單從這里看就已經(jīng)越界了,所以不可以。
20.
a是數(shù)組,(int*)(&a+1)表示什么意思正確答案:對于數(shù)組而言,一個數(shù)組名代表的含義是數(shù)組中第一個元素的位置,即地址,通過數(shù)組名可以訪問該數(shù)組。程序示例如下:
#include<stdio.h>
intmain()
{
inta[5]={1,2,3,4,5};
intb[100]
int*ptr=(int*)(&a+1);
printf("%d\n%d\n",*(a+1),*(ptr-1));
printf("sizeof(b)=%d\n",sizeof(b));
printf("sizeof(&b)=%d\n",sizeof(&b));
return0;
}
程序輸出結(jié)果:
2
5
slzeot(b)=400
sizeof(&b)=400
一般而言,對指針進行加1操作,得到的將是下一個元素的地址,一個類型為T的指針移動,是以sizeof(T)為移動單位,如果ptr=a+1,那么最終輸出*(ptr-1)的值肯定是2,1。而ptr=&a+1,輸出則變?yōu)?,5。&a是數(shù)組指針,是一個指向int(*)[5]的指針,但是這時候ptr相當于int*[5],也就是指向了一個含有5個元素的數(shù)組,這也就不難解釋為什么第二個打印出來&b的大小也是400了,sizeof(b)打印出來的是b數(shù)組的大小。sizeof(&b)同樣
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年二手房的購房合同經(jīng)典版(三篇)
- 2025年度家政服務(wù)人員勞動合同標準模板
- 2025年度市政道路改造工程土石方運輸服務(wù)合同
- 2025年度企業(yè)債券發(fā)行合同的有償融資與償債保障措施
- 2025年度公司私人長期借款合同中的合同履行期限
- 2025年度環(huán)保型空調(diào)機組設(shè)計、安裝及運行維護合同
- 2025年度建筑勞務(wù)清包工質(zhì)量監(jiān)督合同樣本
- 2025年二手房購房買賣合同(2篇)
- 2025年度綠色生態(tài)農(nóng)業(yè)建設(shè)項目施工合同(WPS高端版)
- 2025年度環(huán)保項目掛靠實施合同
- 初中英語-Unit2 My dream job(writing)教學設(shè)計學情分析教材分析課后反思
- 2023湖南株洲市茶陵縣茶陵湘劇保護傳承中心招聘5人高頻考點題庫(共500題含答案解析)模擬練習試卷
- 廣州市勞動仲裁申請書
- 江西省上饒市高三一模理綜化學試題附參考答案
- 23-張方紅-IVF的治療流程及護理
- 頂部板式吊耳計算HGT-20574-2018
- 因數(shù)和倍數(shù)復(fù)習思維導(dǎo)圖
- LY/T 2986-2018流動沙地沙障設(shè)置技術(shù)規(guī)程
- GB/T 16288-1996塑料包裝制品回收標志
- 三級教育考試卷(電工)答案
- 醫(yī)院標準化運營管理課件
評論
0/150
提交評論