《程序员面试笔试宝典》学习笔记(四)程序设计基础

时间:2021-07-31 19:18:02

六、结构体与类

1、C语言中struct 和union 的区别是什么

struct (结构体)与union (联合体)是C语言中两种不同的复合型数据结构。

区别:
1)联合体中所有成员共用一块地址空间,联合体的长度为其最长的成员的长度;而结构体中所有成员占用空间是累加的(需要考虑字节对齐)

2)对于联合体的不同成员赋值,将会对它的其它成员重写,原来的值不复存在;而对结构体不同成员赋值互不影响

2、C 和C++ 中struct 的区别是什么

C语言中struct 与C++ 中的struct 区别表现在以下三个方面:

1)C语言的struct 不能有函数成员,而C++ 的struct 可以有;
2)C语言的struct 中数据成员没有private、public和protected 访问权限的设定,而C++的struct 的成员有访问权限的设定
3)C语言的struct 是没有继承关系的,而C++ 的struct 却有丰富的继承关系。

3、C++ 中struct 与class的区别是什么

两点区别:
1)默认继承权限,class 继承默认是private 继承,而struct 继承默认是 public 继承
2)class 还用于定义模板参数,就像typename, 但关键字struct 不用于定义模板参数。

c++保留struct的原因:
1)保证与c语言的向下兼容
2)对struct的定义的扩展可以使c代码轻易地移植到c++中来。

七、位操作

1、一些结构声明中的冒号和数字是什么意思

C语言的结构体可以实现位段,它的定义形式是在一个定义的结构体成员后面加上冒号,然后是该成员所占的位数。位段的结构体必须是int 或者unsigned int 类型,不能是其他类型。

定义位段的长度不能大于存储单元的长度,存储单元是指该位段的类型大小,如int a:2;大小最大为4.

2、最有效的计算 2 乘以 8 的方法是什么

移位操作:2<<3

快速计算一个整数的7倍:(x<<3)-x

3、如何实现位操作求两个数的平均值

(x & y) + ((x^y)>> 1

1)(x & y)表示的是取出 x 与 y 二进制位数中都为 1 的所有位,因为相同,所以可以直接相加。
2)(x^y)表示的是x 与 y 中有一个为 1 的所有位,右移一位相当为执行除以2运算。然后跟前面相加就可以表示两者平均数

扩展:如何利用位运算计算数的绝对值?

y=x>>31;
(x^y)-y;即是x的绝对值

5、如何求解整型数的二进制表示中1 的个数

int countx=0;
while(x)
{
countx++;
x=x&(x-1); //&=的作用是重新去掉已经计算过的1,并将该值重新设置给x
}
例如x=8:
1000
& 0111
得到0之后不再循环,计算出1的个数为1

6、不能用sizeof,如何判断操作系统是16位还是32 位的

打印最大值,或者对0取反。

机器位数不同,表示数字的最大值也不同,例如打印65536这个数,在16位下回越界,导致打印不对,而32位下正常输出。

7、嵌入式编程中,什么是大端?什么是小端

大端模式:低地址存放高字节,高地址存放低字节
小端模式:低地址存放低字节,高地址存放高字节

在C语言中,不同于结构体,共用体(联合体)中的几种不同类型的变量存放在同一段内存单元中。利用这一特点,可以判断大小端模式。

#include "stdio.h"
int main()
{
union w
{
int a; //4 bytes
char b; //1 byte
} c;
c.a=1;
if (c.b==1)
printf("It is Little_endian!\n");
else
printf("It is Big_endian!\n");
return 1;
}

1)在c中,联合体(共用体)的数据成员都是从低地址开始存放。
2)若是小端模式,由低地址到高地址c.a存放为0x01 00 00 00,c.b被赋值为0x01;

  ————————————————————————————

地址 0x00000000 0x00000001 0x00000002 0x00000003

c.a 01 00 00 00

c.b 01 00

————————————————————————————

3)若是大端模式,由低地址到高地址c.a存放为0x00 00 00 01,c.b被赋值为0x0;

  ————————————————————————————

地址 0x00000000 0x00000001 0x00000002 0x00000003

c.a 00 00 00 01

c.b 00 00

————————————————————————————

8、考虑n 位二进制数,有多少个数不存在两个相邻的1

利用斐波拉契数列。

设所求结果为a(n),对于第n位的值,分为0或者1两种情况:
1)第n位为0,则有a(n-1)个数
2)第n位为1,则要满足没有两个相邻的1,第n-1位为0,有a(n-2)种。

因此a(n)=a(n-1)+a(n-2),满足斐波那契数列。

八、函数

1、怎样写一个接受可变参数的函数

C语言中支持函数调用的参数为变参形式,如printf()函数。

在C/C++中,为了通知编译器函数的参数个数和类型可变(即是不定的、未知的),就必须以三个点结束该函数的声明。

// printf函数的声明     
int printf(const char * _Format, ...);
//scanf函数声明
int scanf(const char * _Format, ...);
//自定义变长参数函数func的声明
int func(int a,int b,...);

//下面这种声明是非法的
int func(...);//错误
int func(...,int a);//错误

对于变长参数函数,结合一定的条件,我们可以根据最后一个指定参数获取之后的省略参数内容。如,对于函数func,我们知道了参数b的地址及类型,就可知道第一个可变参数的栈地址(如果有的话),如果知道第一个可变参数的类型,就可知道第一个可变参数的内容和第二个可变参数的地址(如果有的话)。以此类推,可以实现对可变参数函数的所有参数的访问。

2、函数指针和指针函数有什么区别

指针函数是带指针的函数,本质上是一个函数,函数的返回类型为某一类型的指针。其形式如下:

类型标识符   *函数名 (参数列表)

例如,int *f(x,y),它的意思是声明一个函数f(x,y),该函数返回类型为int 型指针。

函数指针式指向函数的指针变量,即本质上是一个指针变量,表示的是一个指针,它指向的是一个函数。其形式如下:

类型标识符   (*函数名)(参数)

例如:int (*pf)(int x),它的意思是声明一个函数指针,而pf = func 则是将func 函数的首地址赋值给指针。

1)数组指针/指针数组

数组指针就是指向数组的指针,它表示的是一个指针,它指向的是一个数组,它的重点是指针。

指针数组就是指针的数组,表示的是一个数组,它包含的元素是指针,它的重点是数组。

2)函数模板/模板函数

函数模板是对一批模样相同的函数的说明描述,它不是某一个具体的函数;

而模板函数则是将函数模板内的“数据类型参数”具体化后得到的重载函数(就是由模板而来的函数)

简单地说,函数模板是抽象的,而模板函数是具体的。

3、C++ 函数传递参数的方式有哪些

有值传递、指针传递、传引用、全局变量传递。

4、重载和覆盖有什么区别

重载是指函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数。

覆盖是指派生类中存在重新定义基类的函数,其函数名、参数列、返回值类型必须同父类中的相对应被覆盖的函数严格一致,覆盖函数和被覆盖函数只有函数体不同。当派生类对象调用子类中该同名函数时,会自动调用覆盖版本,和多态真正相关!!!

7、C++ 中函数调用有哪几种方式?

编译器一般使用堆栈来实现函数调用。堆栈主要用来存放每个函数的参数、局部变量等信息。

当一个函数被调用时,进程内核对象为其在进程的地址空间的堆栈部分分配一定栈内存给函数使用,函数堆栈功能如下:
1)在进入函数之前,保存返回地址和环境变量
2)在进入函数之后,保存实参或实参复制、局部变量

在参数传递中,有两个重要的问题必须要明确说明:
1)当参数个数多于一个时,按照什么顺序把参数压入堆栈;
2)函数调用后,由谁来把堆栈恢复原状。

在高级语言中,就是通过函数的调用方式来说明这两个问题的。常见的调用方式有:
1)stdcall
2)cdecl
3)fastcall
4)thiscall
5)naked call

8、什么是可重入函数?C语言中如何写可重入函数

可重入函数是指能够被多个线程“同时”调用的函数,并且能够保证函数结果正确性的函数。

在C语言中编写可重入函数时,尽量不要使用全局变量或静态变量,如果使用了全局变量或静态变量,就需要特别注意对这类变量访问的互斥。一般采用以下几种措施来保证函数的可重入性:信号量机制、关调度机制、关中断机制等方式。

注意:不要调用不可重入函数,当调用了不可重入的函数时,会使该函数也变为不可重入函数。一般驱动函数都是不可重入函数,因此在编写驱动程序时一定要注意重入的问题。