嵌入式知识笔记1——C++面试复习(3)

四、关键字库函数

4.1 sizeof strlen 的区别

strlen 是头文件中的函数,sizeof C++ 中的运算符。

strlen 测量的是字符串的实际长度(其源代码如下),以 \0 结束。而 sizeof 测量的是字符数组的分配大小。

strlen 本身是库函数,因此在程序运行过程中,计算长度;而 sizeof 在编译时,计算长度;

sizeof 的参数可以是类型,也可以是变量;strlen 的参数必须是 char* 类型的变量。

 4.2 lambda 表达式(匿名函数)的具体应用和使用场景

lambda 表达式的定义形式如下: 

[capture list] (parameter list) -> reurn type

{

function body

}

其中: capture list:捕获列表,指 lambda 表达式所在函数中定义的局部变量的列表,通常为空,但如果函数体中用到了 lambda 表达式所在函数的局部变量,必须捕获该变量,即将此变量写在捕获列表中。捕获方式分为:引用捕获方式 [&]、值捕获方式 [=]  return typeparameter listfunction body分别表示返回值类型参数列表函数体,和普通函数一样。

举例:lambda 表达式常搭配排序算法使用。

#include 
#include 
#include 
using namespace std;
 
int main()
{
    vector arr = {3, 4, 76, 12, 54, 90, 34};
    sort(arr.begin(), arr.end(), [](int a, int b) { return a > b; }); // 降序排序
    for (auto a : arr)
    {
        cout << a << " ";
    }
    return 0;
}
/*
运行结果:90 76 54 34 12 4 3
*/

4.3 explicit 的作用(如何避免编译器进行隐式类型转换

作用:用来声明类构造函数是显示调用的,而非隐式调用,可以阻止调用构造函数时进行隐式转换。可用于修饰单参构造函数,因为无参构造函数和多参构造函数本身就是显示调用的,再加上 explicit 关键字也没有什么意义。

隐式转换:

#include 
#include 
using namespace std;
 
class A
{
public:
    int var;
    A(int tmp)
    {
        var = tmp;
    }
};
int main()
{
    A ex = 10; // 发生了隐式转换
    return 0;
}

上述代码中,A ex = 10; 在编译时,进行了隐式转换,将 10 转换成 A 类型的对象,然后将该对象赋值给 ex等同于如下操作:为了避免隐式转换,可用 explicit 关键字进行声明:

#include 
#include 
using namespace std;
 
class A
{
public:
    int var;
    explicit A(int tmp)
    {
        var = tmp;
        cout << var << endl;
    }
};
int main()
{
    A ex(100);
    A ex1 = 10; // error: conversion from 'int' to non-scalar type 'A' requested
    return 0;
}

4.4 static 的作用

作用:static 定义静态变量,静态函数。

保持变量内容持久static 作用于局部变量,改变了局部变量的生存周期,使得该变量存在于定义后直到程序运行结束的这段时间。

#include 
using namespace std;
 
int fun(){
    static int var = 1; // var 只在第一次进入这个函数的时初始化
    var += 1;
    return var;
}
  
int main()
{
    for(int i = 0; i < 10; ++i)
    	cout << fun() << " "; // 2 3 4 5 6 7 8 9 10 11
    return 0;
}

隐藏:static 作用于全局变量和函数,改变了全局变量和函数的作用域,使得全局变量和函数只能在定义它的文件中使用,在源文件中不具有全局可见性。(注:普通全局变量和函数具有全局可见性,即其他的源文件也可以使用。)

static 作用于成员变量和类的成员函数,使得类变量或类成员函数和类有关,也就是说可以不定义类的对象就可以通过类访问这些静态成员。注意:类的静态成员函数中只能访问静态成员变量或者静态成员函数不能将静态成员函数定义成虚函数。

#include
using namespace std;
 
class A
{
private:
    int var;
    static int s_var; // 静态成员变量
public:
    void show()
    {
        cout << s_var++ << endl;
    }
    static void s_show()
    {
        cout << s_var << endl;
		// cout << var << endl; // error: invalid use of member 'A::a' in static member function. 静态成员函数不能调用非静态成员变量。无法使用 this.var
        // show();  // error: cannot call member function 'void A::show()' without object. 静态成员函数不能调用非静态成员函数。无法使用 this.show()
    }
};
int A::s_var = 1;  // 静态成员变量在类外进行初始化赋值,默认初始化为 0
 
int main()
{
    
    // cout << A::sa << endl;    // error: 'int A::sa' is private within this context
    A ex;
    ex.show();
    A::s_show();
}

4.5 static在类中使用的注意事项(定义、初始化和使用)***

static 静态成员变量:

静态成员变量是在类内进行声明,在类外进行定义和初始化,在类外进行定义和初始化的时候不要出现 static 关键字和privatepublicprotected 访问规则。

静态成员变量相当于类域中的全局变量,被类的所有对象所共享,包括派生类的对象。

静态成员变量可以作为成员函数的参数,而普通成员变量不可以。

 

#include 
using namespace std;
 
class A
{
public:
    static int s_var;
    int var;
    void fun1(int i = s_var); // 正确,静态成员变量可以作为成员函数的参数
    void fun2(int i = var);   //  error: invalid use of non-static data member 'A::var'
};
int main()
{
    return 0;
} 

静态数据成员的类型可以是所属类的类型,而普通数据成员的类型只能是该类类型的指针或引用。

#include 
using namespace std;
 
class A
{
public:
    static A s_var; // 正确,静态数据成员
    A var;          // error: field 'var' has incomplete type 'A'
    A *p;           // 正确,指针
    A &var1;        // 正确,引用
};
 
int main()
{
    return 0;
}
 

static 静态成员函数:

静态成员函数不能调用非静态成员变量或者非静态成员函数,因为静态成员函数没有 this 指针。静态成员函数做为类作用域的全局函数。

静态成员函数不能声明成虚函数(virtual)、const 函数和 volatile 函数。

 

4.6 static 全局变量和普通全局变量的异同

相同点:

存储方式:普通全局变量和 static 全局变量都是静态存储方式

不同点: 

作用域:普通全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,普通全局变量在各个源文件中都是有效的;静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其他源文件中引起错误。

初始化:静态全局变量只初始化一次,防止在其他文件中使用。

4.7 const 作用及用法

作用:

const 修饰成员变量,定义成 const 常量,相较于宏常量,可进行类型检查,节省内存空间,提高了效率。

const 修饰函数参数,使得传递过来的函数参数的值不能改变

const 修饰成员函数,使得成员函数不能修改任何类型成员变量mutable 修饰的变量除外),也不能调用const 成员函数,因为非 const 成员函数可能会修改成员变量。

在类中的用法:

const 成员变量:

const 成员变量只能在类内声明、定义,在构造函数初始化列表中初始化

const 成员变量只在某个对象的生存周期内是常量,对于整个类而言却是可变的,因为类可以创建多个对象,不同类的 const 成员变量的值是不同的。因此不能在类的声明中初始化 const 成员变量,类的对象还没有创建,编译器不知道他的值

const 成员函数: 不能修改成员变量的值,除非有 mutable 修饰;只能访问成员变量

不能调用非常量成员函数,以防修改成员变量的值。

#include 
using namespace std;
 
class A
{
public:
	int var;
	A(int tmp) : var(tmp) {}
	void c_fun(int tmp) const // const 成员函数
	{
		var = tmp; // error: assignment of member 'A::var' in read-only object. 在 const 成员函数中,不能修改任何类成员变量。		
		fun(tmp); // error: passing 'const A' as 'this' argument discards qualifiers. const 成员函数不能调用非 const 成员函数,因为非 const 成员函数可能会修改成员变量。
	}
 
	void fun(int tmp)
	{
		var = tmp;
	}
};
 
int main()
{
    return 0;
}
 

4.8 define const 的区别

1.区别:

  • 编译阶段:define 是在编译预处理阶段进行替换,const 是在编译阶段确定其值。
  • 安全性:define 定义的宏常量没有数据类型,只是进行简单的替换,不会进行类型安全的检查;const 定义的常量是有类型的,是要进行判断的,可以避免一些低级的错误。
  • 内存占用:define 定义的宏常量,在程序中使用多少次就会进行多少次替换,内存中有多个备份,占用的是代码段的空间const 定义的常量占用静态存储区的空间,程序运行过程中只有一份
  • 调试:define 定义的宏常量不能调试,因为在预编译阶段就已经进行替换了;const 定义的常量可以进行调试

2.总结const 的优点有数据类型,在定义式可进行安全性检查。/可调式。/占用较少的空间。

4.9 define typedef 的区别

原理#define 作为预处理指令,在编译预处理时进行替换操作作正确性检查,只有在编译已被展开的源程序时才会发现可能的错误并报错。

          typedef 是关键字,在编译时处理,有类型检查功能,用来给一个已经存在的类型一个别名,但不能在一个函数定义里面使用 typedef

功能:typedef 用来定义类型的别名,方便使用。

#define 不仅可以为类型取别名,还可以定义常量、变量、编译开关等。

作用域:#define 没有作用域的限制,只要是之前预定义过的,在以后的程序中都可以使用,而 typedef 有自己的作用域

指针的操作:typedef #define 在处理指针时不完全一样。

#include 
#define INTPTR1 int *
typedef int * INTPTR2;
 
using namespace std;
 
int main()
{
    INTPTR1 p1, p2; // p1: int *; p2: int
    INTPTR2 p3, p4; // p3: int *; p4: int *
 
    int var = 1;
    const INTPTR1 p5 = &var; // 相当于 const int * p5; 常量指针,即不可以通过 p5 去修改 p5 指向的内容,但是 p5 可以指向其他内容。
    const INTPTR2 p6 = &var; // 相当于 int * const p6; 指针常量,不可使 p6 再指向其他内容。
    
    return 0;
}
 

4.10 用宏实现比较大小,以及两个数中的最小值

#include 
#define MAX(X, Y) ((X)>(Y)?(X):(Y))
#define MIN(X, Y) ((X)<(Y)?(X):(Y))
using namespace std;
 
int main ()
{
    int var1 = 10, var2 = 100;
    cout << MAX(var1, var2) << endl;
    cout << MIN(var1, var2) << endl;
    return 0;
}
/*
程序运行结果:
100
10
*/
 

4.11 inline 作用及使用方法

作用:

inline 是一个关键字,可以用于定义内联函数内联函数,像普通函数一样被调用,但是在调用时并不通过函数调用的机制而是直接在调用点处展开,这样可以大大减少由函数调用带来的开销,从而提高程序的运行效率

使用方法:

内定义成员函数默认是内联函数

在类内定义成员函数,可以不用在函数头部加 inline 关键字,因为编译器会自动将类内定义的函数(构造函数、析构函数、普通成员函数等)声明为内联函数,代码如下:

#include 
using namespace std;
 
class A{
public:
    int var;
    A(int tmp){ 
      var = tmp;
    }    
    void fun(){ 
        cout << var << endl;
    }
};
 
int main()
{    
    return 0;
}

类外定义成员函数,若想定义为内联函数,需用关键字声明

当在类内声明函数,在类外定义函数时,如果想将该函数定义为内联函数,则可以在类内声明时不加 inline 关键字,而在类外定义函数时加上 inline 关键字。

#include 
using namespace std;
 
class A{
public:
    int var;
    A(int tmp){ 
      var = tmp;
    }    
    void fun();
};
 
inline void A::fun(){
    cout << var << endl;
}
 
int main()
{    
    return 0;
}
 

另外,可以在声明函数定义函数的同时加上 inline//也可以只在函数声明时加 inline,而定义函数时不加 inline。只要确保在调用该函数之前把 inline 的信息告知编译器即可。

关于inline的补充

内联函数的作用:

消除函数调用的开销 在内联函数出现之前,程序员通常用 #define 定义一些“函数”来消除调用这些函数的开销。内联函数设计的目的之一,就是取代#define 的这项功能(因为使用 #define 定义的那些“函数”,编译器不会检查其参数的正确性等,而使用 inline 定义的函数,和普通函数一样,可以被编译器检查,这样有利于尽早发现错误)。

去除函数只能定义一次的限制。内联函数可以在头文件中被定义,并被多个 .cpp 文件 include而不会有重定义错误。这也是设计内联函数的主要目的之一。

关于减少函数调用的开销内联函数一定会被编译器在调用点展开吗?

错,inline 只是对编译器的建议,而非命令。编译器可以选择忽视 inline。当程序员定义的 inline 函数包含复杂递归,或者 inlinie 函数本身比较长,编译器一般不会将其展开,而仍然会选择函数调用。

“调用”普通函数时,一定是调用吗?

错,即使是普通函数,编译器也可以选择进行优化,将普通函数在“调用”点展开。 

既然内联函数在编译阶段已经在调用点被展开,那么程序运行时,对应的内存中不包含内联函数的定义,对吗?

错。 首先,如第一点所言,编译器可以选择调用内联函数,而展开内联函数。因此,内存中仍然需要一份内联函数的定义,以供调用。

而且,一致性是所有语言都应该遵守的准则。普通函数可以有指向它的函数指针,那么,内函数也可以有指向它的函数指针,因此,内存中需要一份内联函数的定义,使得这样的函数指针可以存在。

4.12 inline 函数工作原理

内联函数不是在调用时发生控制转移关系,而是在编译阶段将函数体嵌入到每一个调用该函数的语句块中编译器会将程序中出现内联函数的调用表达式用内联函数的函数体来替换。

普通函数是将程序执行转移到被调用函数所存放的内存地址,当函数执行完后,返回到执行此函数前的地方。转移操作需要保护现场,被调函数执行完后,再恢复现场,该过程需要较大的资源开销

4.13 宏定义(define)和内联函数(inline)的区别

内联函数是在编译时展开,而编译预处理时展开;在编译的时候,内联函数直接被嵌入到目标代码中去,而宏只是一个简单的文本替换

内联函数是真正的函数,和普通函数调用的方法一样,在调用点处直接展开,避免了函数的参数压栈操作减少了调用的开销。而宏定义编写较为复杂,常需要增加一些括号来避免歧义。

宏定义只进行文本替换,不会对参数的类型、语句能否正常编译等进行检查。而内联函数是真正的函数,会对参数的类型、函数体内的语句编写是否正确等进行检查

 

#include 
 
#define MAX(a, b) ((a) > (b) ? (a) : (b))
 
using namespace std;
 
inline int fun_max(int a, int b)
{
    return a > b ? a : b;
}
 
int main()
{
    int var = 1;
    cout << MAX(var, 5) << endl;     
    cout << fun_max(var, 0) << endl; 
    return 0;
}
/*
程序运行结果:
5
1
 
*/

4.14 new 的作用?

new C++ 中的关键字,用来动态分配内存空间,实现方式如下:

int *p = new int[5];

4.15 new malloc 如何判断是否申请到内存?

malloc 成功申请到内存,返回指向该内存的指针;分配失败,返回 NULL 指针。

new 内存分配成功,返回该对象类型的指针;分配失败,抛出 bad_alloc 异常。

4.16 delete 实现原理?delete delete[] 的区别?

delete 的实现原理

首先执行该对象所属类的析构函数

进而通过调用 operator delete 标准库函数来释放所占的内存空间

delete delete [] 的区别:

delete 用来释放单个对象所占的空间,只会调用一次析构函数

delete [] 用来释放数组空间,会对数组中的每个成员都调用一次析构函数

 

4.17 new malloc 的区别,delete free 的区别

在使用的时候 newdelete 搭配使用,mallocfree 搭配使用。

mallocfree 库函数,而newdelete 关键字

-new 申请空间时,无需指定分配空间的大小,编译器会根据类型自行计算malloc 在申请空间时,需要确定所申请空间的大小

new 申请空间时,返回的类型是对象的指针类型,无需强制类型转换,是类型安全的操作符;malloc 申请空间时,返回的是 void* 类型,需要进行强制类型的转换,转换为对象类型的指针。

new 分配失败时,会抛出 bad_alloc 异常,malloc 分配失败时返回空指针

对于自定义的类型,new 首先调用 operator new() 函数申请空间(底层通过 malloc 实现),然后调用构造函数进行初始化,最后返回自定义类型的指针;delete 首先调用析构函数,然后调用 operator delete() 释放空间(底层通过 free 实现)。mallocfree 无法进行自定义类型的对象的构造和析构。

new 操作符从自由存储区上为对象动态分配内存,而 malloc 函数从动态分配内存。(自由存储区不等于堆)

堆是c语言和操作系统的术语,是操作系统维护的一块内存自由存储C++中通过newdelete动态分配释放对象的抽象概念。

4.18 malloc 的原理?malloc 的底层实现?

malloc 的原理:

当开辟的空间小于 128K ,调用 brk() 函数,通过移动 _enddata 来实现;

当开辟空间大于 128K 时,调用 mmap() 函数,通过在虚拟地址空间中开辟一块内存空间来实现。

malloc 的底层实现:

brk() 函数实现原理:向高地址的方向移动指向数据段的高地址的指针 _enddata

mmap 内存映射原理:

进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域

调用内核空间的系统调用函数 mmap()实现文件物理地址进程虚拟地址一一映射关系

进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝

4.19 C C++ struct 的区别?

C 语言中 struct 是用户自定义数据类型;在 C++  struct 抽象数据类型,支持成员函数的定义。

C 语言中 struct 没有访问权限的设置,是一些变量的集合体不能定义成员函数;C++ struct 可以和类一样,有访问权限,并可以定义成员函数

C 语言中 struct 定义的自定义数据类型,在定义该类型的变量时,需要加上 struct 关键字,例如:struct A var;,定义 A 类型的变量;而 C++ 中,不用加该关键字,例如:A var; 

4.20 为什么有了 class 还保留 struct

C++ 是在 C

4.21 struct union 的区别

说明:union 联合体struct 结构体

区别:

联合体结构体都是由若干个数据类型不同的数据成员组成。使用时,联合体只有一个有效的成员;而结构体所有的成员都有效

联合体的不同成员赋值,将会对覆盖其他成员的值,而对于结构体的对不同成员赋值时,相互不影响

联合体大小为其内部所有变量的最大值,按照最大类型的倍数进行分配大小;结构体分配内存的大小遵循内存对齐原则。

语言的基础上发展起来的,为了与 C 语言兼容C++ 中保留了 struct

#include 
using namespace std;
 
typedef union
{
    char c[10];
    char cc1; // char 1 字节,按该类型的倍数分配大小
} u11;
 
typedef union
{
    char c[10];
    int i; // int 4 字节,按该类型的倍数分配大小
} u22;
 
typedef union
{
    char c[10];
    double d; // double 8 字节,按该类型的倍数分配大小
} u33;
 
typedef struct s1
{
    char c;   // 1 字节
    double d; // 1(char)+ 7(内存对齐)+ 8(double)= 16 字节
} s11;
 
typedef struct s2
{
    char c;   // 1 字节
    char cc;  // 1(char)+ 1(char)= 2 字节
    double d; // 2 + 6(内存对齐)+ 8(double)= 16 字节
} s22;
 
typedef struct s3
{
    char c;   // 1 字节
    double d; // 1(char)+ 7(内存对齐)+ 8(double)= 16 字节
    char cc;  // 16 + 1(char)+ 7(内存对齐)= 24 字节
} s33;
 
int main()
{
    cout << sizeof(u11) << endl; // 10
    cout << sizeof(u22) << endl; // 12
    cout << sizeof(u33) << endl; // 16
    cout << sizeof(s11) << endl; // 16
    cout << sizeof(s22) << endl; // 16
    cout << sizeof(s33) << endl; // 24
 
    cout << sizeof(int) << endl;    // 4
    cout << sizeof(double) << endl; // 8
    return 0;
}

4.22 class struct 的异同

struct class 都可以自定义数据类型,也支持继承操作。

struct 中默认的访问级别是 public,默认的继承级别也是 publicclass 中默认的访问级别是 private,默认的继承级别也是 private 

class 继承 struct 或者 struct 继承 class 时,默认的继承级别取决于 class  struct 本身, classprivate 继承),structpublic 继承),即取决于派生类的默认继承级别。

struct A{}

class B : A{}; // private 继承

struct C : B{}// public 继承

4.23 volatile 的作用?是否具有原子性,对编译器有什么影响?

volatile 的作用:当对象的值可能在程序的控制或检测之外被改变时,应该将该对象声明为 violatile,告知编译器不应对这样的对象进行优化

volatile不具有原子性。

volatile 对编译器的影响:使用该关键字后,编译器不会对相应的对象进行优化,即不会将变量从内存缓存到寄存器中,防止多个线程有可能使用内存中的变量,有可能使用寄存器中的变量,从而导致程序错误

4.24 什么情况下一定要用 volatile, 能否和 const 一起使用?

使用 volatile 关键字的场景:

当多个线程都会用到某一变量,并且该变量的有可能发生改变时,需要用 volatile 关键字对该变量进行修饰

中断服务程序中访问的变量或并行设备的硬件寄存器的变量,最好用 volatile 关键字修饰。

volatile 关键字和 const 关键字可以同时使用,某种类型可以既是 volatile 又是 const ,同时具有二者的属性。

C++多线程中volatile不具有原子性;无法对代码重新排序实施限制。

能干什么:告诉编译器不要在此内存上做任何优化。如果对内存有只写未读的等非常规操作,如

x=10;

x=20;

编译器会优化为:

 x=20;

volatile 就是阻止编译器进行此类优化。

4.25 extern C 的作用?

当 C++ 程序 需要调用 C 语言编写的函数,C++ 使用链接指示,即 extern "C" 指出任意非 C++ 函数所用的语言。

// 可能出现在 C++ 头文件中的链接指示

extern "C"{

    int strcmp(const char*, const char*);

}

C++ C语言编译函数签名方式不一样, extern关键字可以让两者保持统一,这样才能找到对应的函数。

4.26 extern static 的区别,什么情况用前者什么情况用后者

1extern外部变量:它属于变量声明extern int aint a区别就是,前者告诉编译器,有一个int类型的变量a定义在其他地方,如果有调用请去其他文件中查找定义

 

2static静态变量:简单说就是在函数等调用结束后,该变量也不会被释放,保存的值还保留。即它的生存期是永久的,直到程序运行结束,系统才会释放,但也无需手动释放

4.27 memcpy 函数的底层原理?

memcpy函数是C和C++标准库中的一个内存操作函数,用于将一个内存区域的内容复制到另一个内存区域。其底层原理主要包括以下几个方面:
内存地址处理
`memcpy`函数通过指针操作内存地址,使用`void*`类型的指针来接收源地址和目标地址,这样可以处理任意类型的数据。
 未对齐处理
-对于未对齐的数据,`memcpy`函数可能需要先处理头部和尾部未对齐的字节,确保中间的复制过程是对齐的,以利用硬件指令的优势。
 内存重叠处理
当源地址和目标地址存在重叠时,`memcpy`函数的行为是未定义的。这是因为`memcpy`假设源和目标之间没有依赖关系,因此没有做重叠处理。在这种情况下,应该使用`memmove`函数,它能够正确处理内存重叠的情况。
流水线和并行执行
利用CPU的流水线能力和多指令并行执行复制操作,提高整体效率。
综上所述,`memcpy`函数通过多种优化手段,实现了高效的内存复制操作。在实际使用中,应该根据具体的需求和平台特性选择合适的复制策略,以达到最佳的性能。

4.28 strcpy 函数有什么缺陷?

strcpy 函数的缺陷strcpy 函数不检查目的缓冲区的大小边界,而是将源字符串逐一的全部赋值给目的字符串地址起始的一块连续的内存空间,同时加上字符串终止符,会导致其他变量被覆盖。

4 .29strcpymemcpy的区别

strcpymemcpy都是在C语言和C++语言中用于复制内存块的函数,但它们在使用和效率上有所不同。

strcpy用于将一个以null结尾的字符串从源地址复制到目标地址。它会复制整个字符串,包括null终止符,直到遇到null为止。如果源字符串长度超过目标地址所分配的内存空间,则会导致内存越界缓冲区溢出问题。

memcpy用于将一段内存块从源地址复制到目标地址,可以复制任意长度的内存块,而不仅限于字符串memcpy不会关心内存块中是否有null终止符,而只是按照给定的长度复制内存块。因此,使用memcpy时需要确保目标地址足够的内存空间,否则也会导致缓冲区溢出问题

在效率方面,memcpy通常比strcpy更快,因为它不需要扫描整个字符串来查找null终止符。另外,memcpy也可以进行一些优化,例如使用字长操作来提高复制速度。但是,由于strcpy具有更简单的语法更高的可读性,因此在处理字符串时,通常首选strcpy函数。

4.30 auto 类型推导的原理

auto 类型推导的原理:编译器根据初始值来推算变量的类型,要求用 auto 定义变量时必须有初始值。编译器推断出来的 auto 类型有时和初始值类型并不完全一样,编译器会适当改变结果类型使其更符合初始化规则。

auto变量的规则是"做函数模板需要做的事情"

4.31sizeof(1==1) 在 C 和 C++ 中分别是什么结果?

C语言:sizeof1 == 1=== sizeof1按照整数处理,所以是4字节,这里也有可能是8字节(看操作系统)

C++:因为有bool 类型

sizeof1 == 1== sizeoftrue 按照bool类型处理,所以是1个字节 

C语言没有布尔类型,因此按整数

C++布尔类型,占1字节

你可能感兴趣的:(嵌入式学习资料,笔记,c++,算法)