程序员面试宝典笔记

笔试就是考试,所以以做题和总结来查缺补漏;

1.i++与++i

#  a=i++----> a=i ;  i=i+1;

#  !i++--------->  !i ;  i=i+1;

#  a=++i------>i=i+1 ; a=i;

#  !++i-------->i=i+1 ;  !i;

对于含有i++的式子,都是先参与运算,再自增;

2.函数参数压栈顺序:从右到左  or 从左到右

3.浮点数和整数在内存中的存储方式不同

float a=125.5的存储方式:

125.5的二进制表示为1111101.1,规定尾数的整数部分恒为1,则表示为1.1111011*2^6;

阶码=6+127=133表示为10000101;

尾数将整数部分1去掉,为1111011;

符号位    阶码    尾数

    1           8         23      共32位

125.5在内存中存储方式:0 | 10000101 |111 1011 0000 0000 0000 0000

float a = 1.0f;
cout << (int)a << endl;  //强制截取
cout << (int&)a << endl;  //float类型(IEEE)的存储方式;int类型的解读
输出结果:
1
1065353216

浮点数0.0f的存储比较特殊
+0.0f  ----> 0 00000000 00000000000000000000000
-0.0f  ----> 1 00000000 00000000000000000000000

4.malloc/free和new/delete的区别:

malloc/free是c/c++语言的标准库函数

new/delete是c++语言的运算符

非内部数据类型在创建时要自动执行构造函数,消亡时自动执行析构函数,malloc/free无法满足;

Obj* obj = (Obj*)malloc(sizeof(Obj));

free(obj);

malloc()函数只是分配一块内存,而不是生成一个对象,它返回了一个void*类型指针;没有对指针对象进行初始化操作;

free被调用说明编译器释放这块内存,意思是,可以将这块内存分配给其他数据使用;虽然该指针仍然指向这块内存,但是一定不要再使用,可以令*obj=0改为空指针

Obj* obj=new Obj; //默认初始化

Obj* obj=new Obj();//值初始化

delete obj;

new函数实现了两步,第一步调用malloc分配一块内存;第二步是对指针对象进行初始化操作

new完成初始化操作时,可以采用不同的方式

delete首先完成free功能释放内存,然后调用析构函数

常见问题:#判断内存分配是否成功   #指针未初始化,而使用了空指针


5.

string a="1233";
cout<<a<<endl;     //如果包含的头文件是#include<string>
cout<<a.c_str<<endl;    #如果包含的头文件是#include<string.h>

6.隐式类型提升、截取、转换

#类型总是提升为较宽的类型  

#小于整型的类型在算术表达式计算之前会提升为整型

char char_a='a';
char char_d='b';
int int_b=3;
double dou_c=2.1;
 
char_a+int_b; //'a'被提升为int类型:97
char_a+char_d;  //提升为int型,在运算
int_b+dou_c ;  //3被提升为double类型:3.0
 
int* pt=0;  //0被转换为int*类型的空指针
 
int_b=dou_c;  //2.1被截取为int型:2


7.int类型转为(void*)类型

(void *)是一个指针类型;空指针。既然是指针,他们它就是一个地址,在内存中,地址就可以用int来表示嘛;
参数传递中常常用到(void *)的,尤其多线程。
你可以把任何的结构体的地址转为(void *)类型来传递,用到的时候再把它转回来。(好处要到你用到的时候才能体会到了)

 

8.关于extern "C"

//标准头文件格式
#ifndef _INCvxNANA
#define _INCvxNANA    //宏定义防止该头文件被重复引用
#ifdef _cplusplus
extern "C"{
#endif
int a;
double func(int t,int s);
#ifdef _cplusplus
}
#endif
#endif

被extern "C“ 修饰的变量和函数是按照C语言方式编译和连接的,所以extern"C"声明的目的是为了实现C/C++语言的混合编程。

1.在C++中引用C语言中的函数和变量时,就要在其函数前加上extern “C",说明该函数是按照C语言方式编译,连接。

2.C++调用C语言编写的dll时,当包括DLL头文件的接口函数时,加extern "C”

3.在C中调用C++编写的dll时,dll的导出函数必须 加上extern “C"


9.C/C++/MATLAB/.NET各自的特点

#C语言

是一种结构化语言,C程序的设计首先考虑的是如何通过一个过程,对输入进行运算处理得到输出。

#C++

首先考虑的是如何构造一个对象模型,让这个模型更能够契合问题域,通过获取对象的状态信息得到输出。

#matlab

如果涉及向量或矩阵运算,可以使用MATLAB编写组件(COM);

#.NET

大规模的用户界面开发;同COM之间的操作十分容易;对数据访问支持也良好;


10.C/C++中的const

const int t;  
const int size=10;
char buf[size];
//C
C中const的意思是:一个一旦初始化就不能被改变的变量;
既然是变量,编译阶段编译器不会知道其值;
//C++
C++中const的意思是:一个必须被初始化的常量;
既然是常量,编译阶段就知道其值;
//C++中const、define在定义常量比较
const有数据类型,define没有;编译器会对前者类型检查,define只是简单的字符替换


11.C++中const使用

//const成员变量
int a=5;          //a是变量
int const *b=&a;  //*b是常量
int* const b=&a;  //b是常量
const int *b=&a;  //*b是常量
const int* const b=&a; //*b是常量 b是常量
//const成员函数——"只读函数"
void fun(int a,int b) const{  }
不能改变数据成员的值;
不能调用非const成员函数;

12.指针和数组与"双引号中的字符串"

char a[]="12345";
char *b[]="12345";
 
sizeof(a);  //输出:6
sizeof(b);  //输出:4   b是一个指针类型,占4字节

#双引号字符串

编译器在字符串后自动加上空字符'\0'的额外存储片,标志字符串结束


13.内存中数据对齐

#为什么要进行数据对齐

主存储器结构包括存储单元和存储字长;存储容量=存储单元个数*存储字长;

每次内存主存要经过一系列步骤:MAR->译码->发出读或写命令->MDR,这是一个固定开销;

为了节省读写内存的开销,数据存入内存地址必须是该数据长度的整数倍,即数据对齐;

struct {  
  short a1;
  short a2;
  short a3;
}A;
struct {
  long b1;
  short b2;
}B;
struct {
  static int a;
  int b;
}C;
sizeof(A);  // 6
sizeof(B);  // 8
sizeof(C);  // 4  sizeof计算的是栈中分配的大小;static变量放在全局数据区;

short类型2字节;long类型4字节;在给结构体分配字节大小时,结构体每个数据都要保证数据对齐;

结构体A:

0000  a1;

0002  a2;

0004  a3;  

结构体B:

0000 b1;

0004 b2;

在这里虽然b1,b2都确保了数据对齐;但是当定义了一个struct B test[2];其内存地址如下:

0000 test[0].b1;

0004 test[0].b2;

0006 test[1].b1;

000A test[1].b2;

000C test[2].b1;

000F test[2].b2;

则0006不是4的倍数;所以为了保证数据对齐,分配给struct B结构体大小为8字节;

0000 test[0].b1;

0004 test[0].b2;

0008 test[1].b1;

000C test[1].b2;

14.strlen和sizeof的区别

//strlen函数只能接收char*类型参数,且必须以'\0'结束
char *a="123";
cout<<strlen(a) ; //3
 
char b[]="123";
cout<<strlen(b);  //3
 
char c[]={'1','2','3','\0'};
cout<<strlen(c);   //3 ,如果不加'\0'则error

//sizeof是宏定义,只和类型在内存中占得字节数有关
int a[100];
sizeof(a);   //100
 
int *b="123";
sizeof(b);   //4


15.关于类在内存中占的空间大小

//空类
class A{ }
class B1{ void fun();}
class B2{ virtual void func(){}}
class C:public A{}
class D:public B1{}
class E:public B2{}
 
//sizeof(A)    1
//sizeof(B1)   1
//sizeof(B2)   4
//sizeof(C)    1
//sizeof(D)    1
//sizeof(E)    4

#A

空类的size=1;C++要求每个实例在内存中都有独一无二的地址;所以当定义A a;对象a是根据A的大小分配内存,a至少有一个地址;

#B1

类所占内存的大小不包括成员函数大小,出虚拟成员函数除外;

ps:那么类如何访问自己的成员函数捏?

#B2

C++类中有一个指向虚函数的指针,占4字节;无论多少个虚函数,只有这一个指针;

16.指针和引用差别

#引用必须初始化;  指针可以不初始化,但使用前进行NULL检查;

#引用初始化后不能改变;  指针可以改变所指对象;

int *a;

*a=5; 

上述代码是错误的;a并没有被初始化;

a没有指向任何地址,*a=5显然是不合理的;

int *a=NULL;表示a指向了地址为0的内存,也就是此处不可解引用,可用于在使用前进行指针检查;

17.函数参数传递

//传值
void func(int a,int &b,int *c)
{
   a++;
   b++;
   (*c)++;
   
}
void main()
{
  int x=1,y=1,z=1;
  func(x,y,&z);
  cout<<x<<endl;   1 //传值,a的值改变,但x的值未变
  cout<<y<<endl;   2 //传引用,y和b指向同一个地址
  cout<<Z<<endl;   2 //传地址,改变的同一个地址的值
}


18.函数参数传指针,动态内存

//这是一个错误的程序!!!
void getMemory(char *a,int n)
{
   a=(char *)memset(sizeof(char)*n);
}
void main()
{
   char *str=NULL;
   getMemory(str,100);  //函数为指针a分配内存,而不是str,str依然是空;且函数未释放内存(堆),会造成内存泄露
   strcpy(str,"hello,nana");
}
//修改为正确程序
char* getMemory(char *a,int n)
{
   a=(char *)memset(sizeof(char)*n); //memset在堆上分配内存;
   return a;
}


19.字符串常量究竟在哪里分配内存

char* test_func()
{
  char s1[]="hello,nana";  //局部,在栈上分配内存,函数结束就释放掉;可修改s[i]的值
  char *s2="hello,nana";   //全局,在堆上分配在只读数据段;只读也就是不能修改~~~
}

20.强制类类型转换

class A{
public:
     A(){m_a=1,m_b=2;}
     ~A(){};
     void func(){cout<<m_a<<endl;cout<<m_b<<endl;}
private:
     int m_a;
     int m_b;
};
class B{
public:
     B(){m_c=3;}
     ~B(){};
     void func(){cout<<m_c<<endl;
private:
    int m_c;
};
int main()
{
     A ss;  //首先这里创建类对象未用new,则默认会调用类的默认构造函数
     ss.func();
     B *b=(B*)(&a);
     b->func();  // 内存偏移问题;输出 m_a的值1
}

21.区分数组or指针

/*区分定义的是数组还是指针
**数组——这数组元素是什么类型
**指针——指针指向什么类型
*/
float (**def)[10];
//def是二维指针,最终指向float类型的一维数组,size=10

double*(*gh)[10];
//gh是指针,指向double*类型的一维数组size=10

double(*f[10])();
//f是数组,数组元素类型时指向函数的指针,函数类型时无参数返回double类型的函数

int (*(*F)(int,int))(int);
//F是函数指针,指向返回值为函数指针,接收两个int参数的指针,返回的函数指针类型为接收int参数返回int类型的函数

int v[2][5]={{1,2,3,4,5},{6,7,8,9,10}};
int (*a)[5]=v;
cout<<**a<<**(a+1)<<endl;   1  11
cout<<*(a[0])<<*(a[1])<<endl;  1  11
cout<<*(*a+1)<<endl;   2
/*
**搞清楚a,*a,**a分别是什么类型,上述问题迎刃而解~
#a是指向一维int型数组的指针;所以a+1是指向下一个这种类型的指针
#a[0]=*(a+0); a[1]=*(a+1)
#*a是int型指针;
#**a是int型
*/

22.&a和a

#include<iostream>
using namespace std;
int main()
{
	int a[]={1,2,3};
	cout<<&(a[0])<<endl;   //002FFDCC
	cout<<&(a[1])<<endl;   //002FFDD0
	cout<<&(a[2])<<endl;   //002FFDD4
 
	cout<<a<<endl;         //002FFDCC
	cout<<a+1<<endl;       //002FFDD0
 
	cout<<&a<<endl;        //002FFDCC
	cout<<&a+1<<endl;      //002FFDC8   //&a+1和a+1的区别
	getchar();
	return 0;
}


转载于:https://my.oschina.net/ingu5102/blog/480940

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值