例.系统调用和库函数的区别
- 所有 C 函数库是相同的,而各个操作系统的系统调用是不同的。
- 函数库调用是调用函数库中的一个程序,而系统调用是调用系统内核的服务。
- 函数库调用是与用户程序相联系,而系统调用是操作系统的一个进入点
- 函数库调用是在用户地址空间执行,而系统调用是在内核地址空间执行
- 函数库调用的运行时间属于「用户」时间,而系统调用的运行时间属于「系统」时间
- 函数库调用属于过程调用,开销较小,而系统调用需要切换到内核上下文环境然后切换回来,开销较大
- 在C函数库libc中大约 300 个程序,在 UNIX 中大约有 90 个系统调用
- 函数库典型的 C 函数:system, fprintf, malloc,而典型的系统调用:chdir, fork, write, brk
老师给的标答:
系统调用是操作系统提供给用户程序访问并使用操作系统内核提供的各种服务的一组特殊接口。
系统调用:
- 各个操作系统的系统调用是不同的;
- 由操作系统提供的一类函数;
- 提供操作系统才能实现的服务;
- 内核态,即在内核地址空间内执行;
- 需要在用户空间和内核空间环境上下切换,开销较大;
库函数调用:
- 在所有的ANSI C编译器版本中,C库函数是相同的;
- 通常调用的是代码库中的函数;
- 这些库函数和程序链接,静态链接和动态链接都可以;
- 用户态,即在用户地址空间内执行;
- 属于过程调用,调用开销较小;
例.什么是系统调用?系统调用是通过什么方式陷入内核态的?请写出你对系统调用的理解。什么是文件I/O和标准I/O库?文件I/O和标准I/O库的区别?
老师给的标答:
- 系统调用是指操作系统提供给用户程序调用的一组特殊接口,用户程序可以通过这组接口获得操作系统内核提供的服务。
- 系统调用是通过软件中断方式陷入内核的
- linux的文件I/O是由操作系统提供的基本I/O服务, 标准I/O库通过封装系统调用,提供了一个到底层I/O的接口。
- 标准I/O默认采用了缓冲机制,还创建了一个包含文件和缓冲区相关数据的数据结构;文件I/O一般没有采用缓冲模式,需要自己创建缓冲区。一种是标准库封装系统调用而成,更高级 ,一种是系统提供的,比较低级;标准I/O可移植性高、文件I/O可移植性低。
例.格子中输出:StringInGrid函数会在一个指定大小的格子中打印指定的字符串。要求字符串在水平、垂直两个方向上都居中。如果字符串太长,就截断。如果不能恰好居中,可以稍稍偏左或者偏上一点。下面的程序实现这个逻辑,请填写划线部分缺少的代码。
#include <stdio.h>
#include <string.h>
void StringInGrid(int width, int height, const char* s)
{
int i,k;
char buf[1000];
strcpy(buf, s);
if(strlen(s)>width-2) buf[width-2]=0;
printf("+");
for(i=0;i<width-2;i++) printf("-");
printf("+\n");
for(k=1; k<(height-1)/2;k++){
printf("|");
for(i=0;i<width-2;i++) printf(" ");
printf("|\n");
}
printf("|");
printf("%*s%s%*s",_____________________________________________); //填空
printf("|\n");
for(k=(height-1)/2+1; k<height-1; k++){
printf("|");
for(i=0;i<width-2;i++) printf(" ");
printf("|\n");
}
printf("+");
for(i=0;i<width-2;i++) printf("-");
printf("+\n");
}
int main()
{
StringInGrid(20,6,"abcd1234");
return 0;
}
对于题目中数据,应该输出:
答案:(width-strlen(buf)-2)/2,"",buf, (width-1-strlen(buf))/2, ""
注意:
- *修饰符在scanf中是起到过滤读入的作用。比如一个有三列数值的数据,我只想得到第2列数值,可以在循环里用scanf(“%*d%d%*d”, a[i])来读入某行的第2个数值到a[i]。
- *修饰符在printf中的含义完全不同。如果写成printf(“%6d”, 123),对于很多人来说应该就不会陌生了,这是设置域宽的意思。同理,%6s也是域宽。* 修饰符正是用来更灵活的控制域宽。使用%*s,表示这里的具体域宽值由后面的实参决定,如printf(“%*s”, 6, “abc”)就是把”abc”放到在域宽为6的空间中右对齐。
- 知道了前面这两点,第二个控制域宽要注意。因为题目要求是如果不能正好居中对齐就偏左,所以要考虑空格是奇数的情况,不能直接空格数除以二就结束了。
例.九数组分数:1,2,3...9 这九个数字组成一个分数,其值恰好为1/3,如何组法?下面的程序实现了该功能,请填写划线部分缺失的代码。
#include <stdio.h>
void test(int x[])
{
int a = x[0]*1000 + x[1]*100 + x[2]*10 + x[3];
int b = x[4]*10000 + x[5]*1000 + x[6]*100 + x[7]*10 + x[8];
if(a*3==b) printf("%d / %d\n", a, b);
}
void f(int x[], int k)
{
int i,t;
if(k>=9){
test(x);
return;
}
for(i=k; i<9; i++){
{t=x[k]; x[k]=x[i]; x[i]=t;}
f(x,k+1);
_____________________________________________ // 填空处
}
}
int main()
{
int x[] = {1,2,3,4,5,6,7,8,9};
f(x,0);
return 0;
}
答案:
{t=x[k]; x[k]=x[i]; x[i]=t;}
其实就是全排列的递归方法
例.tcp,udp的区别
- TCP面向连接(如打电话要先拨号建立连接,在互通之前,面向连接的协议会先建立连接,如 TCP 有三次握手,而 UDP 不会);UDP是无连接的,即发送数据之前不需要建立连接
- TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达(TCP 报文头里面的序号能使 TCP 的数据按序到达,报文头里面的确认序号能保证不丢包,累计确认及超时重传机制);UDP尽最大努力交付,即不保证可靠交付
- TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的(流模式vs数据包模式)
- TCP 拥有流量控制及拥塞控制的机制;UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
- 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
- TCP首部开销20字节;UDP的首部开销小,只有8个字节
- TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
- TCP对系统资源要求较多,UDP对系统资源要求较少。
老师给的标答:
TCP面向连接的传输控制协议,传输可靠(保证数据的正确性和数据顺序)、用于传输大量数据(流模式)、速度慢,
建立链接需要开销较多(时间,系统资源等);
UDP面向非连接的数据包服务,传输不可靠,用于传输少量数据(数据包模式)、速度快,具有较好的实时性,
工作效率较TCP协议高;
例.写出7层网络模型和4层网络模型
- 7层:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层
- 4层:应用层,传输层,网络层,网络接口层
例.手链样式:小明有3颗红珊瑚,4颗白珊瑚,5颗黄玛瑙。他想用它们串成一圈作为手链,送给女朋友。现在小明想知道:如果考虑手链可以随意转动或翻转,一共可以有多少不同的组合样式呢?请你提交该整数。不要填写任何多余的内容或说明性的文字。
网上查的,用c++解(因为会用到vector,c没有vector需要自己写),涉及函数应该都只适用与c++
方法就是暴力枚举判重,有找到单纯用数学方法解的但只懂了一半(仿佛回到小学奥数)
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
vector<string> vec;
int main()
{
string str="AAABBBBCCCCC";
int res=0;
do
{
int flag=1;
for(int i=0;i<vec.size();i++)
{
if(vec[i].find(str)!=string::npos)
{
flag=0;
break;
}
}
if(flag)
{
res++;
string tmp=str+str;
vec.push_back(tmp);
reverse(tmp.begin(),tmp.end());
vec.push_back(tmp);
}
}while(next_permutation(str.begin(),str.end()));
cout<<res<<endl;
return 0;
}
主要运用stl中的string.find(str),next_permutation(str.begin(),str.end()),reverse(str.begin,str.end())。
在一个vector中存下已经出现过的排列,每一个排列先在vector中查找是否已存在该排列,若不存在,则res++,并将str+str(可任意转动)和reverse(str.begin(),str.end()) (可任意翻转) 压入vector。
find()函数:返回类型:size_type,即一个无符号整数(按打印出来的算)。若查找成功,返回按查找规则找到的第一个字符或子串的位置;若查找失败,返回npos,即-1(打印出来为4294967295)。
reverse()函数:需包含头文件 #include <algorithm> ,无返回类型。用于反转在[first,last)范围内的顺序(包括first指向的元素,不包括last指向的元素)。连接中有两个使用例子分别是string类和vector类。
next_permutation()函数(全排列算法):需包含头文件 #include <algorithm> ,在begin和end之间(指针类型)求出下一个排列组合,能找到下一个就返回1,找不到就返回0。