1、tcp/udp是属于哪一层?tcp/udp有何优缺点?
tcp /udp属于运输层
TCP 服务提供了数据流传输、可靠性、有效流控制、全双工操作和多路复用技术等。
与 TCP 不同, UDP 并不提供对 IP 协议的可靠机制、流控制以及错误恢复功能等。由于 UDP 比较简单, UDP 头包含很少的字节,比 TCP 负载消耗少。
tcp: 提供稳定的传输服务,有流量控制,缺点是包头大,冗余性不好
udp: 不提供稳定的服务,包头小,开销小
2、分析下方代码(数值溢出和强转)
char a = 100; char b = 150;//10010110 //01101010
unsigned char c ;
c = (a < b)? a:b;
则c的数值=___ ,
答:c=150 ;
被赋值为 150
,这超出了 char
的正数范围,因为char通常只能存储-128到127之间的值(150
对应的二进制是 10010110
,但存储在 char
中时,它会被解释为 -106
的二进制表示,即 10010110
);所以a<b的判断为假,c=b;所以c=150(由于 c
是无符号的,所以 -106
会被转换为无符号整数对应的值。在8位无符号整数中,这将是 256 - 106 = 150
)
3、在C语言中memcpy和memmove是一样的吗?
memmove()与memcpy()一样都是用来拷贝src所指向内存内容前n个字节到dest所指的地址上;
不同是,当src和dest所指的内存区域重叠时,memmove()仍然可以正确处理,不过执行效率上略慢些。
4、C语言程序代码优化方法
- * 选择合适的算法和数据结构
- * 使用尽量小的数据类型
- * 使用自加、自减指令
- * 减少运算的强度
求余运算(a=a%8改为a=a&7)
平方运算(a=pow(a,2.0)改为a=a*a)
用移位实现乘除法运算
* 延时函数的自加改为自减
* switch语句中根据发生频率来进行case排序
5、找出一个字符串中一个最长的连续的数字,并标注出位置和个数。
答:
void main()
{
char input[100];
char output[100] = {0};
int count = 0, maxlen = 0, i = 0;
char *in=input, *out=output,*temp=NULL,*final=NULL;
printf("Please input string(length under 100):\n");
scanf("%s", input);
printf("Input string is %s\n", input);
while(*in!='\0')
{
if(*in>='0'&&*in<='9')
{
count=0;
temp=in;
for(;(*in>='0')&&(*in<='9');in++)
count++;
if (maxlen<count)
{
maxlen=count;
=temp;
}
}
in++;
}
for(i=0; i<maxlen; i++)
*(out++) = *(final++);
*out='\0';
printf("Maxlen is %d\n", maxlen);
printf("Output is %s\n", output);
}
6、写出螺旋矩阵
螺旋矩阵
答:该代码段是一个C语言函数,用于创建一个顺时针螺旋矩阵。函数接受两个整数参数 m
和 n
,分别表示矩阵的行数和列数。矩阵的大小是 m x n
,元素值从1开始递增填充。算出螺旋矩阵的“半径” k = small / 2
,其中 small
是 m
和 n
中的较小值
void Matrix(int m,int n) //顺时针
{
int i,j,a=1;
int s[100][100];
int small = (m<n)?m:n;
int k=small/2;
for(i=0;i<k;i++)
{
for(j=i;j<n-i-1;j++)
s[i][j]=a++;
for(j=i;j<m-i-1;j++)
s[j][n-i-1]=a++;
for(j=n-i-1;j>i;j--)
s[m-i-1][j]=a++;
for(j=m-i-1;j>i;j--)
s[j][i]=a++;
}
if(small & 1)
{
if(m<n)
for(i=k;i<n-k;++i)
s[k][i]=a++;
else
for(i=k;i<m-k;++i)
s[i][k]=a++;
}
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
printf("=",s[i][j]);
printf("\n");
}
}
7、输出下方程序结果(内存偏移)
int *a = (int *)2;
printf("%d",a+3);
答:14; int *a = (int *)2;
你实际上是在将整数 2
强制转换为指向整数的指针,并将这个指针赋值给变量 a
。这意味着 a
现在指向内存地址 2;
a+3,实际上是在将指针 a
向后移动3个 int
的大小,int类型地址加1 相当于加4个字节;a+3
就会是 2 + 3 * 4 = 14。
8、输出下方代码结果(读字节操作)
main()
{
char a,b,c,d;
scanf("%c%c",&a,&b);
c=getchar(); d=getchar();
printf("%c%c%c%c\n",a,b,c,d);
}当执行程序时,按下列方式输入数据(从第1列开始,<CR>代表回车,注意:回车也是一个字符)
12<CR>
34<CR>
则输出结果是
输出:
12
3
该代码的功能是读取四个字符并打印它们。不过,由于 scanf
和 getchar
的特性,读取字符的方式可能会有一些微妙之处。
scanf("%c%c",&a,&b);标准输入读取两个字符,分别存储在变量 a
和 b;getchar()
函数会读取输入流中的下一个字符,包括空格、制表符和换行符;所以一概是12\n3
9、评价下方代码(指向常亮的指针和常指针)
int a[2] = {1, 2}; //a是指向常量的指针,指针可以变,指针指向的内容不可以变
const int *p = a;//与int const *p = a;等价
p++; //ok
*p = 10; //error --指针指向的内容不可以变!
int* const p2 = a;//与int const *p2 = a;不等价,
p2++; //error
*p2 = 10; //ok
const int* const p3 = a;//指向常量的常指针,都不可以改变
p3++;//error
*p3 = 10;//error
分析:
(1)const int *p = a; // 与 int const *p = a; 等价
这里声明了一个指向常量的指针 p
,即 p
可以指向不同的地址,但是它所指向的内容是不可变的
(2)int* const p2 = a; // 与 const int* p2 = a; 不等价
声明了一个常指针 p2
,它指向一个整型变量。这意味着 p2
一旦指向了某个地址,就不能再指向其他地址,但是它所指向的内容是可以修改的。
(3)const int* const p3 = a,这样声明的,那么它的指向和它所指向的内容都不能改变
10、实现二分擦找算法
int binary_search(int array[],int value,int size)
{
int low=0,high=size-1,mid;
while(low<=high) //只要高低不碰头就继续二分查找
{
mid=(low+high)/2;
if(value==array[mid]) //比较是不是与中间元素相等
return mid;
else if(value > array[mid]) //每查找一次,就判断一次所要查找变量所在范围,并继续二分
low=mid; //如果大小中间值,下限移到中间的后一个位,上限不变,往高方向二分
else
high=mid; //上限移到中间的前一个位,往低方向二分
}
return -1;
}
/*双向循环链表插入函数*/
TYPE *insert_link(TYPE *head,TYPE *p_in)
{
TYPE *p_mov = head,p_front = head;
if(head == NULL)
{
head = p_in;
p_in->next = head;
p_perior = head;
}
else
{
while((p_in->[] > p_mov->[]) && (p_mov->next != head))
{
p_front = p_mov;
p_mov = p_mov->next;
}
if(p_in->[] <= p_mov->[])
{
if(head == p_mov)
{
p_in->prior = head->prior;
head->prior->next = p_in;
p_in->next = p_mov;
p_mov->prior = p_in;
head = p_in;
}
else
{
pf->next = p_in;
p_in->prior = p_front;
p_in->next = p_mov;
p_mov->prior = p_in;
}
}
else
{
p_mov->next = p_in;
p_in->prior = p_mov;
p_in->next = head;
head->prior = p_in;
}
}
return head;
}
/*双向链表删除函数*/
TYPE *delete_link(TYPE *head,int num)
{
TYPE *p_mov = head,p_front = head;
if(head == NULL)
printf("Not link\n");
while((p_mov->num != num) && (p_mov->next != head))
{
p_front = p_mov;
p_mov = p_mov->next;
}
if(p_mov->num == num)
{
if(p_mov == head)
{
if(p_mov->next == head && p_mov->prior == head)
{
free(pb);
head =NULL;
return head;
}
head->next->prior = head->prior;
head->prior->next = head->next;
head = head->next;
}
else
{
p_front->next = p_mov->next;
p_mov->next->prior = p_front;
}
free(p_mov);
printf("The node is delete\n");
}
else
{
printf("The node not been found\n");
}
return head;
}
11、简述中断活动的全过程:
1、中断请求:中断事件一旦发生或者中断条件一旦构成,中断源提交“申请报告”,
与请求CPU暂时放下目前的工作而转为中断源作为专项服务
2、中断屏蔽:虽然中断源提交了“申请报告”,但是,是否得到CPU的响应,
还要取决于“申请报告”是否能够通过2道或者3道“关卡”(中断屏蔽)送达CPU
(相应的中断屏蔽位等于1,为关卡放行;反之相应的中断屏蔽位等于0,为关卡禁止通行);
3、中断响应:如果一路放行,则CPU响应中断后,将被打断的工作断点记录下来
(把断点地址保护到堆栈),挂起“不再受理其他申请报告牌”
(清除全局中断标志位GIE=0),跳转到中断服务子程序
4、保护现场:在处理新任务时可能破坏原有的工作现场,所以需要对工作现场和工作环境进行适当保护;
5、调查中断源:检查“申请报告”是由哪个中断源提交的,以便作出有针对性的服务;
6、中断处理:开始对查明的中断源进行有针对性的中断服务;
7、清除标志:在处理完毕相应的任务之后,需要进行撤消登记(清除中断标志),以避免造成重复响应;
8、恢复现场:恢复前面曾经被保护起来的工作现场,以便继续执行被中断的工作;
9、中断返回:将被打断的工作断点找回来(从堆栈中恢复断点地址),
并摘下“不再受理其他申请报告牌”(GIE=1),继续执行原先被打断的工作。
12、linux进程间通讯的几种方式的特点和优缺点,和适用场合。分类:
#管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
#有名管道(named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
#信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。 它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
#消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
#信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
#共享内存( shared memory):共享内存就是映射一段能被其他进程所访问的内存,
这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,
它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,
如信号量,配合使用,来实现进程间的同步和通信。
#套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,
它可用于不同及其间的进程通信。
13、如何防止同时产生大量的线程?
方法是使用线程池,线程池具有可以同时提高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队等候
14、评价下方代码(memset)
main()
{
char *p1=“name”;
char *p2;
p2=(char*)malloc(20);
memset (p2, 0, 20);
while(*p2++ = *p1++);
printf(“%s\n”,p2);
}
答案:输出为空串;
因为在whilie语句执行之前,memset 将p2的内容全置为\0, 因此第一次循环p2++已经指到了'\0'了;程序就结束了。
15、操作系统的内存分配一般有哪几种方式,各有什么优缺点?
答:两者方式,定长和变长。
变长:内存时比较灵活,但是易产生内存碎片。
定长:灵活性差,但分配效率较高,不会产生内存碎片
16、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
答:可以,在不同的C文件中以static形式来声明同名全局变量。可以在不同的C文件中声明同名的全局变量,
前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错
17、确定模块的功能和模块的接口是在软件设计的那个队段完成的?
答:概要设计阶段 。
18、评价下方代码(变量范围)
#define N 500
unsigned char count;
for(count=0;count < N;count++)
{
printf("---%d---\n",count);
}
答:程序走入死循环,因为unsigned char 最大为255
19、评价结构体的内存大小
给定结构
struct A
{
char t:4; //4位
char k:4; //4位
unsigned short i:8; //8位
unsigned long m; // 偏移2字节保证4字节对齐
};
答:共8字节 ,考查结构体的定义和自动对齐;
20、Ping操作使用的是什么一些,该协议的功能有哪些?
ICMP(ICMP协议对于网络安全具有极其重要的意义)功能主要有:
- · 侦测远端主机是否存在。
- · 建立及维护路由资料。
- · 重导资料传送路径。
- · 资料流量控制。
单向、双向链表操作、写一个快速排序算法(原理:找一个基准值,分别将大于和小于基准值的数据放到基准值左右两边,即一次划分。由于处在两边的数据也是无序的,所以再用同样的划分方法对左右两边的序列进行再次划分,直到划分元素只剩1个时结束,)
21、单向链表逆序算法
LONDE *link_reverse(LNODE *head)
{
LNODE *pb,*pt;
if(head == NULL)
return head;
pb = head->next;
head->next=NULL;
while(pb != NULL)
{
pt = pb->next;
pb->next = head;
head = pb;
pb = pt;
}
return head;
}
22、快速排序算法
void quick_sort(int num,int start_num,int end_num)
{
if(start_num < end_num)
{
int i = start_num;
int j = end_num;
int temp = num[start_num];
while(i < j)
{
while(i < j && num[j] < temp)
j--;
if(i < j)
num[i++] = num[j];//把小于基准值放在左边
while(i < j && num[i] >= temp)
i++;
if(i < j)
num[j--] = num[i];//把大于基准值放在右边
}
num[i] = temp;
quick_sort(num,start_num,i-1);
quick_sort(num,i+1,end_num);
}
}