北京聯發科嵌入式軟件工程師筆試題目解析


最近在寫大論文,頭都要禿了,技術文章也更不動了。去年參加秋招的時候,記錄了一些筆試題目,今天整理了下答案,供大家參考。最近幾周,我都會發一些大廠筆試的題目和答案。今天分享的是聯發科(北京)提前批嵌入式軟件工程師的筆試題目。聯發科(北京)是我面試的第一家公司,面試過程體驗不是很好,所以,一面完了就沒消息了。面經可以參考下這篇文章[2020秋招聯發科小米等面經分享]

目錄

邏輯題

1. 參加斷型冠狀病毒疫苗開發研討會的70名學者中,亞商學者39人,博士33人,非亞裔學者中無博士學位的4人,根據以上陳述,參加此次研討會的亞裔博士有幾人?

A 1

B 2

C 6

D 7

E 8

C

亞裔學者39人,博士33人,非亞裔學者中無博士學位4人,這三者加起來是76人,但實際總人數只有70人。亞裔學者和博士兩個概念之間為交叉關系,這兩個概念和非亞裔學者中無博士學位者之間都是全異關系。這說明,既是亞裔學者又是博士即亞裔博士有6人。

也可以通過運用計算法來求解。設亞裔博士有x人,則可列方程:31+33-x+4=70,解這個方程,可得:x=6。

2. 某省婦女兒童占全省總人口的2/3。如果婦女是指所有女性人口,兒童是指所有非成年人口,並且對任一年齡段,該省男女人口的數量持平,則上述斷定能推出以下哪項結論?

A 該省男性成年人口和兒童人口持平。

B 該省男性成年人口大於兒童人口。

C 該省男性成年人口小於兒童人口。

D 該省女性成年人口和男性兒童人口持平

E 該省男性成年人口和女性兒童人口持平。

A

由題干,可以給出以下表

女性 男性
成年 1/3 1/3 2/3
兒童 1/6 1/6 1/3
1/2 1/2

由任一年齡段,該省男女人口的數量持平,可得總人口男女持平。

由婦女兒童占全省總人口的三分之二,可得成年男性占三分之一。

由成年男性占三分之一,得男童占六分之一(因為男性占二分之一)。

由男童占六分之一,得女童占六分之一。因此,該省男性成年人口和兒童人口持平。

3. 某次討論會共有25名與會者,已知(1)至少有7名青年教師是男性,(2)至少有8名男教師已過中年(3)至少有10名男青年是教師;如上述3句話兩真一假,則關於與會人員可以得出以下哪項

A 青年教師至少有10名

B 男教師至多有15名

C 男青年都是教師

D 男青年至少有7名

D

[1]:大於等於7名青男 [2]:大於等於8名中男[3]:大於等於10名青男。

[1]與[3]數量上有重疊部分,如果[3]為真,則[1]一定為真;

如果[1]為假,則[3]一定為假。此時就會兩個為假,與題干條件的兩真一假相矛盾,故[1]必真。而如果[1]為真,既青年男教師大於等於7人,那么男青年至少有7名。

4. 某中葯配方有如下要求(1)如果有甲葯材,那么沒有乙葯材(2)如果沒有丙葯材,那么必須有丁葯材(3)人參和天麻不能都有(4)如果沒有甲葯材而有丙葯材,則需要有人參。如果還有天麻,則關於該配方的斷定哪項為真?D

A 含有甲葯材

B 含有丙葯材

C 沒有丙葯材

D 含有乙葯材或不含丁葯材

無正確答案

由“含有天麻”和(3)可以推出,不含有人參;進而由(4),否定后件就能否定前件,可以推出,有甲葯材或者沒有丙葯材。

如果有甲葯材,由(1)可以推出,無乙葯材;如果沒有丙葯材,由(2)可以推出,有丁葯材;故無乙葯材或有丁葯材

因此,選項中沒有正確答案。

5. 某國擬在甲、乙、丙、丁、戊己6種農作物中進口幾種,用於該國龐大的動物飼料產業,考慮到些農作物可能有違禁成分,以及它們之間存在的互補或可替代因素,該國對進口這些農作物有如下要求(1)它們當中不含違禁成分的都進口。2)如果甲或乙含有違禁成分,就進口丙和丁。3)如果戊含有違禁成分,那么己就不進口了;如果進口丙,就進口乙和己。(4)如果不進口己,就進口戊;如果進口戊,就不進口己。根據上述要求,以下哪項所列的農作物是該國可以進囗的;

A 甲、乙、丙

B 乙、丙、丁

C 甲、乙、戊

D 甲、乙、己

E 丙、戊、己

C

A選項與(2)矛盾

B選項與(2)矛盾

D選項與(3)矛盾

E選項與(4)矛盾

不定向選擇

1. int i =1;const int j =2;以下說法不正確的是

A const int *p1 = &i;

B const int *p2 = &j;

C int *const p3 = &i;

D int *const p4 = &j;

D

int *const p4 ,p4為指針常量,p4指向的內存位置不能改變,但是,p4所指內存存放的值是可以改變的。j表示常量,其數值不能被改變。

將j的地址賦給p4后,p4可以執行其他操作( 如*p4=4;),將j的值改變,因此,int *const p4 = &j;是錯誤的。

2. 以下關於內存的說法正確的是

A RAM是隨機存儲器,在斷電時將丟失其存儲內容,ROM是只讀存儲器,斷電時不會丟失存儲內容

B 內存的數據帶寬與內存的數據傳輸頻率、內存數據總線位數以及內存大小有關

C 用戶進程通常情況只能訪問用戶空間的虛擬地址,不能訪問內核空間虛擬地址

D Linux中使用 buddy system算法可以管理頁外內存碎片,使用slub算法可以管理頁內內存碎片

ACD

B:內存的數據帶寬的計算公式是:數據帶寬=內存的數據傳輸頻率×內存數據總線位數/8

3. 以下哪些事件會導致進程的創建

A 系統初始化

B fork系統調用

C pthread_ create函數調用

D 一個批處理作業的初始化

ABD

創建進程的多種方式但凡是硬件,都需要有操作系統去管理,只要有操作系統,就有進程的概念,就需要有創建進程的方式,一些操作系統只為一個應用程序設計,比如掃地機器人,一旦啟動,所有的進程都已經存在。

而對於通用系統(跑很多應用程序),需要有系統運行過程中創建或撤銷進程的能力,主要分為4中形式創建新的進程

1.系統初始化(查看進程 linux中用ps命令, windows中用任務管理器,前台進程負責與用戶交互,后台運行的進程與用戶無關,運行在后台並且只在需要時才喚醒的進程,稱為守護進程,如電子郵件、web頁面、新聞、打印)

2.一個進程在運行過程中開啟了子進程(如 nginx開啟多進程,os.fork等)

3.用戶的交互式請求,而創建一個新進程(如用戶用鼠標雙擊任意一款軟件,qq,微信等)

4.—個批處理作業的初始化(只在大型機的批處理系統中應用)

無論哪一種,新進程的創建都是由—個已經存在的進程執行了—個用於創建進程的系統調用而創建的。

4. 下列說法正確的有

A 計算機體系結構是一門研究計算機系統軟件結構的學科。

B 現代計算機處理器結構按照存儲方式划分,可分為復雜指令集計算機和精簡指令集計算機

C RISC技術對比CISC最大的區別就是對CPI的精簡

D 單指令流單數據流計算機的每個機器周期最多執行一條指令

CD

A.計算機體系結構主要研究軟件、硬件功能分配和對軟件、硬件界面的確定

B.現代計算機處理器結構按照指令系統方式划分,可分為復雜指令集計算機和精簡指令集計算機

5. 32位系統中,該程序的輸出為

//參數傳遞 退化為指針
void Func(char str_arg[100])
{
	printf("%d\n",sizeof(str_arg));
}
int main()
{
	char str[] = "Hello";
	printf("%d\n",sizeof(str));
	printf("%d\n",strlen(str));
	char *p = str;
	printf("%d\n",sizeof(p));
	Func(str);
	return 0;
}

A 5 5 4 4

B 6 5 4 4

C 6 5 6 4

D 5 5 5 100

B 6 5 4 4

使用函數strlen()求某個字符串的長度時是不包括結尾標志符'\0'的,但當你用sizeof()求某個字符串占用的內存空間時,結尾字符'\0'是被包括在里面的

strlen用來計算字符串的長度(在C/C++中,字符串是以"\0"作為結束符的),它從內存的某個位置(可以是字符串開頭,中間某個位置,甚至是某個不確定的內存區域)開始掃描直到碰到第一個字符串結束符\0為止,然后返回計數器值。

sizeof是C語言的關鍵字,它以字節的形式給出了其操作數的存儲大小,操作數可以是一個表達式或括在括號內的類型名,操作數的存儲大小由操作數的類型決定。

6. 有以下程序,求輸出結果

#include<stdio.h>
int fun(int i)
{
	int cnt = 0;
	while(i)
	{
		cnt++;
		i=i&(i-1);
	}
	return cnt;
}
int main()
{
	printf("%d\n\r",fun(2021));
	return 0;
}

8

&是按位與,對應位都為1時該位得1,否則得0。所以 i&(i-1) 的作用:將i的二進制表示中的最右邊的1置為0。

在本題中即數出2021轉換成二進制有幾個1就會走幾次循環(不斷除2)。2021對應的二進制是:10100111111,一共8個1,故走8次。

擴展:(n > 0 && ((n & (n - 1)) == 0)是判斷n是不是2的次冪

7. 若 int x = 5&6,那么x的值為()

A 3

B 4

C 5

D 6

B
5: 0101
6: 0110
x: 0100

8. 以下錯誤的表達式為

struct {
	inr a;
	char b;
}Q,*p=&Q;

A Q.a

B (*p).b

C p->a

D *p.b

D

*p=&Q ,把Q的地址賦值給了指針p,對p解引用其實就是Q。

A 選項肯定是對的,結構體的正常訪問方法。

B 選項 (*p).b 等價於 Q.b

C p->a p為指針訪問結構體用->沒問題。

D *p.b 優先級問題,.的優先級高於 *,所以 *p.b == *(p.b),p為指針,訪問結構體成員要用->。

擴展:結構體中.和->兩種訪問區別

定義結構體指針,訪問成員時就用->

定義結構體變量,訪問成員時就用.

struct A {
int a;
char b;
};
struct A q; //訪問成員就用:q.a;
struct A *p; //訪問成員就用:p->a;

9. 關於對象的this指針,以下敘述不正確的有

A 必須顯示地在類中定義聲明this數據成員才能使用this指針

B 一且生成一個對象,該對象的this指針就指向該對象本身

C 一個類的所有對象的this指針的值都是相同的

D 不能通過對象的this指針訪問對象的數據成員和成員函數

A

this指針的特點

( 1 )每個當前對象都含有一個指向該對象的this指針。this指針只能在類的成員函數中使用,在全局函數、靜態成員函數中都不能使用 this 。

( 2 ) this 指針是在成員函數的開始前構造,並在成員函數的結束后清除 。

( 3 ) this 指針會因編譯器不同而有不同的存儲位置,可能是寄存器或全局變量 。

( 4 ) this 是類的指針 。

( 5 ) 因為 this 指針只有在成員函數中才有定義,所以獲得一個對象后,不能通過對象使 用 this 指針,所以也就無法知道一個對象的 this 指針的位置。 不過,可以在成員函數中指定this 指針的位置 。

( 6 )普通的類函數(不論是非靜態成員函數,還是靜態成員函數)都不會創建一個函數表來保存函數指針,只有虛函數才會被放到函數表中。

10. 若某線性表中最常用的操作是在最后一個元素之后插入一個元素和刪除第一個元素,則最節省運算時間的存儲方式是

A 單鏈表

B 僅有頭指針的單循環鏈表

C 雙鏈表

D 僅有尾指針的單循環鏈表

D

單鏈表只能單向遍歷,即只能由鏈表頭向鏈表尾遍歷。

單循環鏈表也只能單向遍歷:鏈表頭->鏈表尾->鏈表頭;

對於A,B,C要想在尾端插入結點,需要遍歷整個鏈表。

對於D,要插入結點,只要改變一下指針即可,要刪除頭結點,只要刪除指針.next的元素即可。

如果只要知道尾指針p,則通過計算一次p->next就能獲得頭指針;插入和刪除算法復雜度O(1)+O(1)

而如果只知道頭指針,則需要遍歷整個鏈表來獲得尾指針的位置;插入和刪除算法復雜度O(1)+O(N)

所以D僅有尾指針的單循環鏈表存儲方式最節省運算時間

填空題

1. F和Q分別是指向單鏈表兩個元素的指針,那么,F所指元素是Q所指元素后繼的條件是(Q->next == F)

2.設有一個空棧,現有輸入序列為1,2,3,4,5,經過 push, push, pop, push pop, push, pop, push后,輸出序列是(2,3,4)

2,3,4

push進棧,棧中是1

push進棧,棧中是1,2

pop出棧,棧中是1,輸出2

push進棧,棧中是1,3

pop出棧,棧中是1,輸出3

push進棧,棧中是1,4

pop出棧,棧中是1,輸出4

push進棧,棧中是1,5

3.H是一個帶有頭結點的單鏈表,那么在第一個元素之后插入一個結點(設P為要插入節點的指針),其操作是( p->next=H->next; )(H->next = p;)

4.已知二叉樹后序遍歷序列為dabec,中序遍歷序列為debac,它的前序遍歷序列為

cedba

1、由后續遍歷可知c是根結點。

2、由中序遍歷可知deba在c的左孩子樹上。

3、由后序遍歷知e是c的左孩子樹的根結點。

4、由中序遍歷可知d是e的左孩子,ba在e的右孩子樹上。

5、由后序遍歷,可以得出b是e的右孩子,a是e的左孩子,而第4步中確定了a不在e的左孩子數上,因此,a只能在b上。

6、由中序遍歷,可知,a是b的右孩子。

所以前序序列為cedba,具體如下圖所示。

5.以下程序的輸出結果為

#include <stdio.h>
int main()
{
	int a;
	a=(int)((double)(3/2)+0.5);
	printf("a = %d",a);
    return  0;
}

1

3和2是整形常量,所以3/2=1;前面(double) 1 = 1.000000;1.000000+0.5 = 1.500000;double轉int會直接去掉小數部分。所以答案為1。

下面簡單分析下double轉int為什么會舍去小數部分

根據國際標准 IEEE 754,任意一個二進制浮點數 V 可以表示成下面的形式:

V = (-1) ^ s × M × 2 ^ E

(1)(-1)^s 表示符號位,當 s=0,V 為正數;當 s=1,V 為負數。

(2)M 表示有效數字,大於等於 1,小於 2,但整數部分的 1 不變,因此可以省略。M由frac編碼。

(3)2^E 表示指數位。E由exp編碼。

雙精度浮點數在內存中的存放方式

對於 64 位的雙精度數來說,從低位到高位,尾數 M 用 52 位來表示,階碼用 11 位來表示,而符號位用最高位 1 位來表示,0 表示正,1 表示負。

將1.5轉換為雙精度浮點數的過程如下:

  1. 將十進制數1.5轉換成二進制為1.1。
  2. 1.1用二進制的科學計數法表示為1.1 * 2^0
  3. 按照上面浮點數的存儲結構, 得出符號位為: 0,表示正數;階碼(指數) E 為1023; 小數部分 M 為1。
  4. 雙 精 度 的 二 進 制 位 : 0_01111111111_0000000000000000000000000000000000000000000000000001

int類型為32位,double轉換為int只能截取低32位為00000000000000000000000000000001。

所以最終的輸出結果為1。

6.這段程序會存在什么問題

#include <stdio.h>
void my_alloc(char **p)
{
    *p = (char *)malloc(100);
}
intm main()
{
    char *otr = NULL;
    char *ptr = NULL;
    my_alloc(&ptr);
    strcpy(ptr,"hello world");
    printf("%s\n",ptr);
    return 0;
}

內存泄漏,分配給其他變量的內存就會減小。我們在刪除一個指針之后,編譯器只會釋放該指針所指向的內存空間,而不會刪除這個指針本身。此時p也就成為一個野指針

擴展:

free()到底釋放了什么?

free()釋放的是指針指向的內存!注意,釋放的是內存,不是指針

指針是一個變量,只有程序結束時才被銷毀。釋放了內存空間后,原來指向這塊空間的指針還是存在,只不過現在指針指向的內容的垃圾,是未定義的,所以說是垃圾。

因此,釋放內存后把指針指向NULL,防止指針在后面不小心又被解引用了。當然,具體情況要具體分析以及具體解決。比如說,你定義了一個指針,在一個函數里申請了一塊內存,然后通過函數返回傳遞給這個指針,那么也許釋放這塊內存這項工作就應該留給其他函數了。

malloc()到底從哪里得到了內存空間?

從堆里面獲得空間。也就是說函數返回的指針是指向堆里面的一塊內存。操作系統中有一個記錄空閑內存地址的鏈表。當操作系統收到程序的申請時,就會遍歷該鏈表,然后就尋找第一個空間大於所申請空間的堆結點,然后就將該結點從空閑結點鏈表中刪除,並將該結點的空間分配給程序。

7.改正程序中的錯誤

下列給定程序將數組元素循環右移,數組大小和元素以及移動位數由鍵盤輸入指定,例如數組{1,2,3,4,5,6},循環右移三位,得到結果為{4,5,6,1,2,3}

請改正程序中的錯誤,使它能得出正確的結果。注意:不能更改程序的結構(共有四處錯誤)

#include<stdio.h>
#include<stdlib.h>
void shift_func(int *array, int len, int k)
{
	int i = 0, j = 0;
	int temp = 0;
	if (array == NULL)
		return;
	k %= len;
	for (i = 0; i < k; i++)
	{
		temp = array[len - 1];
		for (j = len; j > 0; j--)
		{
			array[j] = array[j - 1];
		}
		array[0] = temp;
	}
}
int main()
{
	int len = 0, k = 0, i = 0;
	int *array = NULL;

	scanf("%d%d", &len, &k);
	array = (int*)malloc(len * sizeof(int*));
	if (array == NULL)
		return -1;
	printf("input the array\n");
	for (i = 0; i < len; i++)
		scanf("%d", array[i]);

	shift_func(array, len, k);

	printf("after shift the array is:\n");
	for (i = 0; i < len; i++)
		printf("%d", array[i]);
	printf("\n");

	return 0;
}

修改后的程序如下所示

void shift_func(int *array, int len, int k)
{
	int i = 0, j = 0;
	int temp = 0;
	if (array == NULL)
		return;
	k %= len;
	for (i = 0; i < k; i++)
	{
		temp = array[len - 1];
		for (j = len; j > 0; j--)
		{
			array[j] = array[j - 1];
		}
		array[0] = temp;
	}
}
int main()
{
	int len = 0, k = 0, i = 0;
	int *array = NULL;

	scanf("%d%d", &len, &k);
	array = (int*)malloc(len * sizeof(int));//array = (int*)malloc(len * sizeof(int));
	if (array == NULL)
		return -1;
	printf("input the array\n");
	for (i = 0; i < len; i++)
		scanf("%d", &array[i]);//scanf("%d", &array[i]);

	shift_func(array, len, k);

	printf("after shift the array is:\n");
	for (i = 0; i < len; i++)
		printf("%d", array[i]);
	printf("\n");
       
    // free(array);
    // array = NULL;
	return 0;
}

編程題

輸入年,月,日,計算這一天是該年的第幾天。年份符合以下兩種條件的任意種即為閏年,閏年里2月會有29天:

1.年份是4的倍數但不是100的倍數。

2.年份是40的倍數

輸入描述:

輸入表示日期的格式: Year, Month, Day包含三個整數:年(1<=Year3000),月(1<= Month<=12),日(1<=Day<=31)

思路:比較容易想到的一種方法是查表。將一年中每個月份的天數放進數組中,數組下標索引即代表月份。

這里要注意閏年的處理。為了方便,我們定義兩個數組,分別對應閏年的天數和非閏年的天數。再定義一個變量flag來判斷是否為閏年即可。具體代碼如下所示。

/*
 * @Description: 北京聯發科嵌入式軟件工程師筆試題目
 * @Version: 
 * @Autor: 嵌入式與Linux那些事
 * @Date: 2021-3-15 22:24:12
 * @LastEditors: 嵌入式與Linux那些事
 * @LastEditTime: 2021-3-15 22:39:41
 */
#include<stdio.h>
//兩個數組,分別存放閏年和非閏年每個月的天數,對應 1~ 12月
int b1[12]= {31,28,31,30,31,30,31,31,30,31,30,31};
int b2[12]= {31,29,31,30,31,30,31,31,30,31,30,31};

/**
 * @Description: 累加天數
 * @Param: months,月份。flag,閏年判斷標志位
 * @Return: 天數
 * @Author: 公眾號【嵌入式與Linux那些事】
 */
int add(int months,int flag)
{
    int i,j = 0;
    if(flag)
    {
        for(i=0; i<months; i++)
        {
            j+=b2[i];
        }
    }
    else
    {
        for(i=0; i<months; i++)
        {
            j+=b2[i];
        } 
    }
    return j;
}

int main()
{
    int years,months,days;
    //設置標志位判斷閏年
    int flag = 0;
    scanf("%d,%d,%d",&years,&months,&days);
    
    if(years>=1 && years<=3000 && months>=1 && months<=12 && days>=1 && days<=31)
    {
        if((years%4==0&&years%100!=0))
        {
            flag = 1;
        }
        printf("result is %d",add(months,flag));
    }
    else
    {
        printf("invalid parameter");
    }
    return 0;
}

今天的題目就分享到這里,下一篇文章,將會分享小米的筆試題目和答案。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM