Linux C 面试题汇总(总有你不知道的知识点)

时间:2024-11-14 09:24:18

linux C


1.用变量a给出下面的定义.

a) 一个整型数
b) 一个指向整型数的指针
c) 一个指向指针的的指针,它指向的指针是指向一个整型数
d) 一个有10个整型数的数组
e) 一个有10个指针的数组,该指针是指向一个整型数的
f) 一个指向有10个整型数数组的指针
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数

答案是:
a) int a;
b) int *a;
c) int **a;
d) int a[10];
e) int *a[10];
f) int (*a)[10];
g) int (*a)(int);
h) int (*a[10])(int);

2.写一个“标准”宏MIN,这个输入两个参数并返回较小的一个。
#define Min(a,b) ( ((a)>=(b))?(b):(a))

3.嵌入式经常用到死循环,你怎么样用C来编写死循环呢?
我的首选就是:
while(1){},一些有经验的程序员喜欢用for( ; ; ){} ,还有一种是写汇编语言的程序员常用的写法:
Loop:

goto Loop;

4.引用和指针的区别?

答:1.引用必须要初始化,指针可以不用初始化;2.不存在指向空值的引用,但是存在指向空值的指针;3.引用初始化后不能改变,但存在指向空值的指针;4.引用是变量的别名,本身不单独分配自己的内存空间,而指针有自己的内存空间;

5.关键字static的作用是什么?

在c语言中static有三个明显的作用:
1)static在修饰局部变量的时候,其使得局部变量的生命周期发生改变,使得其放在data段,直到程序运行结束才结束。
2)static在修饰全局变量的时候,作用是改变其作用域,使得全局变量只能在定义的文件中使用。
3)static在修饰函数的时候,同样也是只能时函数只能在当前的文件中使用。

6.请填写bool , float, 指针变量 与“零值”比较的if语句。

1)if ( flag ) if ( !flag )
2)const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON)
3)if (p == NULL) if (p != NULL)

7.用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)

#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

c 作用

告诉编译器该段代码以C语言进行编译。

9.头文件中的 ifndef/define/endif 干什么用?

预处理,防止头文件被重复使用。

……while和while……do有什么区别?

前一个循环一遍再判断,后一个判断以后再循环。

语言链表与数组的区别?

链表跟数组都属于一种数据结构。可以分为两个点来看:
从逻辑结构来看
1)数组必须事先定义固定的长度,不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费;数组可以根据下标直接存取。
2) 链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。链表必须根据next指针找到下一个元素。
从内存存储来看
1)(静态)数组从栈中分配空间, 对于程序员方便快速,但是*度小
2)链表从堆中分配空间, *度大但是申请管理比较麻烦

12.队列和栈有什么区别?

队列先进先出,栈后进先出。

有什么用途?

1)可以定义const常量。
2)修饰函数的返回值和形参。

14 . #include <…h> 和#include “…h” 有什么区别?

对于#include <…h> ,编译器从标准库路径开始搜索.h
对于#include “…h” ,编译器从用户的工作路径开始搜索.h

15.请写出下列代码的输出内容

#include <stdio.h>
int main()
{
	int a,b,c,d;
	a=10;
	b=a++;
	c=++a;
	d=10*a++;
	printf("b,c ,d:%d,%d,%d",b,c,d );
	return 0;
}

10,12,120

16.关键字volatile有什么含意?

提示编译对象的值可能在编译器未检测到的情况改变。

18.不用标准库函数,写一个字符串拷贝函数strcpy。

1)不考虑重叠问题:

	char* strcpy(char* dst, const char* src)
	{
			assert((dst != NULL )&&(src != NULL));
			char * ret = dst;
			while ((*dst ++ = *src ++) != '\0');
			return ret;
	}

2)strcpy考虑重叠与不重叠问题:

	char *strcpy(char *dst, const char *src)
	{
			assert((dst != NULL) && (src != NULL));
			char *ret = dst;
			int size = strlen(src) + 1;
			if (dst > src  || dst < src + szie)
			{
					dst = dst + size -1;
					  src = src + size -1;
					while (size -- )
					{
						*dst ++ = *src ++;
					}
			}
			else
			{
					while (size --)
					{
						*dst ++ = *src ++;
					}
				}
			
	}

19.在下面语句中,fd的具体含义?

void ((*fp)(int))[10];
1).()的优先级最高所以先看(*fp),fp是个指针;
2). 假设 func=(*fp); func(void ) 是个指针函数返回值是个指针,参数是(void),所以fp是指向指针函数的函数指针;

20.有一个16位整数,每4位为一个数,写函数求他们的和。

解析:
整数1101010110110111
和 1101+0101+1011+0111

char SumOfQuaters(unsigned short n)
{
		char c = 0;
		int i = 4;
		do
		{
				c += n & 15;
				n = n >>4;
		}while(--i);
return c;
}

21.用c语言写一个冒泡排序函数。

/*
冒泡排序: 比较相邻的元素,如果第一个比第二个大,交换他们两个;
*/

void BubbleSort(int *arr,int size)
{
	int i,j,temp;
	for (i = 0; i < size - 1; i++)
	{
		for (j = 0; j <size -i -1; j++)
		{
			if (arr[j] > arr[j+1])
			{
				temp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] =temp;
			}
		}
	}
}

22.堆栈溢出一般是由什么原因导致的?

1).内存泄露,比如某一数组原先已定义好大小,但是在后续操作中存放的个数超出这一既定长度,会导致堆栈溢出。
2).由于程序员动态申请的内存块使用后未立即释放,导致内存区不够用,也会导致堆栈溢出 。

23.请用指针实现一个函数,函数的功能是交换2个整数,并要求写出如何调用这个函数。

#include ""
void fun(int  *x,int  *y)
{
	int t;
	t=*x;
	*x=*y;
	*y=t;		

}
int main(int argc, char **argv)
{
	int a,b;
	printf("请输入a和b:");
	scanf("%d%d",&a,&b);
	fun(&a,&b);
	printf("\n = %d ,b = %d\n",a,b);
	return 0;
}

语言程序运行的时候内存分布?

1).堆区:在动态申请内存的时候,在堆里开辟内存。
2).栈区: 主要存放局部变量(在函数内部,或复合语句内部定义的变量)。
3).静态/全局区
3.1):未初始化的静态全局区 (bss区):
静态变量(定义的时候,前面加 static 修饰),或全局变量 ,没有初始化的。
3.2):初始化的静态全局区 全局变量、静态变量,赋过初值的,存放在此区 。

4.代码区:存放咱们的程序代码
5.文字常量区 :存放常量的。

25.写一个链表逆序?

struct link
{
int data;
link* next;

};

link* reverse(link *head)
{
link *p,*q,*r;
if (head ==NULL ||head ->next ==NULL)
return head;
p = head;
q = head->next;
while(q != NULL)
{
r = q ->next;
q->next = p;
p = q;
q = r;
}
head ->next =NULL;
head = p;
return head;
}

26.怎么判断链表中是否有环?

/*
分析:用两个指针来遍历这个单向链表,第
一个指针p1,每次走一步;第二个指针p2,每次走两
步;当p2 指针追上p1的时候,就表明链表当中有环
路了。
 */
struct link
{
	int data;
	link* next;

};

bool Isloop(link* head)
{
	link *p1;
	link *p2;
	p1 = head;
	p2 = head;
	if (head == NULL   ||  head -> next == NULL)
		return false;
	do
	{
		p1 = p1 ->next;
		p2 = p2 ->next->next;
	}while(p2&&p2->next&&p1!=p2);
	if (p1 == p2)
	{
		return true;
	}
	else
	{
		return false;
	}
}

27.什么是守护进程?

守护进程:是指在UNIX或其他多任务操作系统中在后台执行的电脑程序,并不会接受电脑用户的直接操控。此类程序会被以进程的形式初始化。守护进程程序的名称通常以字母“d”结尾:例如,syslogd就是指管理系统日志的守护进程。通常,守护进程没有任何存在的父进程(即PPID=1),且在UNIX系统进程层级中直接位于init之下。守护进程程序通常通过如下方法使自己成为守护进程:对一个子进程调用fork,然后使其父进程立即终止,使得这个子进程能在init下运行。这种方法通常被称为“脱壳”

28.描述一下进程与线程的区别?

1)调度:线程作为调度和分配的基本单元,进程作为拥有资源的基本单位;
2)并发性:不仅进程可以并发执行,同一进程内的线程也可以并发执行。
3)拥有资源:进程是拥有资源的基本独立单元,线程不拥有资源,但可以访问进程内的资源;
4)在创建或撤销线程时,由于系统都要为之分配和回收内存资源,导致系统的开销明显大于创建或撤销线程时的开销。

29.进程之间通信有哪些?

1) 管道(pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有血缘关系的进程间使用。进程的血缘关系通常指父子进程关系。
2)有名管道(named pipe):有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间通信。
3)信号量(semophore):信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它通常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
4)消息队列(message queue):消息队列是由消息组成的链表,存放在内核中 并由消息队列标识符标识。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
5)信号处理机制(signal):信号是一种比较复杂的通信方式,用于通知接收进程某一事件已经发生。
6)共享内存(shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问,共享内存是最快的IPC方式,它是针对其他进程间的通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信。
7)套接字(socket):套接字也是一种进程间的通信机制,与其他通信机制不同的是它可以用于不同及其间的进程通信。

()、_exit()和return()的区别?

exit():结束当前的进程,并且会刷新缓存区,关闭没有关闭的文件等。
_exit():结束当前的进程,不对缓存区刷新。
return:
1)在main函数中会结束当前进程。
2)在子函数中,会返回调用当前函数的调用位置,进程从下个C语句开始执行。

与vfork区别?

fork和vfork都用于创建子进程。但是vfork创建子进程后,父进程阻塞,直到子进程调用exit()或者excle()。
对于内核中过程fork通过调用clone函数,然后clone函数调用do_fork()。do_fork()中调用copy_process()函数先复制task_struct结构体,然后复制其他关于内存,文件,寄存器等信息。fork采用写时拷贝技术,因此子进程和父进程的页表指向相同的页框。但是vfork不需要拷贝页表,因为父进程会一直阻塞,直接使用父进程页表。

32.路由器工作在哪一层(B)

A:链路层 B:网络层 C:传输层 D:应用层

33.网络192.168.220.0/24 定向广播地址是(192.168.220.255),受限的广播地址为(255.255.255.255);

34.简述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),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手

35.简述一下TCP 、UDP 的区别?

TCP:TCP 则提供面向连接的服务。在传输数据前必须先建立连接,数据传输完毕后要释放连接。
UDP:UDP协议是面向无连接的用户数据报协议,在传输数据之前不需要先建立连接。远地主机的运输层收到UDP报文后,不需要给出任何确认。

区别:
是否面向连接:Tcp面向连接 ,udp是面向无连接
是否可靠: Tcp可靠 ,udp不可靠
应用场合: Tcp应用于传输大量数据 ,udp用于传输少量数据
速度: Tcp的速度慢 ,udp的速度快
是否能够广播:tcp不能 ,udp能广播
tcp是基于连接的,速度慢,有校验等,所以传送相同的数据,要比UDP发送的包多很多。

36.请说明信号量与互斥量的区别?

答:互斥量用于线程的互斥,信号量用于线程的同步。其根本区别在于互斥和同步之间的区别。
互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。
同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

什么情况下可读?

1)socket接收缓冲区中已经接收的数据的字节数大于等于socket接收缓冲区低潮限度的当前值;对这样的socket的读操作不会阻塞,并返回一个大于0的值(准备好读入的数据的字节数)。
2)连接的读一半关闭(即:接收到对方发过来的FIN的TCP连接),并且返回0;
3)socket收到了对方的connect请求已经完成的连接数为非0.这样的soocket处于可读状态。
4)异常的情况下socket的读操作将不会阻塞,并且返回一个错误(-1)。

编程,如果client断电了,服务器如何快速知道?

使用定时器(适合有数据流动的情况);使用socket选项SO_KEEPALIVE(适合没有数据流动的情况);

1)、自己编写心跳包程序,简单的说就是自己的程序加入一条线程,定时向对端发送数据包,查看是否有ACK,根据ACK的返回情况来管理连接。此方法比较通用,一般使用业务层心跳处理,灵活可控,但改变了现有的协议;
2)、使用TCP的keepalive机制,UNIX网络编程不推荐使用SO_KEEPALIVE来做心)跳检测。

与http的区别?如何实现加密传输?

1)https就是在http与传输层之间加上了一个SSL。
2)对称加密与非对称加密。

在这里插入图片描述

扫二维码关注微信公众号,获取技术干货