最近收集了许多嵌软的面试题,内容都是在很多文章中剪下来的!
面试不仅仅是C、C++的问题,相关涉广,还有系统、网络和数据库等。
应用层:HTTP,SNMP,FTP,DNS,Telnet。
表示层:
会议层:
传输层:TCP,UDP。
网络层:IP,ICMP,IGMP,OSPF,ARP,RARP。
数据链路层:PPP。
物理层:比特流,IEEE802,IEEE802.2。
应用层、传输层、网络层、数据链路层。
按照时间顺序:
附参考链接:http://t.csdn.cn/I3QGLhttp://t.csdn.cn/I3QGL
strcat函数原型(把 src 所指向的字符串追加到 dest 所指向的字符串的结尾)
#include "assert.h"
//将源字符串加const,表明其为输入参数,保持值不变
char *mew_strcat(char *dest,const char *src)
{
assert(dest&&src); //对源地址和目的地址加非0断言
//if(!(dest && src)) return NULL; //与上行代码等同
//若使用while(*dest++),则会出错,指向'\0'之后,会出现dest++,则指向了个'\0'的下一个位置
//循环体内的++可以使指向字符串结束标志'\0'
while(*dest !='\0') dest++;
while((*dest++=*src++)!='\0');
return dest;
}
为什么要char * 的返回值:
主要是为了实现链式表达式。如strcpy(buf,strcat(dest,src));。
strcpy()函数的原型是char *strcpy(char *strDest,const char *strSRC)。其中,strDest是目标字符串,strSRC是源字符串。
对于断言关键字assert,下面进行简单讲解:
#include "assert.h"
void assert( int expression );
assert的作用是现计算表达式 expression,如果其值为假(即为0),那么它先向 stderr 打印一条出错信息,然后通过调用 abort 来终止程序运行。
使用 assert 的缺点是,频繁的调用会极大的影响程序的性能,增加额外的开销。
在调试结束后,可以通过在包含 #include 的语句之前插入 #define NDEBUG 来禁用 assert 调用。
int new_atoi(char *str)
{
int sum=0,status=1;
if(str == NULL) /*判断是否为空*/ return 0;
if(*str == '-') /*判断是否负号*/
{
status = -1;
str++;
}
while((*str)!='\0')
{
sum=sum*10+ ((*str)-'0');
str++;
}
return sum*status;
}
/*方法1*/
typedef union {
int i;
char c;
}my_union;
int checkSystem1(void)
{
my_union u;
u.i = 1;
//return (u.i == u.c);//返回1表示小端,返回0表示大端
return (u.c);//返回1表示小端,返回0表示大端
}
/*方法2*/
int checkSystem2(void)
{
int i=1;
return *(char *)&i;//返回1表示小端,返回0表示大端
}
大端存储模式:数据的低位,存放在地址的高位。
小端存储模式:数据的低位,存放在地址的低位。
我们的pc机一般都是小端模式,在低位置存放的就是低位数据。
//方法一:
void checkCpuMode(void)
{
int i = 0x12345678; //i为4字节,32位。
char *c = (char *)&i; //c位1字节,8位。
//将i字符化得c,判断c的8位存储的是i数据哪里的位置。
if(*c == 0x78)
printf("little endian"); //小端格式
else
printf("big endian\n"); //大端格式
}
//方法二:
void checkCpuMode(void)
{
int a = 0x12345678;
if((char)a == 0x12)
printf("big endian\n"); //大端格式
else
printf("little endian\n"); //小端格式
}
int select(unsigned char data)
{
int count=0;
while(data!=0)
{
count += (data & 1);
data >>= 1;
}
return count;
}
附参考链接:http://t.csdn.cn/z67KUhttp://t.csdn.cn/z67KU
全局变量和静态变量存储在全局/静态存储区。常量存储在常量存储区。
栈存储的是函数参数值、局部变量等。
进程:指在系统中正在运行的一个应用程序。程序一旦运行就是一个进程。进程是资源分配的最小单位。
线程:程序执行的最小单位。
这里借阅了其他文章的想法,其他博主写的很好。
做个简单的比喻:进程=火车,线程=车厢
线程在进程下运行(单纯的车厢无法运行)
一个进程可以包含多个线程(一辆火车可以有多个车厢)
不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-"互斥锁"
进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”。
附参考链接:线程和进程的区别是什么? - 知乎看了一遍排在前面的答案,类似”进程是资源分配的最小单位,线程是CPU调度的最小单位“这样的回答感觉太…https://www.zhihu.com/question/25532384
int printf(const char *fmt,...);
栈,又称堆栈,是计算机中最常用的一种数据结构,保存数据的一块连续内存;比如函数的调用是用堆栈实现的。
串行通信 |
并行通信 |
|
传输原理 |
数据按位顺序传输 |
数据各个位同时传输 |
优点 |
占用引脚资源少 |
速度快 |
缺点 |
速度相对较慢 |
占用引脚资源多 |
列举 |
UART、SPI、IIC、RS485、CAN |
SDIO、FSMC |
异步通信和同步通信;并行速度快,串行口线间干扰小。
//对于第五点,32位编译器
#include
int main()
{
char a = 30;
char *p = &a;
char &b = a;
cout<
21:FTP(文件传输协议) 23:telnet(远程登录) 25:SMTP(简单邮件传输协议)
53:DNS(域名服务器) 80:HTTP(超文本传输协议) 109:POP2(邮局协议2)
110:POP3(邮局协议3)
地址解析协议(ARP)的作用是将IP地址转换成MAC地址;反地址解析协议(RARP)则负责将MAC地址转换成IP地址。
#define COUNT(table) ( sizeof(table)/sizeof(table[0]) )
#define COUNT(table) ( sizeof(table)/sizeof(*(table)) )
两种方式都可以,意思都一样。
当且仅当两个子树的高度差不超过1时,这个树是平衡二叉树。
全局变量,储存在全局/静态存储区.进入main函数之前就被创建.生命周期为整个源程序;
局部变量,在栈中分配.在函数被调用时才被创建.生命周期为函数内。
全局变量和静态变量存储在全局/静态存储区;栈存储的是函数的参数值,局部变量的值等。
数组:顺序存储,插入删除效率低,访问元素效率高。
链表:随机存储,插入删除效率高,访问元素效率低。
先进先出算法,最短CPU运行期优先调度算法,轮转法,多级队列方法
答:可以用引用头文件的方式,也可以用extern关键字。
如果用引用头文件方式来引用某个在头文件中声明的全局变量,假定你将那个变量写错了,那么在编译期间会报错。
如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。
答:可以,在不同的C文件中以static形式来声明同名全局变量。
答:没问题,和while(1)相同。
设2个栈为A,B, 一开始均为空.
入队: 将新元素push入栈A;
出队:
没有回收垃圾资源。
if(x>0.000001&&x<-0.000001);
IP地址由两部分组成,网络号和主机号。
main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1); //&a相当于变成了行指针,加1则变成了下一行首地址
printf("%d,%d",*(a+1),*(ptr-1));
}
*(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5
unsigned char *p=(unsigned char *)0x0801000
unsigned char *q=(unsigned char *)0x0810000
p+5 =? 0x0801005
q+5 =? 0x0810005
printf("%d\n",5["abcdef"]);输出'f'的ACSII值,如果是4["abcdef"]则输出'e'的ACSII的值。
信号量、条件变量、互斥锁
0,1,2,3
需要注意的是,对于条件判断,只要其一还能通过,就能执行。
答案是2+3*4=14;int类型地址加1 相当于加4个字节
方法是使用线程池,线程池具有可以同时提高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队等候。
定长和变长。
变长:比较灵活,但是易产生内存碎片。
定长:灵活性差,但分配效率较高,不会产生内存碎片。
(原理:找一个基准值,分别将大于和小于基准值的数据放到基准值左右两边,即一次划分。由于处在两边的数据也是无序的,所以再用同样的划分方法对左右两边的序列进行再次划分,直到划分元素只剩1个时结束)
这里借阅了其他文章的想法,其他博主写的很好。
void quick_sort(int a[],int start,int end)
{
if(start < end)
{
int i = start,j = end,temp = a[start];
while(i < j)
{
while(i < j && a[j] < temp) j--;
if(i < j) a[i++] = a[j];//把小于基准值放在左边
while(i < j && a[i] >= temp) i++;
if(i < j) a[j--] = a[i];//把大于基准值放在右边
}
a[i] = temp;
quick_sort(a,start,i-1);
quick_sort(a,i+1,end);
}
}