linux c嵌入式工程师笔试题,嵌入式软件工程师/linux c程序员 笔试题

一、什么叫可重入?

可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是

可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断

它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么

错误;而不可重入的函数由于使用了一些系统资源,比如全局变量区,

中断向量表等,所以它如果被中断的话,可能会出现问题,这类函数是

不能运行在多任务环境下的。

二、tcp/IP三次握手?

1.首先客户端通过向服务器端发送一个SYN来建立一个主动打开,作为三

路握手的一部分。(同步位为1)

2. 然后服务器端应当为一个合法的SYN回送一个SYN/ACK。(同步位和确

认位都为1)

3. 最后,客户端再发送一个ACK。这样就完成了三路握手,并进入了连接

建立状态。(确认位位1)

三、TCP/IP通信阻塞和非阻塞?

阻塞:当socket的接收缓冲区中没有数据时,read调用会一直阻

塞住,直到有数据到来才返回。当socket缓冲区中的数据量小于期望读取

的数据量时,返回实际读取的字节数。当socket的接收缓冲区中的数据大于

期望读取的字节数时,读取期望读取的字节数,返回实际读取的长度。

非阻塞:socket的接收缓冲区中有没有数据,read调用都会立刻返回。接收

缓冲区中有数据时,与阻塞socket有数据的情况是一样的,如果接收缓冲区

中没有数据,则返回错误号为EWOULDBLOCK,表示该操作本来应该阻塞的,但

是由于本socket为非阻塞的socket,因此立刻返回,遇到这样的情况,可

以在下次接着去尝试读取。如果返回值是其它负值,则表明读取错误。

因此,非阻塞的rea调用一般这样写:

if ((nread = read(sock_fd, buffer, len)) < 0)

{

if (errno == EWOULDBLOCK)

{

return 0; //表示没有读到数据

}

else

return -1; //表示读取失败

}else return nread; //读到数据长度

四、TCP/UDP区别?

TCP—传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器

彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP

提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端

传到另一端。

UDP—用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠

性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达

目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有

超时重发等机制,故而传输速度很快

五、林锐内存思考?

(1)

void GetMemory(char *p)

{

p = (char *)malloc(100);

}

void Test(void)

{

char *str = NULL;

GetMemory(str);

strcpy(str, “hello world”);

printf(str);

}

答:程序崩溃因为GetMemory并不能

传递动态内存,Test函数中的 str一

直都是NULL

strcpy(str, “hello world”);

将使程序崩溃

(2)

char *GetMemory(void)

{

char p[] = “hello world”;

return p;

}

void Test(void)

{

char *str = NULL;

str = GetMemory();

printf(str);

}

答:可能是乱码 因为GetMemory返回的

是指向栈内存的指针,该指针的地址

不是 NULL,但其原现的内容已经被清

除,新内容不可知

(3)

void GetMemory2(char **p, int num)

{

p = (char)malloc(num);

}

void Test(void)

{

char *str = NULL;

GetMemory(&str, 100);

strcpy(str, “hello”);

printf(str);

}

答: (1)能够输出hello (2)内存泄漏没有free

(4)

void Test(void)

{

char str = (char) malloc(100);

strcpy(str, hello);

free(str);

if(str != NULL)

{

strcpy(str, world);

printf(str);

}

}

答:篡改动态内存区的内容,后果难以

预料,非常危险 因为free(str);之后

str成为野指针,if(str != NULL)语句

不起作用

六、编写各种str函数?

strcpy函数

char *my_strcpy(char *dest, const char *src)

{

char *temp;

assert(dest!=NULL && str != NULL);

while((*dest++ = *src++) != ‘\0’);

return temp;

}

char *my_strcat(char *dest, const char *stc)

{

char *temp;

assert(dest!=NULL && str != NULL);

while(*dest)

dest++;

while((*dest++ = *stc++) != ‘\0’);

return temp;

}

char my_strstr(const char *str, char c)

{

for(; *str != c; str++)

if(*str == ‘\0’)

return NULL;

else

return str;

}

int my_strlen(const char *str)

{

char *temp = str;

for(; *temp != ‘\0’; temp++);

return (int)(temp - str);

}

void my_memcpy(void dest, const void *src, size_t count)

{

char temp_dest = (char)dest;

char temp_src = (char)src;

assert(dest != NULL && src != NULL);

while(count–)

*temp_dest++ = *temp_src++;

return dest;

}

int my_strcmp(char *str1, char *str2)

{

int ret = 0;

while((ret = (unsigned char)str1 - (unsigned char)str2)&&*str1&&*str2)

{

str1++;

str2++;

}

if(ret < 0)

ret = -1;

else if(ret > 0)

ret = 1;

return ret;

}

七、链表操作

(1)逆序?

node *reverse_node(node *head)

{

node *record, *current;

if(head == NULL || head->next == NULL)

return head;

current = head->next;

head->next = NULL;

while(current != NULL)

{

record = current->next;

current->next = head->next;

head->next = current;

current = record;

}

return head;

}

(2)插入

node *add_node(node *head, node *data)

{

node *current = head;

while(current->next != NULL)

current = current->next;

current->next = data;

data->next = NULL;

return head;

}

(3)删除

node *del_node(node *head, node *data)

{

node *pf,*pb;

pf = head->next;

while(pf != NULL && pf->data != data->data)

{

pb = pf;

pf = pf->next;

}

if(pf->data == data->data)

{

pb->next = pf->next;

free(pf);

}

else

printf(“NO node!\n”);

}

(4)单链表(非循环)倒数第4个元素?

思路:让第一个元素先走四步,然后两个游标指针一起走。

node *Get_Node(node *head)

{

int i;

node *first = head;

node *back = head;

for(i=0; i<4; i++)

{

if(first->next == NULL)

printf(“Node less than four!\n”);

first = first->next;

}

while(first != NULL)

{

first = first->next;

back = back->next;

}

return back;

}

(5)如何找出链表中间元素?

思路:让前一个指针每次走两步,后一个指针每次走一步。

node *Get_middle_node(node *head)

{

node *first = head;

node *back = head;

while(first != NULL)

{

first = first->next->next;

back = back->next;

}

return back;

}

(6)删除一个无头单链表的一个节点(链表很长10万以上)?

思路:把当前节点的下个节点数据拷贝到当前节点,然后删除下一个节点。

void del_node(node *del_node)

{

node *record = del_node->next;

node *current = del_node;

current->data = record->data; //数据拷贝,假设结构体数据为data

current->next = record->next; //指针跳转

free(record);

}

(7)如何判断一个单链表是否有环?

思路:我们用一个步长1和一个步长2的游标指针遍历链表,观察是否有两个游标相遇的时刻。

int judge_list_circle(node *head)

{

node *first = head;

node *back = head;

while(first->next != NULL && back->next->next != NULL)

{

first = first->next;

back = back->next->next;

if(first == back)

return true;

}

return false;

}

八、两种排序法(冒泡排序、选择排序)

void bubble(int a[], int n)

{

int i,j,temp;

for(i=0; i

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值