您的位置:首页 > 职场人生

嵌入式软件工程师 面试经验

2016-06-07 19:28 260 查看
一、什么叫可重入?

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

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

它,转入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<n-1; i++)
for(j=i+1; j<n; j++)
{
if(a[i] > a[j])
{
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
return 0;

}

void choise(int a[], int n)

{
int i, j, temp, min;

for(i=0; i<n-1; i++)
{
min=i;
if(j=i+1; j<n; j++)
{
if(a[i] > a[j])
min = j;
}
if(min != i)
{
temp = a[i];
a[i] = a[min];
a[min] = temp;
}
}
return 0;

}

九、字符串反转?

char str_rev(char *str, size_t len)

{
char *start = str;
char *end = str+len-1;
char ch;

if(str != NULL)
{
while(start < end)
{
ch = *start;
*start++ = *end;
*end-- = ch;
}
}
return str;

}

十、关键字volatile有什么含意 并给出三个不同的例子。

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会

去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重

新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

1) 并行设备的硬件寄存器(如:状态寄存器);

2) 一个中断服务子程序中会访问到的非自动变量;

3) 多线程应用中被几个任务共享的变量;

1) 一个参数既可以是const还可以是volatile吗?解释为什么。

2) 一个指针可以是volatile 吗?解释为什么。

3) 下面的函数有什么错误:

int square(volatile int *ptr)

{

  return *ptr  *  *ptr;

}

答案:

1) 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想

不到地改变。它是const因为程序不应该试图去修改它。

2) 是的。尽管这并不很常见。一个例子是当一个中断服务子程序修该一个指向一个buffer的指针时。

3)这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向

一个volatile型参数,编译器将产生类似下面的代码:

int square(volatile int *ptr)

{
int a,b;
a = *ptr;
b = *ptr;
return a * b;

}

由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:

long square(volatile int *ptr)

{
int a;
a = *ptr;
return a * a;

}

十一、内存分配应该注意什么问题?

1、检查内存分配是否成功。

2、内存生命周期,程序结束时记得free,避免内存泄露。

3、使用过程中避免指针越界访问,会导致不必要的错误。

十二、字符串回文判断?

int str_is_resv(char *str)

{
int i;
int  len = strlen(str);
char *start = str;
char *end   = str+len-1;

for(i=0; i<len/2; i++)
{
if(*(start+i) != *(end-i))
return -1;
}
return 0;

}

十三、字符串循环左移?

//循环左移,库函数实现

char *str_rev1(char *str, int bit)

{
int len = strlen(str);
int n = bit%len;
char temp[128] = "";

strcpy(temp, str);
strncpy(str, str+n, len-n);
str[len-n] = '\0';
strncat(str, temp, n);

return str;

}

//循环左移,不用库函数

char *str_rev2(char *str, int bit)

{
char temp_str[128];
int i,j;
int str_len = strlen(str);
bit = bit%str_len;

for(i=0; i<bit; i++)
{
*(temp_str+i) = *(str+i);
}

for(i=0; i<str_len-bit; i++)
{
*(str+i) = *(str+bit+i);
}

for(j=0; j<bit; j++)
{
*(str+i+j) = *(temp_str+j);
}
return str;

}

十四、如何编程判断大小端?

/*返回1表示小端,返回0表示大端*/

int check_cpu_endian()

{
union
{
unsigned int a;
unsigned char b;
}s;
s.a = 1;      //0x01
return (s.b == 1);

}

十五、如何判断一个字节(char)里面多少bit被置1?

int count_bit(int value)

{
int i, count=0;

for(i=0; i<8; i++)
{
if(value&0x01 == 0x01)
count++;
value  = value >> 0x01;
}
return count;

}

十六、判断一个整数里面有多少个1?

int count_bit(int value)

{
int count=0;

while(value)
{
count++;
value &= value-1;
}
return count;

}

十七、二分法查找?

int mid_serach(int arry[], int key, int size)

{
int low, high, mid;

low = 0;
high = size-1;

while(low < high)
{
mid = (low+high)/2;
if(key == arry[mid])
return mid;
else if(key < arry[mid])
high = mid-1;
else if(key > arry[mid])
low = mid+1;
}
return -1;

}

十八、字符串转成十进制数?

int str_to_int(char *str)

{
int temp = 0;

while(*str != '\0')
{
temp = temp*10 + *str - '0';
str++;
}
//temp = atol(str);    //库函数一步完成
return temp;

}

十九、OSI模型分哪几层?

物理层、数据链路层、网络层、传输层、应用层、表示层、会话层

集线器hub工作在OSI参考模型的(物理)层;

网卡工作在OSI参考模型的(物理)层;

路由器router工作在OSI参考模型的(网络)层;

交换机Switch工作在OSI参考模型的(数据链路)层。

二十、给定某个整数n(0-10000),求小于等于n的整数中与7有关的整数

(被7整除或者数字中含有7,只要求求出个数,不用列觉出每一个数字)

int fun(int data)

{
int count=0;
int i;
int first,second,third,four;

for(i=1; i<=data; i++)
{
first  = i%10;
second = i/10%10;
third  = i/100%10;
four   = i/1000;
if ((i%7 == 0) || (first == 7) || (second == 7) || (third == 7) || (four == 7))

{
//printf("qian bai shi ge is:%d %d %d %d\n", four, third, second, first);
count++;
}
}
return count;

}

二十一、将字符串中每个单词的第一个字符变为大写?

char *fun(char *str)

{
int i;
int flag = -1;
int len = strlen(str);

if(islower(str[0]))
{
str[0] = toupper(str[0]);
}

for(i=1; i<len; i++)
{
if(str[i]==' ' && islower(str[i+1]))
{
str[i+1] = toupper(str[i+1]);
}

}
return str;

}

二十二、统计出具有n个元素的一维整数数组中大于等于

所有元素平均值的元素个数。验证该函数?

int fun(int a[], int size)

{
int i=0;
float sum=0, aver=0;
int count=0;

for(i=0; i<size; i++)
sum +=a[i];
aver = sum/size;

for(i=0; i<size; i++)
{
if(a[i] > aver)
count++;
}
printf("The aver is %f\n", aver);

return count;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  经验 面试