日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

西邮Linux兴趣小组2019-2021年纳新面试题解析

發(fā)布時(shí)間:2023/12/14 linux 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 西邮Linux兴趣小组2019-2021年纳新面试题解析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

2019年

2020年

2021年


2019年


1、

?

結(jié)果:無(wú)限打印“ = ”

大家都知道在碼長(zhǎng)為16位的情況下,int 的取值范圍為-32768~32767,而unsinged int即無(wú)符號(hào)整形的范圍為0~65535。

那么問(wèn)題來(lái)了哈,unsigned int類型如果遇到的賦值為負(fù)數(shù),它會(huì)怎么處理呢?

答案是它會(huì)以補(bǔ)碼的形式轉(zhuǎn)換表示。

回歸到本題:當(dāng)for循環(huán)最后一次將unsigned int類型的i自減為-1時(shí),此時(shí)i將會(huì)變成其十進(jìn)制補(bǔ)碼:65535。而跳脫for循環(huán)的條件是i<0,但每每i想要成為負(fù)數(shù)的那一刻,啪,直接給轉(zhuǎn)為正數(shù)補(bǔ)碼了,所以無(wú)法結(jié)束這個(gè)循環(huán),也就只能一直打印“ = ”了。


2、

?

?(1)主要引用了一個(gè)中間變量c,可看作它是一個(gè)空杯子,相當(dāng)于一個(gè)交換媒介。先把a(bǔ)的值暫存于“空杯子"c中,再把b的值賦給a,最后將”空杯子“c(也就是a)的值賦給b,即完成了數(shù)值交換的操作。

(2)這就是一個(gè)數(shù)學(xué)上的簡(jiǎn)單交換障眼術(shù)。

(3)使用異或進(jìn)行解決。對(duì)于兩個(gè)值來(lái)說(shuō),異或結(jié)果為:同0異1

例如兩個(gè)整數(shù)a=10100001,b=00000110,可通過(guò)下列語(yǔ)句實(shí)現(xiàn)交換:

  a = a^b;   //a=10100111

  b = b^a;   //b=10100001

  a = a^b;   //a=00000110

于是a、b就這樣交換了數(shù)值。


3、

?

?結(jié)果:輸出結(jié)果不一致,每調(diào)用一次函數(shù),a值就遞增1,而b值始終為1。

這道題主要涉及局部變量和static關(guān)鍵字的作用。我們?cè)诔绦蛑卸啻握{(diào)用的輸出如下:

?

?

首先我們知道a、b都定義在函數(shù)體內(nèi),為局部變量。而局部變量一旦離開(kāi)了定義它的代碼塊時(shí),聲明周期結(jié)束,變量就會(huì)被銷(xiāo)毀,無(wú)法再繼續(xù)使用。 (所以每調(diào)用一次函數(shù)結(jié)束時(shí),b都會(huì)被清零,b固然每次都為1)

那憑什么a每次都能遞增?那是因?yàn)榇藭r(shí)對(duì)a設(shè)定了“static”—a就變成了局部靜態(tài)變量

局部靜態(tài)變量

作用域同樣為局部作用域,同樣也是當(dāng)定義它的函數(shù)結(jié)束時(shí),作用域結(jié)束。但是!當(dāng)局部靜態(tài)變量離開(kāi)作用域后,其本身數(shù)據(jù)并沒(méi)有被銷(xiāo)毀,而是留在了內(nèi)存當(dāng)中。直到該函數(shù)再次被調(diào)用,此值就會(huì)在原值的基礎(chǔ)上做改變

也就是說(shuō),每調(diào)用一次函數(shù)對(duì)a實(shí)現(xiàn)++時(shí),最后的結(jié)果都被保存在了內(nèi)存中沒(méi)被清零,因此a呈遞增狀態(tài)。 ( static就相當(dāng)于局部變量值的“保護(hù)傘”啦!)


4、

?

結(jié)果:Xiyou Linux Group2019

主要考察printf函數(shù)的返回值

printf函數(shù)返回其成功打印字符的個(gè)數(shù)包括空格、換行符'\n'本題的輸出自右往左,第一個(gè)輸出沒(méi)有內(nèi)容,因此'%d'處的值為0;接著第二個(gè)函數(shù)輸出為'Xiyou Linux Group20';第三處使用第二處打印的返回值(打印字符個(gè)數(shù))為19。最后連起來(lái)即為輸出結(jié)果。


5、

?

?結(jié)果:-1 0

(a是啥!a沒(méi)聲明也沒(méi)初始化又不能當(dāng)作常量賦值,就導(dǎo)致編譯錯(cuò)誤了,小問(wèn)題這應(yīng)該是題目不小心打錯(cuò)了)修改后代碼如下:

?

?

本題和第一題類似,都是考察數(shù)據(jù)類型的范圍越界問(wèn)題

  • unsigned char(無(wú)符號(hào)型)的范圍為0~255
  • signed char即 char(有符號(hào)型)的范圍為-128~127。

那么為什么這里的255輸出為-1呢?是不是char類型的問(wèn)題?那接下來(lái)我們?cè)囋嚒?55”對(duì)于int類型和char類型的輸出分別是多少:

int main(int argc,char *argv[]) {char ch = 255;int sh = 255;printf("ch = %d\nsh = %d\n",ch,sh); }

結(jié)果輸出為:

?

我們看到int類正常輸出255,而char類輸出卻為-1

首先在計(jì)算機(jī)中,存儲(chǔ)數(shù)據(jù)采用補(bǔ)碼的形式存儲(chǔ)

所以當(dāng)255作為int類型來(lái)聲明時(shí),因?yàn)闆](méi)有超出int類型的有效范圍,并且作為正數(shù)的255的二進(jìn)制原反補(bǔ)碼都相同,即都為1111 1111(255),因此以int類型來(lái)定義時(shí)其輸出值為255;

再來(lái)看看,當(dāng)255以char類型來(lái)聲明時(shí),因?yàn)橛蟹?hào)char型范圍是-128~127:

?

完全可以將其范圍看作是一個(gè)頭接尾的圓圈,一個(gè)圈所容納的數(shù)據(jù)數(shù)量恰好為256(127+128還有0的存在再+1),也就代表著我們可以把255當(dāng)作是這個(gè)圈內(nèi)的第255個(gè)元素,也就是-1了。

用式子表達(dá)就是:在255中,當(dāng)?shù)?27+1越界后變?yōu)?128(即跳到負(fù)數(shù)表達(dá)范圍內(nèi)),-128再與((255-127)-1)相加=(-128+127)=-1。


6、

?

結(jié)果為:x=-1, y=4, t=-1 // x=0, y=5, t=1

本題對(duì)于x和y的輸出很簡(jiǎn)單,就是普通的加操作。重點(diǎn)在于t的兩個(gè)輸出,這其中涉及的兩個(gè)運(yùn)算符是:‘ | ’和‘ || ’

運(yùn)算符|

指的是對(duì)兩數(shù)的二進(jìn)制補(bǔ)碼進(jìn)行按位或運(yùn)算,有1則1,注意最后的結(jié)果也為補(bǔ)碼形式。(‘ & ’有0則0)

邏輯運(yùn)算符||

如果兩個(gè)數(shù)中有一個(gè)為非零,則為真(1)。(‘ && ’兩個(gè)數(shù)都為非零才為真)

同時(shí)注意進(jìn)行運(yùn)算時(shí),x先++再(運(yùn)算/判斷),y先(運(yùn)算/判斷)再++

也就是說(shuō)當(dāng)執(zhí)行代碼段第三行時(shí),x值為-1、y值為3,此時(shí)進(jìn)行按位或運(yùn)算。-1的二進(jìn)制補(bǔ)碼表示為1111 1111,誰(shuí)碰上它進(jìn)行或運(yùn)算每位都必為1,最終結(jié)果都必為-1(1111 1111 ),因此第一個(gè)t的輸出為-1。

同理,當(dāng)執(zhí)行代碼段第五行時(shí),x的值為0、y值為4,此時(shí)進(jìn)行邏輯或判斷。判斷開(kāi)始時(shí)已判斷x為正,因此邏輯判斷為真返回1,所以第二個(gè)t的輸出為1。


7、

?

瞧這個(gè)大坑,你是不是看到“輸出為4”的坑就呼呼往里頭栽?(反正我是)

看看人家輸出:

?

?人家輸出的是3。

不要以為這里面的 X*X =(1+1)*(1+1)= 4,這是人腦思維。計(jì)算機(jī)在#define宏定義的時(shí)候接受的是X = a*b,那它在后續(xù)計(jì)算的時(shí)候只會(huì)傻愣往里頭代 a*b 而不是(a*b),所以此時(shí)X*X = 1+1*1+1=3。

你的初衷如果是想要它輸出4,那你就要把宏定義改為:#define X (a+b),手動(dòng)加括號(hào)。


8、

?

?

輸出結(jié)果為:

?

  • 定義int型變量val,將val賦值為2018
  • 定義int*類型指針pi,并讓pi指向‘2019’所在地址
  • 把val的地址賦給pi
  • 通過(guò)pi指針,將0賦值給val(通過(guò)指針pi修改了val的值)
  • 9、

    ?輸出結(jié)果:

    ?

    *q = p使得p和q同時(shí)指向了malloc出來(lái)的這塊空間,q里存放著p的地址。所以當(dāng)輸入‘Xiyou’時(shí),‘Xiyou’存放在這塊內(nèi)存中;但當(dāng)‘Linux’輸入后,內(nèi)容刷新,覆蓋了前面的‘Xiyou’,因此輸出結(jié)果為兩個(gè)Linux。


    10、

    輸出的結(jié)果為:

    ?

    第一個(gè)打印的兩個(gè)值相同。a用%p打印表示數(shù)組的首地址,&a表示數(shù)組首元素的地址。

    第二個(gè)打印的值都發(fā)生了變化。a+1 是數(shù)組下一元素的首地址,即a[1]的首地址。因?yàn)闉閕nt類型,所以增加了4個(gè)字節(jié);&a+1 是下一個(gè)數(shù)組的首地址


    11、

    實(shí)現(xiàn)代碼如下:

    #include <stdio.h> int feib(int n) {if (n==1||n==2)return 1;else{return feib(n-1)+feib(n-2);} } int main() {int a;scanf("%d", &a);printf("斐波那契數(shù)列第%d項(xiàng)的值為:%d\n", a, feib(a));return 0; }

    ?12、

    上述代碼為冒泡排序

    冒泡排序顧名思義就是通過(guò)元素的兩兩比較,判斷是否符合要求,如不符合就交換位置來(lái)達(dá)到排序的目的。就像它的名字,在交換過(guò)程中,各個(gè)數(shù)值類似水冒泡,小(大)的元素經(jīng)過(guò)不斷的交換由水底慢慢的浮到水的頂端。通過(guò)重復(fù)的循環(huán)訪問(wèn)數(shù)組,直到?jīng)]有可以交換的元素,那么整個(gè)排序就已經(jīng)完成了。

    冒泡的三種優(yōu)化:

    (1)泡泡到頂以后再也不進(jìn)行掃描:減少無(wú)效掃描

    #include <stdio.h> void bubble_sort(int arr[],int sz) {int i = 0;int t;for (i = 0; i < sz-1; i++)//進(jìn)行元素個(gè)數(shù)減一次排序{int j;for (j = 0; j <sz-1-i; j++)//每次排序次數(shù)比較次數(shù)減少i個(gè),也就是不再對(duì)已經(jīng)上浮到位的元素進(jìn)行比較{if (arr[j] > arr[j + 1]){t = arr[j];arr[j] = arr[j + 1];arr[j + 1] = t;}elsecontinue;}} } int main() {int i=0;int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };//排為升序int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr,sz);//冒泡排序函數(shù)for (i = 0; i < sz; i++){printf("%d\n", arr[i]);} return 0; }

    (2)如果檢測(cè)到一整輪沒(méi)有發(fā)生交換,則直接跳出結(jié)束排序。

    #include <stdio.h> void bubble_sort(int arr[],int sz) {int i = 0;int t;for (i = 0; i < sz-1; i++)//進(jìn)行元素個(gè)數(shù)減一次排序{int flag = 1;//定義一個(gè)整型變量一會(huì)用來(lái)判斷if語(yǔ)句int j;for (j = 0; j <sz-1-i; j++)//冒泡雙重for嵌套循環(huán){if (arr[j] > arr[j + 1]){t = arr[j];arr[j] = arr[j + 1];arr[j + 1] = t;flag = 0;//程序如果沒(méi)來(lái)這,說(shuō)明這一次掃描沒(méi)有發(fā)生過(guò)換位,所以可以直接結(jié)束排序}elsecontinue;}if (flag == 1){break;}} } int main() {int i=0;int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };//排為升序int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr,sz);//冒泡排序函數(shù)for (i = 0; i < sz; i++){printf("%d\n", arr[i]);} return 0; }

    (3)冒泡的同時(shí)把“水”沉下去:上浮+下沉的雙向排序(雞尾酒排序)

    雞尾酒排序:冒泡排序的每一輪都是從左至右來(lái)比較元素,進(jìn)行單向的位置交換。那么雞尾酒排序做了怎樣的優(yōu)化呢?雞尾酒排序的元素和交換過(guò)程是雙向的。

    下面舉一個(gè)例子:有數(shù)組[2, 3, 4, 5, 6, 7, 8, 1],希望對(duì)其進(jìn)行從小到大的排序。如果按照冒泡排序的思想去做的話,元素2, 3, 4, 5, 6, 7, 8已經(jīng)是有序的了,只有元素1的位置不對(duì),卻還要進(jìn)行7輪排序!雞尾酒排序正是要解決這個(gè)問(wèn)題的。

    雞尾酒排序過(guò)程:
    第1輪:和冒泡排序一樣,從左至右依次比較并交換;
    第2輪:反過(guò)來(lái),從右至左依次比較并交換;
    第3輪:同第1輪...
    第4輪:同第2輪...
    直到某一輪排序中沒(méi)有元素進(jìn)行交換,證明已經(jīng)有序,排序結(jié)束。這就是雞尾酒排序的思路。排序過(guò)程就像鐘擺一樣,從左至右,從右至左,循環(huán)往復(fù)直至有序。

    #include <stdio.h> void bubble_sort_down(int arr[], int sz)//冒泡排序的雙向掃描 {int i = 0;int t;int m;for (i = 0; i < (sz)/2; i++)//進(jìn)行元素個(gè)數(shù)減一次排序,雙向排序中可以讓排序次數(shù)減半{int flag = 1;//定義一個(gè)整型變量一會(huì)用來(lái)判斷if語(yǔ)句int j;int k;for (j = 0; j < sz - 1 - i; j++)//每次排序次數(shù)比較次數(shù)減少i個(gè),也就是不再對(duì)已經(jīng)上浮到位的元素進(jìn)行比較{if (arr[j] > arr[j + 1]){t = arr[j];arr[j] = arr[j + 1];arr[j + 1] = t;flag = 0;//程序如果沒(méi)來(lái)這,說(shuō)明這一次掃描沒(méi)有發(fā)生過(guò)換位,所以可以直接結(jié)束排序}}for (k = sz -1-i; k > 0; k--)//從小到大排{if (arr[k] < arr[k - 1]){m = arr[k];arr[k] = arr[k - 1];arr[k - 1] = m;flag = 0;}}if (flag == 1){break;}} } int main() {int i = 0;int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };//排為升序int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort_down(arr, sz);//冒泡排序函數(shù)for (i = 0; i < sz; i++){printf("%d\n", arr[i]);}return 0; }

    其他排序方法還有選擇排序、快速排序、直接插入排序、希爾排序、歸并排序、基數(shù)排序和堆排序。


    13、

    • 大端模式:指高位字節(jié)放在內(nèi)存的低地址端,低位字節(jié)放在內(nèi)存的高地址端。這樣的存儲(chǔ)模式有點(diǎn)類似于把數(shù)據(jù)當(dāng)作字符串順序處理:地址由小向大增加,而數(shù)據(jù)從高位往低位放;這和我們的閱讀習(xí)慣一致。

    低地址 --------------------> 高地址
    0x12? |? 0x34? |? 0x56? |? 0x78

    • 小端模式:指低位字節(jié)放在內(nèi)存的低地址端,高位字節(jié)放在內(nèi)存的高地址端。這種存儲(chǔ)模式將地址的高低和數(shù)據(jù)位權(quán)有效地結(jié)合起來(lái),高地址部分權(quán)值高,低地址部分權(quán)值低。

    低地址 --------------------> 高地址
    0x78? |? 0x56? |? 0x34? |? 0x12

    采用大端方式進(jìn)行數(shù)據(jù)存放符合人類的正常思維

    采用小端方式進(jìn)行數(shù)據(jù)存放利于計(jì)算機(jī)處理

    下面這段代碼可以用來(lái)測(cè)試一下你的編譯器是大端模式還是小端模式:

    #include <stdio.h> int main() {short int x;char x0,x1;x=0x1122;x0=*((char*)&x); //低地址單元 ,或者((char*)&x)[0];x1=*((char*)&x + 1); //高地址單元,或者((char*)&x)[1];printf("x0=%x\nx1=%x\n",x0,x1); }

    若x0=0x11,則是大端;若x0=0x22,則是小端。

    我的是小端模式 。


    ?14、

    詳見(jiàn)博客:

    初探Linux操作系統(tǒng)與文件_Yan__Ran的博客-CSDN博客



    2020年


    1、

    ?結(jié)果:>

    上面的代碼沒(méi)有對(duì)全局變量i進(jìn)行初始化,因此編譯器自動(dòng)默認(rèn)其值為0。經(jīng)過(guò)i--的操作i變?yōu)?1,同時(shí)sizeof(i)的值為int類型所占字節(jié)數(shù)為4。

    但要注意一點(diǎn):sizeof返回值為無(wú)符號(hào)數(shù),而i為有符號(hào)數(shù)。當(dāng)有符號(hào)數(shù)遇到無(wú)符號(hào)數(shù)時(shí)將會(huì)被強(qiáng)制轉(zhuǎn)換為無(wú)符號(hào)數(shù),那么i將會(huì)變成一個(gè)非常大的數(shù)。


    2、

    結(jié)果:11

    這題和上面那套2019年納新題的第7題一個(gè)道理,#define宏定義直接替換C為A*B=2+2*3+3=11,不多描述。


    3、

    結(jié)果:26 27

    主要考察strlen函數(shù)和sizeof關(guān)鍵字的輸出。strlen函數(shù)主要用于計(jì)算字符長(zhǎng)度,而sizeof主要用于計(jì)算所占內(nèi)存數(shù)兩值相差1是因?yàn)閟tr是一個(gè)字符串,在字符串末端會(huì)有一個(gè)‘ \0 ’,sizeof關(guān)鍵字會(huì)計(jì)算它。


    4、

    ?多次執(zhí)行該函數(shù),結(jié)果為:

    本題與2019年面試題第3題同理,涉及static設(shè)置局部靜態(tài)變量保留上一次執(zhí)行的值的知識(shí)點(diǎn),不多描述。


    5、

    輸出測(cè)試:

    可以看得出來(lái)這串代碼的作用是將十進(jìn)制數(shù)轉(zhuǎn)化為32位的二進(jìn)制數(shù)。下面我們來(lái)看看這串代碼:

    這里面涉及兩個(gè)運(yùn)算符:左移運(yùn)算符<<和右移運(yùn)算符>>

    • << 左移運(yùn)算符

    V << N,表示數(shù)值V左移N位

    右邊空出的位用0填補(bǔ),高位左移溢出則舍棄。

    例如:3的二進(jìn)制是0000 0011。如果我們把這個(gè)數(shù)值往左移動(dòng)2位,即3<<2,那么就得到0000 1100,此時(shí)表示的十進(jìn)制是12。

    數(shù)值N往左移動(dòng)X位,得到是數(shù)值是N * (2^X),即N乘以2的X次方。

    • >> 右移運(yùn)算符

    V >> N,表示數(shù)值V右移N位

    左邊空出的高位用0或者1補(bǔ)(正數(shù)用0補(bǔ),負(fù)數(shù)用1補(bǔ)),低位右移溢出則舍棄。

    例如:8的二進(jìn)制是 0000 1000。如果把它往右移動(dòng)2位,即8>>2,那么低字節(jié)的2位移出,高位補(bǔ)0,就得到0000 0010,就是2的數(shù)值。

    數(shù)值N往右移動(dòng)X位,得到是數(shù)值是N / (2^X),即N除以2的X次方。

    下面我們來(lái)看一下題中的代碼:

    #include <stdio.h> int main(int argc, char *argv[]) {int number;unsigned mask;mask = 1u << 31; //1u表示將1轉(zhuǎn)化為unsigned int即無(wú)符號(hào)整型scanf("%d", &number);while(mask) //當(dāng)mask不為0時(shí)進(jìn)入循環(huán){printf("%d",(number & mask) ? 1 : 0); //number和mask按位與運(yùn)算(有0則為0)mask>>=1;}return 0; }

    首先第五行操作先將1轉(zhuǎn)化為無(wú)符號(hào)整型并將其32位編碼向左移31位,即最終得到1000..00(32位),并將其賦值給mask。接下來(lái)作while循環(huán),當(dāng)mask不為0時(shí)進(jìn)入循環(huán)。接下來(lái)作運(yùn)算符判斷:

    (number & mask) ? 1 : 0mask>>=1;

    這是代碼的核心部分。number和mask作與運(yùn)算 ,并且每進(jìn)行一次運(yùn)算就對(duì)mask做一次右移操作(使得1移動(dòng))。這套操作主要是想要通過(guò)與運(yùn)算(都為1時(shí)才為1)以及對(duì)1右移實(shí)現(xiàn)對(duì)輸入數(shù)字二進(jìn)制編碼的遍歷,從而一個(gè)一個(gè)輸出對(duì)應(yīng)的0或1,輸出的值就是number所對(duì)應(yīng)的32位二進(jìn)制編碼了


    6、

    結(jié)果:Y

    *str指向字符串的首字母X,而X+1則表示為X的下一個(gè)字母即Y。


    7、

    結(jié)果:XiyouLinuxGroup

    • 判斷浮點(diǎn)數(shù)是否相同:

    判斷兩個(gè)浮點(diǎn)數(shù)時(shí)需要注意兩者類型的統(tǒng)一。像"float"和"double"雖然都表示浮點(diǎn)數(shù),但兩者的精度不同。double精度高,有效16位,而float的精度為7位。(但double消耗內(nèi)存是float的兩倍,所以double的運(yùn)算速度比f(wàn)loat慢得多。)

    第二行中的float b=a看似將a和b等價(jià)了起來(lái),但因?yàn)榫炔煌瑢?dǎo)致了a精度的缺失。

    第一個(gè)if判斷中"(float)a"的操作對(duì)a進(jìn)行了暫時(shí)的強(qiáng)制轉(zhuǎn)換所以a與b能相等;反觀第二個(gè)if判斷,沒(méi)有對(duì)a、b中任意一個(gè)值進(jìn)行強(qiáng)制轉(zhuǎn)換類型,所以類型不相同,成立第二個(gè)if的判斷條件打印其內(nèi)容。


    8、

    結(jié)果:Xiyou Linux Group 2020 ?

    數(shù)組a中表示的是ASCII碼的值,打印出來(lái)為Xiyou Linux Group 20。其中最后一個(gè)元素0被讀取為NULL,使該字符串的輸出停止。最外層打印讀取有效字符數(shù)為20。


    9、

    結(jié)果:2 0 2 0

    考察多維數(shù)組的補(bǔ)位問(wèn)題。來(lái)看看前兩個(gè)數(shù)組的結(jié)構(gòu):

    ?c、d數(shù)組同理,最后即得輸出2 0 2 0。


    10、

    ?結(jié)果:1

    屬實(shí)是套中套中套了。首先一個(gè)指針指向了一個(gè)char類型指針,而這個(gè)char類型指針又指向a的地址。所以返回去究其根本指向的還是a,所以最后打印a的值1。


    11、

    取消第三行const注釋后,a將會(huì)被定義為只讀常量,不可修改。

    取消三個(gè)注釋后,程序運(yùn)行顯示:

    不能對(duì)只讀常量賦值

    那我們現(xiàn)在把第五行代碼注釋掉再執(zhí)行,程序顯示:

    出現(xiàn)段錯(cuò)誤,顯然第六行的存在也不合理。 段錯(cuò)誤究其根本就是訪問(wèn)了非法內(nèi)存如果你想要將字符串b中的第6個(gè)字符替換為\0的話,可以這樣修改:

    #include<stdio.h> int main(int argc,char*argv[]) {const char a[]="xiyoulinux\0";char b[20]="xiyoulinux\0"; //修改指針為數(shù)組b[5]='\0'; //替換printf("%s\n",a);printf("%s\n",b);return 0; }

    輸出結(jié)果:

    原代碼最終注釋掉兩行結(jié)果正常輸出兩遍Xiyou Linux Group。


    12、

    C源文件到可執(zhí)行文件共經(jīng)歷了4個(gè)過(guò)程:預(yù)處理、編譯、匯編、鏈接

    • 預(yù)處理

    預(yù)處理階段編譯器主要作加載頭文件、宏替換、條件編譯的作用。一般處理帶“#”的語(yǔ)句。

    我們可以通過(guò)gcc的-E進(jìn)行查看:

    root@gougou:/home/yanran# gcc -E main.c -o main.i

    編譯器將main.c預(yù)處理結(jié)果輸出 main.i 文件。

    • 編譯

    在編譯過(guò)程中,編譯器主要作語(yǔ)法檢查和詞法分析。我們可以通過(guò)-S來(lái)進(jìn)行查看,該選項(xiàng)預(yù)處理之后的結(jié)果翻譯成匯編代碼:

    root@gougou:/home/yanran# gcc -S main.i

    編譯器將預(yù)處理輸出文件main.i 文件編譯成main.s 文件。

    • 匯編

    在匯編過(guò)程中,編譯器把匯編代碼轉(zhuǎn)化為機(jī)器代碼。我們可以通過(guò)-c來(lái)進(jìn)行查看:

    root@gougou:/home/yanran# gcc –c main.s

    編譯器將main.s 文件轉(zhuǎn)化為main.o 文件。

    • 鏈接

    鏈接就是將目標(biāo)文件、啟動(dòng)代碼、庫(kù)文件鏈接成可執(zhí)行文件的過(guò)程,這個(gè)文件可被加載或拷貝到存儲(chǔ)器執(zhí)行。我們可以通過(guò)gcc main.o查看文件,如下所示:

    root@gougou:/home/yanran# gcc –o main.o

    編譯器將輸出文件main.o 鏈接成最終可執(zhí)行文件a.out。


    13、

    冒泡排序的三種優(yōu)化方法,參考2019年面試題的第12題。


    14、

    (1) 查看工作路徑:pwd+文件名

    (2) 查看文件:ls

    (3)創(chuàng)建目錄

    • 在當(dāng)前目錄下創(chuàng)建一個(gè)新目錄:mkdir+新目錄名? ??? ????
    • 在指定目錄下創(chuàng)建一個(gè)名為aaa的目錄:mkdir+/目錄名/新目錄名
    • 創(chuàng)建文件:touch+新文件名

    ?(4) 拷貝目錄:cp -r +目錄名稱+拷貝的目標(biāo)位置



    2021年


    結(jié)果:16 12

    sizeof和strlen的異同

    sizeof關(guān)鍵字計(jì)算所占內(nèi)存字節(jié)長(zhǎng)度

    strlen函數(shù)計(jì)算字符串字符長(zhǎng)度

    strlen函數(shù)遇\0停止,而sizeof計(jì)算\0。

    都可以計(jì)算字符串?dāng)?shù)組中元素的個(gè)數(shù)

    值得注意的是代碼中字符串的結(jié)尾還有一個(gè)隱藏的\0,所以sizeof值為16而非15。


    結(jié)果:相等,都為16

    主要考察結(jié)構(gòu)體內(nèi)存對(duì)齊的知識(shí)點(diǎn)。

    • 第一個(gè)成員在結(jié)構(gòu)體變量偏移量為0的地址處。
    • 其他成員變量要對(duì)齊到對(duì)齊數(shù)整數(shù)倍的地址處。

    對(duì)齊數(shù) = 編譯器默認(rèn)的一個(gè)對(duì)齊數(shù)與該成員大小中的較小值。(vs中默認(rèn)值是8 Linux默認(rèn)值為4。)

    • 結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)的整數(shù)倍

    由此可見(jiàn),test1三個(gè)元素內(nèi)存分別為4、2、8,其中2補(bǔ)齊2變?yōu)?,所以結(jié)果為4+4+8=16。test2三個(gè)元素內(nèi)存分別為2、4、8,同樣2補(bǔ)齊變?yōu)?,結(jié)果同樣為16。


    ?

    二維數(shù)組作為函數(shù)參數(shù)來(lái)傳遞的三種方法:

    1、行參給出第二維的長(zhǎng)度

    void func(int n,char arr[][13]) {int i;for(i=0;i<n;i++){printf("%s\n",i,arr[i]);} }//下面兩個(gè)的函數(shù)體內(nèi)部結(jié)構(gòu)和這個(gè)一樣,后面的不展開(kāi)寫(xiě)了

    ?2、形參聲明為指向數(shù)組的指針

    void func(int n,char(*arr)[13])

    ?3、形參聲明為指針的指針

    void func(int n,char **arr)

    ?

    • 傳值和傳址的區(qū)別

    傳值:傳值相當(dāng)于單純把值傳給別人,相當(dāng)于復(fù)制粘貼的操作,傳值不會(huì)改變傳值方的值

    傳址:傳地址相當(dāng)于將地址賦給了對(duì)方,也就是說(shuō)兩個(gè)參數(shù)此時(shí)就是綁在一起的螞蚱,它們指向同一片地址。傳址后兩個(gè)參數(shù)只要有一個(gè)發(fā)生改變,另一個(gè)參數(shù)就會(huì)隨之改變

    • 變量的生命周期

    變量類型主要有全局變量、局部變量和靜態(tài)變量。

    局部變量:定義在函數(shù)體內(nèi)部的變量,作用域僅限于函數(shù)體內(nèi)部。離開(kāi)函數(shù)體就會(huì)無(wú)效,再調(diào)用就是出錯(cuò)。

    全局變量:所有的函數(shù)外部定義的變量,它的作用域是整個(gè)程序,也就是所有的源文件,包括.c和.h文件。

    靜態(tài)變量(static):變量在運(yùn)行區(qū)間結(jié)束后內(nèi)存不釋放地址不變

    對(duì)上面代碼中的各個(gè)函數(shù)區(qū)域變量的解析

    #include <stdio.h> int ver=123; //全局變量 void func1(int ver) //只在func1函數(shù)中有效 {ver++;printf("ver=%d\n",ver);//1026 出了這個(gè)函數(shù)數(shù)據(jù)就被滅掉 }void func2(int*pr)//傳局部變量ver的地址給指針pr {*pr=1234;//在此函數(shù)內(nèi)修改pr指向地址中的值(ver)printf("*pr=%d\n",*pr);//1234 修改成功 并且會(huì)影響主函數(shù)中ver的值(因?yàn)槭莻鞯牡刂?#xff09;pr=5678;//直接把int型常量賦給地址會(huì)出現(xiàn)警告printf("ver=%d\n",ver);//123 此時(shí)ver的值為全局變量ver的值。因?yàn)樵趂unc2函數(shù)中的參數(shù)是指針,沒(méi)有int類型常量的定義所以從全局中調(diào)變量值。 }int main() {int a=0;//局部變量,只在主函數(shù)體內(nèi)有效int ver=1025;//同上for(int a=3;a<4;a++) //for循環(huán)內(nèi)a的初定義{static int a=5;//靜態(tài)局部變量a,一次循環(huán)結(jié)束后的值會(huì)保留至下一循環(huán)迭代使用。僅在此循環(huán)中有效printf("a=%d\n",a);//5a=ver;printf("a=%d\n",a);//1025func1(ver); //傳入的是主函數(shù)內(nèi)定義的局部變量verint ver=7;//僅在此循環(huán)中有效的局部變量verprintf("ver=%d\n",ver);//7func2(&ver);//傳局部變量ver的地址給指針pr//如果在這里插入printf("%d",ver);輸出為:ver = 1234,因?yàn)樵趂unc2函數(shù)中通過(guò)修改*pr指針修改了ver的值}//出了這個(gè)for循環(huán),循環(huán)內(nèi)定義的a和ver空間被釋放,其值1025和1234被清空。printf("a=%d\tver=%d\n",a,ver);//0 1025 就是在主函數(shù)內(nèi)剛開(kāi)始定義和初始化的值return 0; }

    ?輸出如下:

    結(jié)果:5050

    表達(dá)式?結(jié)果一:結(jié)果二

    表達(dá)式若為真(1),調(diào)用結(jié)果一;若為假(0),則調(diào)用結(jié)果二。

    遞歸中的“歸”是如何實(shí)現(xiàn)的

    遞歸中必須存在一個(gè)臨界點(diǎn)。比如最內(nèi)層被調(diào)用的函數(shù)被賦給了一個(gè)確切的返回值,這個(gè)確切的返回值就是遞歸的臨界點(diǎn)。接受到確切的值后最內(nèi)層函數(shù)的值確定,然后將結(jié)果返回給上一層函數(shù),然后一層層結(jié)束,一層層返回,這就是“歸”。

    引用一篇博客中的例子:

    我們?cè)倩貧w到這道題中,來(lái)看看函數(shù)體:

    unsigned sum(unsigned n) {return n ? sum(n-1) + n : 0; }

    函數(shù)自己調(diào)用自己,一看就是老遞歸了。?

    從n = 100開(kāi)始遞減,當(dāng)n不為0時(shí),函數(shù)不停返回sum(n-1)調(diào)用自己;

    當(dāng)n減到1時(shí),返回sum(0)+1,而sum(0)通過(guò)運(yùn)算符判斷返回0(臨界點(diǎn)),sum(0)=0再返回給sum(1)...就這樣逐層返回最后0+1+2+...+100=5050。


    代碼分析:

    #include<stdio.h> void func(void); int main() {func();return 0; }void func(void) {short a=-2; //a十進(jìn)制-2,二進(jìn)制1111 1111 1111 1110,十六進(jìn)制fffeunsigned int b=1;b+=a; //b十進(jìn)制-1,二進(jìn)制1111 1111 1111 1111 1111 1111 1111 1111,十六進(jìn)制ffffffffint c=-1;unsigned short d=c*256;c<<=4; //c的補(bǔ)碼右移4位變?yōu)?111 1111 1111 1111 1111 1111 1111 0000,十進(jìn)制-16,十六進(jìn)制fffffff0int e=2;e=~e|6; //~取反后進(jìn)行或運(yùn)算(兩個(gè)只要有一個(gè)為1就為1)得e二進(jìn)制為1111 1111 1111 1111 1111 1111 1111 1111,十六進(jìn)制ffffffffd=(d&0xff)+0x2022; //與運(yùn)算(都為1才是1)結(jié)果為0+0x2022 =0x2022printf("a=0x%hx\t b=0x%x\t d=0x%hx\t e=0x%x\n",a,b,d,e);//十六進(jìn)制輸出(%hx表示輸出short類型值)printf("c=0x%hhx\t\n",(signed char) c);//(%hhx表示輸出short short類型值) }

    程序輸出:


    ?結(jié)果:10 4 9

    首先a是一個(gè)三維數(shù)組,b是一個(gè)指向一維數(shù)組的數(shù)組指針。

    ++b操作如下(框內(nèi)為數(shù)組a[3][3]):

    此時(shí)對(duì)b[1][1]賦值10實(shí)際上是將a[2][1]賦為了10,即7替換成了10

    int*ptr=(int*)(&a+1):指向整個(gè)a數(shù)組的下一個(gè)數(shù)組

    這里插播一個(gè):

    對(duì)于數(shù)組:a和&a的區(qū)別

    • a代表數(shù)組首元素的地址
    • &a代表整個(gè)數(shù)組的地址

    這倆的數(shù)據(jù)類型也不同:a是首元素的類型,&a是數(shù)組的類型

    在最后打印中:

    **(a+1):對(duì)a數(shù)組一維+1得到第二維,然后再對(duì)第二維處首元素取值得到4

    *(ptr-1)整個(gè)數(shù)組的下一個(gè)數(shù)組-1,得到上一個(gè)數(shù)組a中的最后一個(gè)元素9


    先回答后兩個(gè)問(wèn)題:

  • const int 和 int const 沒(méi)有區(qū)別,它們都表示int類型的變量不能修改;
  • const int* 和int const* 也沒(méi)有區(qū)別,它們都表示指針指向的值不能修改。
  • 下面是錯(cuò)誤代碼分析(func2、func3、func4有錯(cuò)):

    主要涉及‘對(duì)于指針來(lái)說(shuō),const作用位置所對(duì)應(yīng)的不可改變位置’

    void func2(const int *n)//const作用于*n:不可修改指針n指向的值 {*n+=1;//向只讀形參賦值,錯(cuò)誤n=&a; } void func3(int*const n)//const作用于n:不可修改指針n指向的地址 {*n+=1;n=&a;//向只讀位置賦值,錯(cuò)誤 } void func4(const int *const n)//const作用于*n和n:地址和值都不能修改 {*n+=1;//向只讀形參賦值,錯(cuò)誤n=&a;//向只讀位置賦值,錯(cuò)誤 }

    如果沒(méi)有限制,則可以使用大小寫(xiě)字母的ASCII相差32的性質(zhì)來(lái)解決大小寫(xiě)反轉(zhuǎn)問(wèn)題:

    #include<stdio.h> int main() {int I=2; //隨便賦個(gè)值使I不為零while (I) //當(dāng)括號(hào)內(nèi)表達(dá)式不為零時(shí)實(shí)現(xiàn)后面的循環(huán){char b;scanf("%c", &b);if (b >= 'A' && b <= 'Z'){b = b + 32;printf("%c", b);}else if (b >= 'a' && b <= 'z'){b = b - 32;printf("%c", b);}}return 0; }

    不過(guò)該題目要求使用以指針為形參的函數(shù)來(lái)解決,并且是特定字符串,最后還要返回反轉(zhuǎn)后的字符串

    代碼實(shí)現(xiàn)如下:

    char *convert(const char *s) {char *k=(char*)malloc(N);//在函數(shù)中分配一段空間并且讓指針k指向這段字符串的位置char *t=s;//指針t用來(lái)保存原字符串s的位置char *q=k;//再用一個(gè)指針保留新分配的內(nèi)存地址memset(k,0,N);//將新分配的內(nèi)存空間中的數(shù)全部置為0char str[N];//用一個(gè)數(shù)組來(lái)存放轉(zhuǎn)換后的字符串for(;*s!='\0';*s++,k++)//通過(guò)指針的移動(dòng)將數(shù)據(jù)儲(chǔ)存在新地址上{if(*s>='a'&&*s<='z')*k=toupper(*s); //小寫(xiě)轉(zhuǎn)大寫(xiě)else if(*s>='A'&&*s<='Z')*k=tolower(*s); //大寫(xiě)轉(zhuǎn)小寫(xiě)else*k=*s;}t=q;//讓原指向s的指針指向新地址return t; }

    • 前兩種方法正確,最后一種錯(cuò)誤。

    Swap1:直接從原函數(shù)中傳遞參數(shù),比較方便

    Swap2:在參數(shù)中創(chuàng)建交換變量t,作用時(shí)間短且占用內(nèi)存空間小

    Swap3:錯(cuò)誤!只傳遞了a,b變量的值,并沒(méi)有傳地址,所以僅在函數(shù)內(nèi)部改變了a,b的值。而函數(shù)作用完后就釋放掉了函數(shù)中變量的內(nèi)存,a,b的值依舊沒(méi)有改變

    • do{...}while(0)的作用:宏定義中實(shí)現(xiàn)局部作用域

    我們知道宏定義只是做一個(gè)標(biāo)識(shí)符和字符串的替換,盡管宏定義的形式可以類似于函數(shù),但它實(shí)際并不具備與函數(shù)類似的局部作用域。當(dāng)然,我們可以通過(guò)在宏定義中使用大括號(hào)的形式,如:#define swap(x){...} 來(lái)實(shí)現(xiàn)局部作用域,但也會(huì)帶來(lái)新的麻煩:

    #define swap(a, b){a = a+b; b = a-b; a = a-b;}int main() {int a = 1, b = 2;if(1)swap(a,b);elsea = b = 0;return 0; }

    上面的代碼乍看并沒(méi)有問(wèn)題,但要想到一點(diǎn):宏定義會(huì)在預(yù)編譯后被替換

    下面是替換后的代碼:

    int main() {int a = 1, b = 2;if(1){a = a+b; b = a-b; a = a-b;};elsea = b = 0;return 0; }

    這下問(wèn)題就明顯了:在if后的代碼塊后面多出了一個(gè) ;,這會(huì)引發(fā)編譯錯(cuò)誤。

    使用該宏定義時(shí)不在后面加;可以解決這個(gè)問(wèn)題,但這顯然不符合我們的編碼習(xí)慣。

    在宏定義中使用do…while(0)可以解決這個(gè)問(wèn)題:

    #define swap(a, b) do{\a = a+b;\ b = a-b;\a = a-b;\}while(0)int main() {int a = 1, b = 2;if(1)swap(a,b);elsea = b = 0;return 0; }

    像上面的代碼那樣,我們可以放心地在宏定義后面使用分號(hào),就不會(huì)造成問(wèn)題啦。

    • 實(shí)現(xiàn)swap功能的其他方法,可參考2019年面試題的第2題。

    ?

    argc為參數(shù)個(gè)數(shù),argv為指針數(shù)組的首元素(指針)。

    要想在不使用argc的前提下完成對(duì)argv的遍歷,只能從argv自身出發(fā)。當(dāng)argv指針不為空時(shí)即可完成遍歷:

    #include<stdio.h> int main(int argc, char*argv[]) {int i=0;while(argv[i]!=NULL)printf("%s\n",argv[i++]);return 0; }

    int *func1(void) {static int n=0;//靜態(tài)變量的生命周期是從調(diào)用它開(kāi)始到本函數(shù)結(jié)束n=1;return &n;//只能返回指針值也就是n的地址(此處可返回地址是因?yàn)殪o態(tài)變量的內(nèi)存不會(huì)被釋放,值會(huì)一直保存且地址一直存在) }int *func2(void) {int *p=(int*)malloc(sizeof(int)); //手動(dòng)給指針p分配內(nèi)存*p=3;return p;//返回首地址 }/*func3錯(cuò)誤*/ int *func3(void) {int n;//沒(méi)初始化return &n;//不可返回局部變量的地址(&n),因?yàn)樗饔猛昃捅会尫帕?#xff0c;相當(dāng)于返回一個(gè)野指針 }

    結(jié)果:Welcome to xiyou linux group 2021

    涉及大小端問(wèn)題,詳情可參考2019年第13題和2020年第8題。


    詳情參考2020年面試題第12題 。


    后面會(huì)單開(kāi)一篇博客討論這個(gè)。


    建立頭文件(.h)是為了聲明c文件(.c)中的函數(shù),以及包括宏定義。

    首先要建一個(gè).h文件:

    打開(kāi).h文件進(jìn)行建立編輯。建立頭文件是有一定步驟的,要用到#ifndef ...、#define ...、#endif,這是為了避免重復(fù)定義。

    #ifndef后面要寫(xiě)的是頭文件名稱的大寫(xiě)。例如:ceshi.h要寫(xiě)成__CESHI_H__。前面與后面是兩個(gè)下劃線。然后在define與endif中間聲明函數(shù)名,記得寫(xiě)冒號(hào)。

    然后在.c文件中寫(xiě)上include這個(gè)頭文件,就可以調(diào)用了。

    ?最后編譯運(yùn)行就完成了。


    詳情參考2020年面試題第14題,以及我的另一篇博客:初探Linux操作系統(tǒng)與文件_Yan__Ran的博客-CSDN博客


    此處應(yīng)有表情包(自行腦補(bǔ)

    總結(jié)

    以上是生活随笔為你收集整理的西邮Linux兴趣小组2019-2021年纳新面试题解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。