笔试题目

1、系统调用和库函数的区别:

库函数是语言本身的一部分,而系统函数是内核提供给应用程序的接口,属于系统的一部分。
函数库调用是语言或应用程序的一部分,而系统调用是操作系统的一部分。
用户应用程序访问并使用内核所提供的各种服系统硬件和各种操务的途径即是系统调用。在内核和用户应用程序相交界的地方,内核提供了一组系统调用接口,通过这组接口,应用程序可以访问操作系统资源。
1.系统调用是为了方便应用使用操作系统的接口,而库函数是为了方便人们编写应用程序而引出的,比如你自己编写一个函数其实也可以说就是一个库函数。
2.系统调用可以理解为内核提供给我们在用户态用的接口函数,可以认为是某种内核的库函数。
3.read就是系统调用,而fread就是C标准库函数.

2、sizeof和strlen的区别:
本质不同:sizeof是运算符、关键字,strlen是函数。
处理的阶段不同:sizeof在编译时就已经完成了计算,strlen是在运行时进行计算。
计算的内容不同:sizeof可以计算一个字符串的长度也可以计算一个数据变量所占内存的大小,strlen只能计算字符串的长度。
对\0的处理方式不同:sizeof会把\0也计算在内,strlen遇到\0停止,但是不会把\0计算在内。
对于指针的处理不同:Sizeof是求这个指针的的大小,strlen是求这个指针所指向的内容的大小。
对括号的要求不同:sizeof求数据类型所占内存大小时可以不加括号;求变量的长度时必须加括号,strlen必须加括号。

3、c语言分配内存的方式有哪些?c语言中常见的内存错误有哪些?内存管理中不同的数据放在那里?
分配方式有三种:
  1、从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
  2、在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  3、从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由程序员决定,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
常见的错误:
关于内存的一些知识已在内存分配中提及,现记录与分享常见的内存错误与对策。
类型 1:内存未分配成功,却使用了它。
方 法:在使用之前检查指针是否为NULL。
1)当指针p是函数的参数时,在函数入口处用语句assert(p!=NULL)进行断言检查。
2)当使用malloc或new来申请内存时,应该用if(p != NULL)进行防错检查。
类型 2:引用了尚未初始化的指针
原 因:内存的缺省初始值究竟是什么并没有统一的标准,在使用之前都进行初始化。
1)没有初始化的观念。
2)内存的缺省值是未定义,即垃圾值。
类型 3:越界操作内存
原 因:内存分配成功且初始了,但越界操作是不允许的。
类型 4:忘记释放内存,造成内存泄漏。
原 因:含有这种类型错误的函数,每被调用一次,就丢失一块内存。当内存充足时看不到这种错误带来的影响,当内存耗尽时系统提示:“内存耗尽”。因此,动态内存的申请与释放必须配对,程序中malloc与free的使用次数要相同。
类型 5:释放了内存却继续使用它
原 因:对应的情况有2种
1)返回了“栈内存的指针或引用”,因为堆栈中的变量在函数结束后自动销毁。
2)某块内存被free后,没有将指向该内存的指针设置为NULL,导致产生“野指针”。
内存管理:
.bass段:未初始化的全局变量
.data段:初始化过的全局变量
.rodata段:常量数据
.text段:代码和部分整数常量
stack:临时变量和函数参数
heap:程序员向系统申请的内存

4、什么是野指针?如何避免野指针?
野指针:指向不确定地址的指针变量。(即没有初始化) (随机指向一块内存的指针)
使用野指针易因内存泄露出现段错误。因为它随机指向的地址可能被分配了内存,不一定每次都产生段错误.
而造成内存泄露的原因有两个:
1.访问了没有权限的内存(平时我们正确使用指针的时候,系统应经将相应的内存分配给用户,但是如果指向没有分配的内存,系统会判定我们没有权限)
2.访问了已经释放了的内存。
如何解决野指针,养成一下编码习惯:
1,当一个指针没有指向时,置为NULL(空),
NULL宏:#define NULL(void * )0,0地址对应的空间不允许进行任何操作。
2,当往一个指针赋值的时候,一定要给指针分配空间,
int *p = malloc(100);返回首地址,int *p = malloc(sizeof(int)):提高代码的移植性。
3,当给一个指针分配空间,一定压迫检查是否分配成功。
if(p == NULL)
{
printf(“malloc error!\n”);
exit(1);
}
4,分配空间成功之后,要先初始化,memset(p,0,sizeof(int));
5,释放 free(p);
6,再次置空:p = NULL;
因为野指针主要是因为我们平时编程习惯造成的,因此我们只能避免野指针的出现,而不能杜绝。(请注意用词)我们在编程时,做到以下几点可以有效地避免野指针的出现:
第一,当一个指针没有指向时,我们一般默认指向NULL。(NULL代表内存的0地址,并且NULL是不允许做任何操作的)
第二,使用malloc分配内存。(在堆空间里分配内存)

difine MAX_SIZE 1024;

char ptr = (char ) maollc (sizeof (char) * MAX_SIZE);

请认真研究这样的表达式的优点,这个表达式在代码的维护性,扩展性都大大提高了。这方面是我们平时写代码时所应该提高的。
使用malloc也是有讲究的,我们应该依照下面的流程:
1.分配内存。(分配成功,返回内存的首地址;分配不成功,返回NULL)。
2.检查是否分配成功(若失败,则 exit(1) 退出程序)。
3.清空内存中的数据 (malloc分配的空间里可能存在垃圾值,因此我们需要清空,可以用到memset或bzero 函数)。
4.使用内存。
5.释放内存(free,这时ptr又变成野指针)。
6.写成NULL。

5、分别写出bool,int,float,指针类型的变量a与“零”的比较语句?
int:
if(n == 0) or if(n != 0)
float:
const float EPSINON = 0.00001;
if((n >= -EPSINON) && (n <= EPSINON))
bool:
if(flag) or if(!flag)
指针:
if (n == NULL) or if(n != NULL)

一、静态链表是数组,动态链表是指针;静态链表中指针表示的是数组下标

二、进程:一个程序运行时向系统申请分配的资源;线程:一个进程包含多个线程

线程是进程的一部分,使用线程有利于提高程序的执行效率,线程切换开销比进程切换开销低,同一个进程中的线程可以共享全局变量

三、TCP协议特点:可靠性、面向连接、可控制;TCP协议可以将原主机的字节无差错的传送到目的主机

TCP/IP协议(从下往上):网络接口层、网络层、传输层、应用层

OSI参考模型:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层
TCP/IP的三次握手:
第一次握手:建立连接时,客户端发送syn包(syn = j)到服务器,并进入SYN_SEND模式,等待服务器确认;

第二次握手:服务器收到syn包,必须确认客户的SYN(ack = j+1),同时自己也发送一个SYN包(syn = k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack = k+1),此时发送完毕,客户端和服务器进入EATABLISHED状态,完成三次握手。

TCP/IP的四次挥手:

某个应用进程首先调用close,我们称该端执行主动关闭。该端的TCP是发送一个FIN分节,表示数据发送完毕。

接收到这个FIN的对端执行被动关闭。这个FIN由TCP确认,他的接受也作为一个文件结束符EOF传递给接收端应用进程(放在已排队等候该应用进程接收的任何其他数据之后),因为FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。

一段时间后,接收到这个文件结束符的应用进程将调用close关闭他的套接字,这导致他的TCP也发送一个FIN。

接收这个最终FIN的原发送端TCP,即执行主动关闭的那一端确认这个FIN。

TCP/IP协议包含了哪些协议?

应用层:应用程序间沟通的层,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。
传输层:在此层中,它提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。
互连网络层:负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)。
网络接口层:对实际的网络媒体的管理,定义如何使用实际网络(如Ethernet、Serial Line等)来传送数据。
四、数组和指针的区别:
(1)指针是动态分配空间,通过malloc在堆上分配所需要的空间,分配的空间不一定连续,在使用完之后需要调用free()来释放分配空间。而数组是静态分配空间,在全局变量区或者栈上分配空间,分配的空间是连续的,局部变量在生命周期结束后自动释放,全局变量在程序结束完自动释放。
(2)访问效率:指针是通过地址间接访问,而数组是直接访问数值。因此指针的访问效率低,数组的访问效率高。
(3)安全性:指针使用不当会造成内存泄漏,数组使用不当会造成数组越界。
(4)函数参数:数组要用相应的指针当形参,而指针要用指针的指针来当形参。
(5)字节大小:指针的字节大小是固定的,由操作系统决定。数组的字节大小由数组大小和数组指向的类型共同来决定。
(6)标识符:指针名是变量,数组名是指针常量。所以指针p可以进行p++,而数组名不可以用于a++。
(7)对应的内存空间:指针保存的是地址,数组保存的是数值。

五、关键字const是什么含义?有什么作用?

const是将一个变量或常量添加到符号表里,编译程序时,略过编译这个变量,提升了效率。

指针可以修改const变量

六、const和#define相比有何优点?

首先阐述两者区别
就起作用的阶段而言: #define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用。
就起作用的方式而言: #define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误。
就存储方式而言:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份;const定义的只读变量在程序运行过程中只有一份备份。
从代码调试的方便程度而言: const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉了。
综上所述const有以下几个优点
(1)const常量有数据类型,编译器可对其类型进行安全检查
(2)const常量可进行调试
(3)const可节省空间,避免不必要的内存分配,提高效率

七、什么是预编译,何时需要预编译?

预编译又称为预处理 , 是做些代码文本的替换工作。
处理以# 开头的指令 , 比如拷贝 #include 包含的文件代码,#define 宏定义的替换 , 条件编译等,就是为编译做的预备工作的阶段。
主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。
C 编译系统在对程序进行通常的编译之前,首先进行预处理。
c 提供的预处理功能主要有以下三种:
1 )宏定义 
2 )文件包含 
3 )条件编译
何时需要预编译:
总是使用不经常改动的大型代码体。
程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个“预编译头”

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值