【面试招聘】C++ 面试题型收集

12 篇文章 0 订阅

C++11 constexpr和const的区别详解

  constexpr 是 C++ 11 标准新添加的关键字,在此之前(C++ 98/03标准)只有 const 关键字,其在实际使用中经常会表现出两种不同的语义;

  const并不能代表“常量”,它仅仅是对变量的一个修饰,告诉编译器这个变量只能被初始化,且不能被直接修改(实际上可以通过堆栈溢出等方式修改)。而这个变量的值,可以在运行时也可以在编译时指定。

  constexpr可以用来修饰变量、函数、构造函数。一旦以上任何元素被constexpr修饰,那么等于说是告诉编译器 “请大胆地将我看成编译时就能得出常量值的表达式去优化我”。

const int func() {
    return 10;
}
main(){
  int arr[func()];
}
//error : 函数调用在常量表达式中必须具有常量值

对于func() ,胆小的编译器并没有足够的胆量去做编译期优化,哪怕函数体就一句return 字面值;

constexpr func() {
    return 10;
}
main(){
  int arr[func()];
}
//编译通过

则编译通过

  编译期大胆地将func()做了优化,在编译期就确定了func计算出的值10而无需等到运行时再去计算。这就是constexpr的第一个作用:给编译器足够的信心在编译期去做被constexpr修饰的表达式的优化。

  constexpr表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。声明为constexpr的变量一定是一个const变量,而且必须用常量表达式初始化:

constexpr int mf = 20;  //20是常量表达式
constexpr int limit = mf + 1; // mf + 1是常量表达式
constexpr int sz = size(); //之后当size是一个constexpr函数时才是一条正确的声明语句

指针和constexpr

  必须明确一点,在constexpr声明中如果定义了一个指针,限定符conxtexpr仅对指针有效,与指针所指的对象无关。

const int*p = nullptr;        //p是一个指向整形常量的指针
constexpr int* q = nullptr;   //q是一个指向整数的常量指针

  p是一个指向常量的指针,q是一个常量指针,其中的关键在于constexpr把它所定义的对象置为了顶层const。并且,constexpr指针所指变量必须是全局变量或者static变量(既存储在静态数据区的变量)。

constexpr的好处:

  • 是一种很强的约束,更好地保证程序的正确语义不被破坏。
  • 编译器可以在编译期对constexpr的代码进行非常大的优化,比如将用到的constexpr表达式都直接替换成最终结果等。
  • 相比宏来说,没有额外的开销,但更安全可靠。
class Solution {
private:
	// 这里我用const就不可以,而用constexpr就可以
    static constexpr int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
public:
};

非引用性参数,函数始终值传递

  如果函数的形参不加引用类型,那么无论参数是变量还是地址,都是以值的形式传递;在做下面这道题的时候,加深了这一块的知识:
在这里插入图片描述
利用递归解题:

class Solution
{
public:
    ListNode *rev(ListNode *head, ListNode *&new_head)
    {
        if (head->next == nullptr)
        {
            new_head = head;
            return head;
        }
        ListNode *cur = rev(head->next, new_head);
        cur->next = head;
        return head;
    }
    ListNode *reverseList(ListNode *head)
    {
        if(head==nullptr)
            return nullptr;
        ListNode *new_head = nullptr;
        rev(head, new_head)->next = nullptr;
        return new_head;
    }
};

  对于函数 rev ,如果形参new_head前面不加引用类型,那么传递的是地址的值,也就是说,随着递归一次次开辟栈,栈局部变量 new_head 仅仅会在进行到最后一个结点时,将自己的地址值修改为 head ,此时 return,那么栈清空,变量释放,回到上一个栈是 new_head仍为 nullptr;

  如果加了 & 引用类型,如果 rev 调用多少次,都是用的new_head本身,并不会在栈中创建和它相等的局部变量;

  当然,这并不否认可以通过函数传指针来交换两个变量这一结果:

void fun(int *a,int *b)
{
	int temp=*a;
	*a=*b;
	*b=*a;
}

calloc和malloc的区别

从调用形式上来看
(类型说明符*) malloc (size) 功能:在内存的动态存储区中分配一块长度为"size" 字节的连续区域。函数的返回值为该区域的首地址。 “类型说明符”表示把该区域用于何种数据类型。(类型说明符*)表示把返回值强制转换为该类型指针。“size”是一个无符号数。例如: pc=(char *) malloc (100); 表示分配100个字节的内存空间,并强制转换为字符数组类型, 函数的返回值为指向该字符数组的指针, 把该指针赋予指针变量pc;

calloc 也用于分配内存空间。调用形式: (类型说明符*) calloc(n,size)功能:在内存动态存储区中分配n块长度为“size”字节的连续区域。函数的返回值为该区域的首地址。(类型说明符*)用于强制类型转换。calloc函数与malloc 函数的区别仅在于一次可以分配n块区域。例如: ps=(struet stu*) calloc(2,sizeof (struct stu)); 其中的sizeof(struct stu)是求stu的结构长度。因此该语句的意思是:按stu的长度分配2块连续区域,强制转换为stu类型,并把其首地址赋予指针变量ps;

从内存分配上来看
两者都是动态分配内存;malloc不初始化分配的内存,已分配的内存中可以是任意的值;而calloc初始化已分配的内存为0;
malloc不会初始化所分配的空间。如果你所申请的空间不释放,不被其他地方再次申请就不会有什么后果。如果你申请的空间进行一些写操作之后(空间里的内容已改变)你再释放再申请给其他指针使用(没有手动初始化)就可能导致程序崩溃;

异常处理

请添加图片描述

请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述
请添加图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值