C/C++常见面试题(二)

本博客内容
一、const用法
二、define与const的区别
三、typedef的用法
四、define与typedef的区别
五、static在C/C++中的用法
六、指针函数与函数指针/指针数组与数组指针
七、前缀++i和后缀i++的区别
八、sizeof和strlen的区别
九、C++字节对齐问题
十、C++中的空类会默认产生哪些类成员函数

一、const用法

1.修饰常量  如 const int value; 与 int const value效果相同。而且必须进行初始化,否则编译报错,而且不能被重新赋值。
2.修饰函数参数,保护参数不被修改
3.修饰指针  分3种情况

const int * p1;   // p1指向的值为常量  即*p1是常量,p1可以指向其他地方
int const * p2;   //与上述相同

int * const p3;   //指针p3为常量,不能指向其他值,但可以通过*p3来修改指向的值

const int * const p4;  //指针本身和指针内容都为常量

4.修饰类对象/对象指针/对象引用
   const修饰的对象,该对象的任何非const成员函数都不能被调用,因为任何非const成员函数都可能会修改成员变量。
5.修饰成员变量
  表示成员变量不能被修改,同时只能在初始化列表中赋值。
6.修饰类的成员函数,则该成员函数不能修改类中任何非const成员函数。,一般放在函数的最后来修饰。

二、define与const的区别

1.编译器处理方式不同
   define宏是预处理阶段展开
   const常量是编译运行阶段使用
2.类型和安全检查不同
   define宏没有类型,不做任何类型检查,仅仅展开
   const常量有具体的类型,在编译阶段会执行类型检查
3.存储方式不同
   define宏仅仅是展开,有多少地方使用,就展开多少次,不分配内存
   const常量会在内存中分配(可堆,可栈)
其它:当用#define定义一个简单的函数时,强烈建议用内联函数替换!

三、typedef的用法

参考:https://blog.csdn.net/wangqiulin123456/article/details/8284939
   https://blog.csdn.net/superhoy/article/details/53504472
1.定义一种类型的别名,不只是简单的宏替换,可以同时声明指针型的多个对象。

char * a, b ;  //仅仅a是指针

typedef char* PCHAR;
    PCHAR a,b;  //a和b均为指针

2.旧的C代码中,声明struct新对象时,必须要带上struct,形式为:struct 结构名 对象名
在C++中,则可以直接使用结构名 对象名
typedef的引入,使得在C程序中,直接通过别名来声明对象。

typedef struct tagPOINT  
{  
    int x;  
    int y;  
}POINT;  
POINT p1; // 这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时候 

3.用来定义与平台无关的类型
  比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:
    typedef long double REAL;
  在不支持 long double 的平台二上,改为:
    typedef double REAL;
  在连 double 都不支持的平台三上,改为:
    typedef float REAL;
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
4.为复杂声明定义一个新的简单别名。
把带变量名的部分留到最后替换。
举例
原声明:int (*a[5])(int, char);
  变量名为a,直接用一个新别名pFun替换a就可以了:
  typedef int (*pFun)(int, char);
原声明的最简化版:
  pFun a[5];
tip:理解复杂声明的“右左法则”
  从变量名看起,先往右,再往左,碰到一个圆括号就调整阅读方向,括号内分析完,就跳出括号,还是按先右后左的顺序,如此循环。
int (*func)(int *p);
  首先找到变量名func,外面有一对圆括号,而且左边是一个号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明 (*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int类型的形参,返回值类型是int。
int (func[5])(int );
  func 右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个,说明func的元素是指针(注意这里的不是修饰func,而是修饰 func[5]的,原因是[]运算符优先级比高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指 针,它指向的函数具有int类型的形参,返回值类型为int。
也可以记住2个模式
  type (*)(….)函数指针
  type (*)[]数组指针

四、define与typedef的区别

1.用法不同
   define用来定义常量,以及书写复杂使用频繁的宏
   typedef用来一种数据类型的别名,增强程序的可读性
2.执行时间不同
   define是宏定义,预编译部分,只是进行字符串的替换,不进行类型检查
   typedef是编译过程的一部分,有类型检查的功能
3.作用域不同

void fun()   
{   
#define A int   
}  
void gun()   
{   
//在这里也可以使用A,因为宏替换没有作用域,   
//但如果上面用的是typedef,那这里就不能用A ,不过一般不在函数内使用typedef  
} 

4.对指针的操作
  二者修饰指针类型时,作用不同
  typedef是语句,句尾要加分号,define不是语句,不能句尾加分号

Typedef int * pint;  
#define PINT int *  
Const pint p;//p不可更改,p指向的内容可以更改,相当于 int * const p;  
Const PINT p;//p可以更改,p指向的内容不能更改,相当于 const int *p;或 int const *p;  
pint s1, s2; //s1和s2都是int型指针  
PINT s3, s4; //相当于int * s3,s4;只有一个是指针。

五、static在C/C++中的用法

C语言中:
1.全局静态变量
 内存中的位置:静态存储区(整个运行期都存在)
 初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非被显示初始化)
 作用:在声明它的文件之外不可见
2.局部静态变量
 内存中的位置:静态存储区
 初始化:未经初始化的会被自动初始化为0
 作用域:仍为局部,离开作用域后,未被销毁,延长了生存周期,只是不能再访问了。
 注意:全局的static变量和在静态初始化过程中分配内存并初始化
    局部静态变量(函数内部的)在第一次使用时分配内存并初始化
3.静态函数
 函数的定义和声明默认是extern的,但静态函数只是在声明它的文件中可见。
C++中:
 增加了2个作用:定义静态数据成员、静态函数成员。
 函数实现时,不需要添加static了。

六、指针函数与函数指针/指针数组与数组指针

//指针函数
int * fun1(int t);  //fun1是一个返回指针的函数 
//函数指针
int (*fun2)(int t); //fun2是一个指向函数的指针 ,该函数的参数为int,返回值为int
//指针数组
int* p1[10];   //p1是一个10个元素的数组,每个元素都是int* ,因为[]优先级高于* ,先和p结合
//数组指针
int (*p2)[10];  //p2是一个指针,该指针指向一个数组,该数组由10个int型元素组成

七、前缀++i和后缀i++的区别

对于内置类型:前缀/后缀效率无区别
对于自定义类型:前缀格式效率更高,因为不需要保存副本,节省时间
        但是前缀的优先级低,和*一样,是从右向左结合的。

扫描二维码关注公众号,回复: 2776290 查看本文章
++*t;  //类似于++(*t);  对值加1
*t++;   //类似于*(t++);  后缀格式优先级高

八、sizeof和strlen的区别

1.sizeof是一个操作符,strlen是一个库函数
2.sizeof的参数可以是类型/变量,strlen只能是以结尾为”\0”的字符串做参数
3.编译器在编译时就得到了sizeof的结果,strlen函数必须在运行时才能得到
4.sizeof计算的是数据类型占内存的大小,strlen计算的是字符串的实际长度
5.数组做sizeof的参数不退化,传递给strlen就退化为指针了

九、C++字节对齐问题

参考:http://www.cnblogs.com/repository/archive/2011/01/13/1933721.html
1.为什么需要字节对齐?
为了提高存取效率。字节是内存空间分配的最小单位。在程序中,我们定义的变量可以放在任何位置。其实不同架构 的CPU在访问特定类型变量时是有规律的,比如有的CPU访问int型变量时,会从偶数地址开始读取的,int类型占用4个字节(windows平台)。 0X0000,0X0004,0X0008…..这样只需要读一次就可以读出Int类型变量的值。相反地,则需要读取二次,再把高低字节相拼才能得到 int类型的值,这样子看的话,存取效率当然提高了。通常写程序的时候,不需要考虑这些情况,编译都会为我们考虑这些情况,除非针对那些特别架构的 CPU编程的时候的则需要考虑 。当然用户也可以手工控制对齐方式。
2.编译器对字节对齐的规则
①关于数据的自身的对齐值,不同类型会按照不同的字节来对齐。
类型    对齐值(字节)
char    1
short    2
int     4
float     4
double   4
②类、结构体的自身对齐值。使用成员当中最大的对齐字节来对齐。
③指定对齐字节值,使用宏#pragma pack(n)
④类、结构体及成员的有效对齐字节值。有效对齐值=min(类/结构体/成员的自身对齐值,指定对齐值)

十、C++中的空类会默认产生哪些类成员函数

编译器默认产生4个成员函数:默认构造函数、默认析构函数、拷贝构造函数、赋值函数
还有2个不经常提到的:取址运算符。取值运算符 const。
注意:只有当实际使用这些函数的时候,编译器才会去定义它们。

   
如有问题,请大家批评指正,谢谢…未完待续…   
   
   

猜你喜欢

转载自blog.csdn.net/xiongluo0628/article/details/81391130