面试问题小结
1.使用宏定义判断两个数的大小
答:
#define Max(a,b) (a>b)?a:b
2.函数指针和指针函数的区别
答:
函数指针顾名思义就是指向函数的指针,
也就是就是指向代码段中函数入口地址的指针。
函数指针的定义可以类比变量的指针来看,例如:
int *count;
即定义了一个指向int类型数据的指针。
函数指针定义方式例如:
int (*p)(int, int);
即定义了一个指向返回值为int类型数据,传递参数为两个int类型数据的函数的指针。
函数指针的初始化方式为:
函数指针变量 = 函数名;
int max(int a, int b)
{
return a > b ? a : b;
}
p = max; //函数指针初始化
函数指针的调用方式为:
int ret = p(10, 15);
指针函数顾名思义本质上是一个函数,只是它的返回值是一个指针。
其定义方式为:
int * func_sum(int n)
即定义了一个返回值为指向int类型数据的指针,传递参数为int类型数据的函数。
在使用指针函数的时候,一定要避免出现返回局部变量指针的情况。
原因在于,一般的局部变量是存放于栈区的,当函数结束,栈区的变量就会释放掉,如果我们在函数内部定义一个变量,在使用一个指针去指向这个变量,当函数调用结束时,这个变量的空间就已经被释放,这时就算返回了该地址的指针,也不一定会得到正确的值。
可以使用了static去修饰变量,那么该变量就变成了静态变量。而静态变量是存放在数据段的,它的生命周期存在于整个程序运行期间,只要程序没有结束,该变量就会一直存在,所以该指针就能一直访问到该变量。
3.static关键词的作用
答:
刚好承接上一个问题,就重新复习一下变量的作用域和生命周期和存储区域的相关知识吧。
首先从变量的定义位置出发,变量的类型可以分为:局部变量和全局变量
局部变量就是在函数内部定义的变量,全局变量就是在函数外部定义的变量。
可以列一个表格来看:
特征 | 局部变量 | 全局变量 |
---|---|---|
作用域 | 只在本函数内有效 | 在整个文件有效 |
生命周期 | 本函数运行期间 | 程序运行期间 |
存储区域 | 动态存储区 | 静态存储区 |
这里要先说明,数据的存储空间,可以分为代码区和数据区,而数据区又分为静态存储区和动态存储区。
以上都为不加任何关键词修饰的变量。
其中局部变量,如果加上static关键词修饰后就做静态局部变量,此时该变量的作用域不变,生命周期变为程序运行期间,存储区域由动态存储区变为静态存储区。
其中全局变量,如果加上static关键词修饰后就做静态全局变量,此时该变量的它的特征和上表中一致。
如果全局变量被extern关键词修饰,就做外部全局变量,该关键词直接扩大了该变量的作用域,由文件内有效变为外部文件可用。
除了变量,也可以对函数进行static和extern声明,又分别叫做静态函数(内部函数)和外部函数,也是作用域的差别,静态函数只能在定义该函数的文件内使用,外部函数可以通过包含声明了该函数的头文件在其他文件内调用。
4.用户空间和系统空间的区别
答:
这是Linux系统的相关知识,网上搜的哈
用户空间就是用户进程所在的内存区域,相对的,系统空间就是操作系统占据的内存区域。
当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态(或简称为内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)。即此时处理器在特权级最低的(3级)用户代码中运行。
5.volatile关键词的作用
答:
简单地说就是防止编译器对代码进行优化。
一般说来,volatile用在如下的几个地方:
a.中断服务程序中修改的供其它程序检测的变量需要加volatile;
b.多任务环境下各任务间共享的标志应该加volatile;
c.存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义
6.堆和栈的区别
答:
堆和栈都是动态存储区的组成部分,温习一下,静态存储区存储的数据都包括:全局变量和static变量(包括静态局部变量和静态全局变量)。
栈:保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁。其特点是效率高,但空间大小有限。
堆:由malloc系列函数或new操作符分配的内存。其生命周期由free或delete决定。
7.数组和链表
答:
数组:在内存中,是一块连续的内存区域;
链表:是由不连续的内存空间组成;
项目 | 数组 | 链表 |
---|---|---|
随机访问性 | 强 | 差 |
查找速度 | 强 | 差 |
插入和删除效率 | 差 | 强 |
内存要求宽松度 | 差 | 强 |
大小扩展难度 | 大 | 小 |
8.位段和联合体
答:
贴一段网上代码吧
typedef struct{
uchar bit0:1;
uchar bit1:1;
uchar bit2:1;
uchar bit3:1;
uchar bit4:1;
uchar bit5:1;
uchar bit6:1;
uchar bit7:1;
}bits;
typedef union{
uchar data_char;
bits data_bits;
}utype;
uchar *addr = (uchar*)0x10;
utype value;
value.data_char = *addr;
如果要查看某个位比如第3位
直接看 value.data_bits.bit3就可以了。
如果要设置某个位,比如设置第0位为1,第7位为0
value.data_bits.bit0 = 1;
value.data_bits.bit7 = 0;
*addr = value.data_char;
9.进程和线程的区别
答:
每一个程序相当于一个进程,而一个进程之中可以有多个线程 。
以下是网上找的
根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位
在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)
内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。
包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程