题目来自程序员面试宝典及我在面试笔试过程中遇到的一些疑难点
++与函数参数进栈
int array[]={1,2,3,4,5}
int *ptr=array;
*(ptr++)+=123;
printf("%d,%d",*ptr,*(++ptr));
结果为输入:3,3
类型转换与地址
float i=1.0f
cout<<(int) i<<endl; //output 1
cout<<&i<<endl; //output address of i
cout<<(int&)i<<endl; //output first 32 bit value of float i's memory address 1065353216
cout<<( (int)a == (int&)a )<<endl; //output false
类型转换与指针
unsigned int a=0xfffffff7;
unsigned char i=(unsigned char) a; //i是a被截断的前8位
char *p=(char*)&a; //p是指向&a类型的指针
printf("%08x,%08x/n",i,*p); //cout 000000f7,fffffff7
int main(int argc, char* argv[]) { int a[2][3]={0,8,2,3,4,5}; int** p; p = (int**)a; cout<<*(p+1)<<endl; //0x00000008 return 0; }
假设数组a的地址为x,则p+1=x+sizeof(int*)=x+4,(p+1)指向8,对(p+1)解引用取到8,但p为指向指针的指针,因此输出为地址形式的0x00000008。
与或非应用
使用一个表达判断x是否为2的幂:(x>0)&&!(x&(x-1))
x is odd return (x&1)==1
求一个数的2进制表示中1的个数 m=$1;while(m) {cnt++;m=m&(m-1);} return cnt;
交换a b:a=a^b;b=a^b;a=a^b;
a = a+b-(b=a)
不使用判断的方法求两个数中的大数:return (a+b)/2+abs(a-b)/2
sizeof
struct {long a;char b;}A; //sizeof(A)=8;
char *p="0123456789"; //sizeof(p) =4
char str[]="0123456"; //sizeof(str)=7
char q[]="a/n"; //sizeof(q)=3
bool val; //sizeof(val)=1
//sizeof(float)=4
//sizeof(double)=8
int fun(){...} //sizeof(fun)=4
//sizeof(string)=4
string array[4] //sizeof(array)=16
class A {}; //sizeof(A)=1; see c++ obj modeule
内联比宏的优势是:前者要做类型检查。
指针和引用
指针可以为空而引用必须在声明时初始化指向某个对象,因此引用在使用时不需要合法性检查。
引用一旦初始化就不能修改,注意的是引用对象的内容可以修改。因此加const限定时,只有一种情况,即const 引用,它是指指向const对
象的引用。int & const p =a 就是 int& p=a; 而const int& p=a表示const int a.
而指针加const限定时就有两种表示意义,int const *p=&a 表示指针指向的内容不能修改,而int* const p=&a就表示指针本身不能再指向其他对象。复合起来就有 const int* const p =&a同时限定了两者都不能被修改。
形参与指针
int a[10];
fun(a){
cout<<sizeof(a); //4
}
void getmem(char* p,int n)
{
p=(char*)malloc(n); //形参p无法修改str的值
//return p;
}
int main()
{
char *str=NULL;
getmem(str,10); //str=NULL
//str=getmem(10)
strcpy(str,"hello");
free(str);
}
字符串常量指针
const char* fun()
{
char str[]="hello world"; //wrong str in stack
//char* str="hello world"; //right str in global store zone
return str;
}
函数、指针与数组
int* a[10] //数组a[10],每个元素为int型指针
int** a[3][4] //sizeof(a)=48 a is array a[i][j] is a ptr of ptr
int (*a)[10] //a为指向一维数组的指针,数组元素为int型
int (**a)[10] //a为二级指针,它指向一个指向一给数组的指针
int* (*a)[10] //a为指向一维数组的指针,数组元素为int*型
int (*a[10])() //a是一维数组,每个元素都是一个返回值为int的函数指针,函数没有形参
int *((*a)[10]) //同int* (*a)[10]
int (*a)(int) //a是函数指针,指向函数带一个int形参
int (*(*f)(int,int))(int) //f为函数指针,指向一个函数fun(int,int),而fun返回值也为函数指针,且类型为int fun(int);
malloc/free 与 new/delete
前者是库函数,只申请空间,后者是操作符对于类类型的操作,后者会进行构造与析构操作。
指针与句柄
指针指向物理地址?
句柄是指向一个对象的地址,而这地址会由于对象对内存管理操作进行移位而改变。
智能指针
智能可以在离开作用后自动析构,并在异常抛出时进行对象的析构,从而防止内存泄露。
类的成员变量
类的静态成员变量可以是公有也可是私有,类的静态成员只能在类的外部初始化。T class::s_mem=0.
如果没有初始化,则不能通过编译。类的静态成员变量及函数只具有文件作用域。
类的常量成员,只能在构造函数的初始化列表里初始化。
重载与重写
重载是指同一个函数具有不同的参数或限定词,重载是由编译器静态完成的,不需要动态完成,与面向对象无关。
重写是指基类的虚函数在派生类中被重新实现的过程。函数的参数与返回值没有改变。重写通过虚函数实现了多态。
访问控制
public private protected
作用于类的成员时,public成员表示它对类外部可见,private成员表示成员对类外部不可见,只能在类的内部使用,而且对子类也不可见,
而protected成员对类外部不可见,但对子类可见。
继承体系中修饰类时,public继承表示子类可以访问父类中的非private成员, 也可以通过子类对象访问基类的非private成员。private继承
表示子类的对象不能访问基类的所有成员,但在子类中可以访问基类的非私有成员。protected继承与私有继承类似,在子类中可以访问基类
的非私有成员,子类对象可以访问基类的非private成员,这些成员继承后成为protected,且不能再子类的子类访问。
private继承和protect继承只在讨论上有用,并不见与软件设计。
如果在继承中没有使用关键字,则默认是私有继承。
位运算与嵌入式
int main()
{
int a=5; double b=5.01;
printf("%f/n",a); //xxxxx
printf("%d/n",b); //1889785610
}
对于printf函数而言,输出%f时,参数应该为double类型,如果参数为非double的,则会从栈中读出8B解释。而对于%d,如果参数为double
型的,则只解释前4位部分。
设置某位的方法
# define BITN (0x1<<N)
void set_bitn(int *p)
{
*p=*p|BITN;
}
void clr_bitn(int* p)
{
*p&=~BITN
}
中断服务程序(ISR)要求没有参数,没有返回值,不进行浮点计算,不使用printf这类存在重入问题的函数。
直接内存地址访问
int *ptr;
ptr=(int*)0x679a;
*ptr=1;
malloc(0)将返回一个非NULL的有效指针。
判断CPU大小端的方法
int x=1;
if(*(char *)&x==1)
//little endian
else
//big endian
位域的赋值
#include "stdio.h" #include "stdlib.h" #include <bitset> #include <iostream> using namespace std; union V { struct X { unsigned char s1:2; unsigned char s2:3; unsigned char s3:3; } x; unsigned char c; } v; int main() { v.c = 100; printf("%d %d %d/n", v.x.s1,v.x.s2,v.x.s3); bitset<8> uchar(100); cout<<uchar<<endl; }
结果输出为01100100
0 1 3
就是说,单个字节中的位域划分是从低位到高位的,这样,s1使用低两位的00,s2使用中间两位001,而s3使用高两位011
变量内存分配
进程在内存中分为三部分:代码段、静态数据区、动态数据区。
动态数据区一般指堆栈,存放自动变量等,静态数据区在另一块区域,存放着全局变量与静态变量。
内存分配函数new
对于基本数据类型的内存分配,操作系统已经记录了分配内存空间的大小,而对于带有析构函数的类类型的内存分配,还要记录调用析构函数的次数,因此有额外的4字节用来记录数组的大小。此外在Windows系统中,new一块内存的后面还有4个字节在后面作为内存分配边界。
进程间通信的方法:信号、信号量、消息队列、共享内存
RAID分类
0 多个磁盘组成一块大硬盘,并行操作,无冗余特性。磁盘利用率为n
1 主盘与镜像盘,同时操作。磁盘利用率最低1/2
3 使用一个单独的磁盘存放其他磁盘的校验数据,当其他磁盘出错时,借助校验数据恢复,但校验盘出错则其他盘数据都无法使用,磁盘利用率为n-1。
5 校验磁盘分散在各块磁盘上。安全性最高,磁盘利用率为n-1
0-1 并行磁盘操作,同时一半磁盘做为镜像。