程序员面试宝典 笔记 第七章

1、指针和引用的区别:

  1.非空区别

  指针能指向空,而引用不行

  2.作为参数传递时不同

  引用:在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈区开辟了内存空间,但这时存放的是主函数实参变量的地址,正因为如此,被调函数对形参的  任何操作都会成为间接寻址,即通过栈存放的地址访问主函数的实参变量,对形参的任何操作都会改变实参变量,如果不想被改变,要将其定义为const(read-only).

  指针:指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数局部变量处理,即在栈中开辟了内存空间来存放主函数放进来的实参的值,从而成了实参的一个副本,不会影响主函数实参变量的值(例如第三题里的swap2()函数)。如果想通过指针参数传递来改变主函数中的相关变量,那就得使用指向指针的指针,或者指针的引用。

  3.合法性区别:

  在使用&前不必检查合法性,而使用指针时,总要检查其是否为NULL

  4.可修改性:

  指针可以被重新赋值以指向另一个不同的对象,而引用总是指向初始化时的那个指定的对象

  5.应用区别:

  使用指针的情况如下:1、不同的时刻指向不同的对象2、不指向任何对象

  使用引用的情况:总是指向一个对象

  6.对指针和引用分别进行sizeof(),引用为其对象的大小,而指针变量为地址的大小;

     

 

 2、常量声明的同时必须初始化

  eg:const double val;//error!

  改为:const double val=0.5;//right!

3、可以实现交换的函数有4、5

4、

#include<iostream>
using namespace std;
void main()
{
    const char str1[]="good";
    const char str2[]="good";
    const char *pstr1 = "good";
    const char *pstr2 = "good";
    cout<<(str1==str2)<<endl;
    cout<<(pstr1==pstr2)<<endl;

}

 结果为 0 ,1. 以为str1和str2都是字符数组,每个都有自己的内存区域,它们的值是各自的首地址;

 而pstr1 和 pstr2是字符指针,并不分配内存区域,good存储在常量区域,而这两个指针都指向这个区域的首地 址,所以相等。

5. 操作符优先级

6.double 和float 类型的数据进行比较时,一般不用== 而用一个范围

    eg:const double val = 0.00001;

   if((x>=-val)&&(x<=val));而不用== 因为小数存在舍入误差

7.new/delete 和malloc/free的主要区别是什么?

      1.new 能自动计算需要分配的内存空间,而malloc需要自己计算 int *ptr=malloc(2*sizeof(int));

    2.new/delete可用于申请、释放类对象的内存,调用构造、析构函数而 malloc/free不能;

  3.malloc需要库函数的支持,new不需要;

  4.new可以直接返回带类型的指针,而malloc返回的是void类型。

      注意:当释放掉指针以后还要将其置为NULL;

8.变量名命名规则:

  变量名只能含有数字、字母、下划线,而数字又不能作为开头。

9.struct和union有什么不同?

  1. 联合体只存放一个被选中的成员,而结构体存储的空间是所有成员相加(要考虑字节对齐)
  2. 对联合体某一个成员赋值将会改变其他变量的值,而结构体不会;

10.C和C++中struct有什么不同

      1、C中不能有成员函数

  2、c++中有访问权限设定而c中没有

  3、c++中有丰富的继承关系,而c中没有

11.c++中struct和class有什么区别?

  1.struct默认继承权限是public 而class是private

  2、class用于定义模板参数 而struct不行

12.如何计算一个整数的7倍?

     (X<<3)-3

13.求两个数的平均数

    unsigned int x=6;
    unsigned int y=10;
    unsigned int z=(x&y)+((x^y)>>1);
    cout<<z;

    异或:位不同取1  相同取0

14.volatile有什么用途?

  volatile是一个类型修饰符,它用来修饰呗不同线程访问和修改的变量,每次用到他的时候都是从内存从新读取  而不会使用cache中的原有数值,以保证读取的是最新的数值

15.assert()作用是什么?

  int i=0;

  assert(i!=0);

  int k=10/i;

  如果括号内的值是真,则继续执行,否则程序终止;

  ASSERT()是宏,而assert()是函数,ASSERT()在DEBUG版本中有,在Release版本中被忽略。

16.枚举变量的值是如何计算的

  enum{a,b=5,c,d=4,e};

     cout<<a<<b<<c<<d<<e<<endl;

     0 5 6 4 5

    第一个初始化为0  后面为前面的数加1

 17.c++中是不是所有的动作都是main()函数引起的?

  不是,静态变量 全局变量 全局对象分配在之前就完成,只有编译器是由main()开始执行的

  同样,main函数执行完以后 全局对象销毁是在其之后完成的

18.如何进行int float bool 指针变量与零值比较?

     int

     if(n==0)if(n!=0)

     float 有误差

       const float EPS=0.00001;

      if((x>=-EPS)&&(x<=EPS))

    bool

   if(x)

   if(!x)

   指针变量

  if(p==NULL)

  if(p!=NULL)

 

19.什么时候需要将引用作为返回值

  江阴用作为函数返回值类型格式如下:

  int &fun(int a);

  将引用作为返回值时 内存不产生返回值副本 从而大大提高效率  假如返回类型是个灰度矩阵 那效率就差远了

  

 20.c++中 如何实现模板函数的外部调用?

  使用export关键字,可以实现模板函数的外部调用,和extern作用类似,

  在头文件中声明模板函数或者模板类,在cpp文件中具体操作如下:

  extern int  i;

  export template<class T>class S<int>s;

     export template<class T>void fun(T &t){......}

21.explicit在c++ 中有什么作用?

  防止普通构造函数呗隐式调用

     String s1="hello"会执行性隐式转换 等价于String s1=String("hello") 而有时候这种情况不是程序员所希望看到的 所以就在构造函数前面加上explicit关键字

22.c++中异常处理用了哪些关键字?

  try catch throw 

      当有finally时  总要执行finally中的语句,如果try中有return 返回值  而finally中也要返回该值 那么 finally中的返回值将会把try中的返回值覆盖

23.内存分配形式有哪些?

  符号起始区域(BSS) 存放未经初始化的全局变量和静态数据

  数据段  存放已经初始化的全局变量和静态数据

  代码段  存放代码和常量

  堆    动态存分配 链式存储

  栈    存储局部变量

  另,static 局部变量 的生命周期是从第一次执行到该静态变量 到程序结束   不执行时没有生命

24.指针与数字相加的结果是什么?

  eg:

  unsigned char *p1;

  unsigned long *p2;

  p1=(unsigned char*)0x801000;

  p2=(unsigned long *)0x801000;

  cout<<p1+5<<endl;

  cout<<p2+5<<endl;

  结果 0x801005

    0x801014

   计算过程 p1+5*sizeof(char)

      p2+5*sizeof(unsigned long)

 25.#include<>和#include“”有什么区别

  尖括号  编译器首先从标准库路径开始搜索  引号 编译器先从用户工作路径开始搜索

 26.C++中内联函数和宏定义有什么区别?

  第一、 宏定义是在预处理阶段进行代码替换 而内联函数是在编译阶段插入代码

  第二、宏定义没有类型检查 而内联函数有类型检查

  另,内联函数不能使用递归调用

27.内联函数和普通函数区别有哪些?

  相同点:内联函数与普通函数的参数传递机制相同

      不同点:普通函数调用时 系统首先跳跃到该函数的入口地址 执行函数体 完成后 再返回到函数调用的地方 函数始终只有一个复制

      内联函数调用时 不需要寻址过程 而是将该函数直接展开 调用多少次就展开多少次

28.定义常量时 #define和const谁更好?

  1.#define是预编译阶段简单地文本替换 存储在code segment ,const常量存在于程序的数据段 并且在堆栈中分配了内存

  2.const有数据类型 define没有数据类型

 

7.7位操作


29.0 一些结构体声明中的冒号和数字是什么意思?

  c语言结构体可以实现位段,位段的结构体成员必须是int 或者unsigned int类型,

  

typedef struct
{
    int a:2;
    int b:2;
    int c:1;
}test;

int main()
{
    test t;
    t.a=2;
    t.b=3;
    t.c=1;
    cout<<t.a<<t.b<<t.c<<endl;
}

结果是 1,-1,-1

 

  a占两位,二进制为01输出1;

  b占两位,二进制为11,第一位会被认为为符号位,输出-1;

  c占一位,二进制为1,也被认为为符号位,输出-1;

 

 

  

29.char 型变量i=127 加1为什么变成了-128?

  补码求十进制:

  如果首位为0,则直接将该数化为十进制就可以  

    例如 0000 0010=2

  如果首位为1,那么将所有位数取反 然后加1 换成十进制 前面加负号就可以了 

     例如 1000 0000 该补码所有位取反为0111 1111 加1 为:1000 0000 该数为128  前面加负号 则为-128

  那么问题就有解了 127=0111 1111 127+1=1000 0000  该数是补码形式存储的 所以表示的是-128

  上述问题可以简化成钟表问题  当溢出时数字又回归到最小值    

     max(char)+1=min(char)  

  同理,max(int)+1=min(int)

  原码 反码 补码之间的关系:

  正数的原码 反码 补码 都相同

  负数:

    原码 1000 0001原

    反码为 符号位不变 其余位取反 1111 1110反

    补码为 符号位不变 其余位取反加1  1111 1111补

    也就是1000 0001原  = 1111 1110反  = 1111 1111补

  知道补码如何求该补码的十进制数

    1.如果首位为0,则直接求数  例如:0111 1111  该数为127

    2.如果首位为1,则所有位取反 然后加1 换成十进制数  再在此数前面加负号  例如:1000 0000补 取反 0111 1111 +1=1000 0000 这个数=128 前面加负号为-128

30.求一个整型数的二进制数的1的个数

  

 1 int fun(int n)
 2 {
 3     int count=0;
 4     while(n>0)
 5     {
 6         count+=n&0x01;
 7         n>>=1;
 8     }
 9     return count;
10 }

31.大端小端问题

  大端:存储方式是从高字节到低字节  

  小端:存储方式从低字节到高字节

  见下图

    

   大小端测试程序:

#include<iostream>
using namespace std;
#include<assert.h>
int main(int argc, char **argv) 
{ 
    int i = 1; 
    char *cp = (char *)&i;  
       if (*cp)
          printf("Little Endian\n"); 
    else 
        printf("Big Endian\n");  
    return 0;
}

 32。大小端例题

int main()
{
	unsigned int val1=0x12345678;
	unsigned int val2=0;
	unsigned char aucval[4]={0x12,0x34,0x56,0x78};//数组地址依次增大
	unsigned short us1=0;
	unsigned short us2=0;
	memcpy(&val2,aucval,sizeof(val2));//将数组的值赋给val2
	us1=(unsigned short)val1;
	us2=(unsigned short)val2;
	cout<<hex<<us1<<endl;//输出十六进制数
	cout<<hex<<us2<<endl;
	return 0;
}

  输出结果5678  3412

  因为是小端  所以存储方式(地址由小到大)val1  78 56 34 12

                      val2 12 34 56 78

 33.不用除法如何实现两个正整数的除法?

  常用等式:

  -n=~(n-1);

  获取整数n的二进制数中最后一个1

  n&(-n)或者n&~(n-1)

  去掉整数n的二进制数最后一个1

  n&(n-1)

  代码

  

int divv(int a ,int b)
{
    int res=0;
    if(b==0)
    cout<<"除数不能为0"<<endl;
    while(a>=b)
    {
        res++;
        a=a-b;
    } 
    return res;
}

34.如何只是用逻辑运算实现加法?

  int add(int num1,int num2)

  {

    if(0==num2)

      return num1;

    int sumtemp=sum1^sum2;

    int carry=(num1&num2)<<1;

    return add(sumtemp,carry);

  }

 35.如何只用逻辑运算实现乘法?

   

int mutiply(int a, int b)
{
    bool neg=(b<0);
    if (b<0)
    b=-b;
    int sum=0;
    map<int,int>bitmap;//<value,key>
    for (int i=0;i<32;i++)
    {
        bitmap.insert(pair<int,int>(1<<i,i));//键值对为(1,0),(2,1),(4,2)
    }
    while(b>0)
    {
        int last_bit =bitmap[b&~(b-1)];//获取二进制中最后一位1
        sum+=(a<<last_bit);
        b&=b-1;//去掉二进制中最后一位1
    }
    if (neg)
    {
        sum=-sum;
    }
    return sum;
}

 

 

 7.8函数

 

36.该函数实现多参数求和

 

int add2(char num,int ,int ,int  )//num是参数个数
{
    int sum=0;
    int index=0;
    int *p=null;
    p=(int *)&num+1;
    for(;index<num;++index)
    {
        cout<<*p<<endl;
        cout<<p<<endl;
        sum+=*p++;
    }
    return sum;
}
void main()
{
    int sum=add2(3,1,2,3);
    cout<<sum;
}

 

  此处注意两点:

  1.形参的压栈顺序由右向左

  2.栈空间地址由高到低

  所以可以得出 形参变量的地址从左往右是依次增大的 并且地址空间是连续的  所以可以用指针遍历形参数据

37 函数指针和指针函数有什么区别?

  指针函数 int * fun(int,int)函数返回的是某一类型的指针

  函数指针 int (*pf)(int,int)声明一个函数指针 而 pf=func 将func函数的首地址赋值给指针

  

38 数组指针和指针数组有什么区别 

  数组指针 int (*p)[8];

  指针数组 int *p[8]

39.模板函数

template<class T>
T VecSum(vector<T>&vec)
{
    T sum=0;
    for (int i = 0;i<(int)vec.size();i++)
    {
        sum=sum+vec[i];
    }
    return sum;
}

40 指针常量与常量指针区别

  指针常量 int * const p;p不可以修改  *p可以修改  也就是p指向的对象不能改 但是对象的值可以修改

  常量指针 const *int p ;*p不可以修改  p可以修改  p指向的对象可以修改   但是对象的值不可以修改

 41.如何把字符串转换成数字?

   基本思路:

    1、判断指针是否为空

    2、判断首位是否有正负号  若有负号 则地址加1  符号sign为-1

    3.开始计算 地址++

    4.返回 sign*结果

int myatoi(char *str)
{
    if (str==NULL)
    {
        cout<<"invalid!"<<endl;
        return -1;
    }
    int Sign=(*str=='-')?-1:1;
    if (*str=='+'||*str=='-')
    {
        str++;
    }
    int result=0;
    while (*str>='0'&&*str<='9')
    {
        result=10*result+((*str)-'0');
        str++;
    }
    return result*Sign;
}

42、如何将整型数字转化为字符串输出?

  基本思路:

  1.申请堆区内存 用以返回指针

  2.判断数字正负 为负的话 将数字取反

  3.用do while循环开始计算 将每个字符存储到临时的char型数组中

  4.若为负的  数组后面加'-'

  5.数组最后面加入结束符号‘\0’

  6.开始将数组里的字符调换顺序

  7.最后加入结束符

  8返回指针

char *myitoa(int num)
{
    char *pResult=new char(12);//返回时可以返回队内存的指针 但是不可以返回栈区内存指针
    int Sign=0;
    char temp[12]={0};
    int IdexTemp=0;
    int IdexResult=0;
    if (num<0)
    {
        num=-num;
        Sign=-1;
    }
    do 
    {
        temp[IdexTemp]=num%10+'0';
        num/=10;
        IdexTemp++;
    } while (num>0);

    if (Sign<0)
    {
        temp[IdexTemp++]='-';
    }
    temp[IdexTemp]='\0';
    IdexTemp--;
    while (IdexTemp>=0)
    {
        pResult[IdexResult]=temp[IdexTemp];
        IdexResult++;
        IdexTemp--;
    }
    pResult[IdexResult]='\0';
    return pResult;
}

 

 

  43 STL(Standard Template Library)标准模板库,是一个c++领域中,用模板技术实现的数据结构和算法库,其中的vector.list.stack.queue等结构不仅拥有强大的功能,还有了更高的安全性。

 什么是泛型编程?

        泛型是指具有在多种数据类型上皆可操作。泛型编程目的是为了发明一种语言机制,能帮助实现一个通用的标准容器库。

 

44.Vector和list的区别有哪些?

  1. Vector分配连续的地址空间,随机访问效率很高;而list是非连续的内存区域,随机访问效率差。
  2. Vector中插入或者删除某个元素,需要将现有的元素进行复制、移动,开销很大;而list在任意位置插入删除元素效率都很高,不需要复制元素来实现移动。
  3. Vector空间只存储数据,而list通过一对指向首尾元素的指针双相链接起来允许前后两个方向进行遍历,每个元素存储两个指针的额外开销。

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/mu-tou-man/p/3728213.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值