本文内容主要基于公众号拓跋阿秀
整理
11. 宏定义和typedef区别?
- 宏主要用于定义常量及书写复杂的内容;typedef主要用于定义类型别名。
- 宏替换发生在编译阶段之前,属于文本插入替换;typedef是编译的一部分。
- 宏不检查类型;typedef会检查数据类型。
- 宏不是语句,不在在最后加分号;typedef是语句,要加分号标识结束。
- 注意对指针的操作,
typedef char * p_char;
和#define p_char char *
区别巨大。
注意:
char* pa, pb; // 声明了一个指向字符变量的指针,和一个字符变量;
//以下则可行:
typedef char* PCHAR;
PCHAR pa, pb;
这种用法很有用,特别是char* pa, pb的定义,初学者往往认为是定义了两个字符型指针,其实不是,而用typedef char* PCHAR就不会出现这样的问题,减少了错误的发生。
typedef与宏定义区别
typedef(自定义数据类型)与#define(宏定义)用法比较
12. 变量声明和定义区别?
- 声明仅仅是把变量的声明的位置及类型提供给编译器,并不分配内存空间;定义要在定义的地方为其分配存储空间。
- 相同变量可以在多处声明(外部变量extern),但只能在一处定义。
//变量声明和定义
extern int i; //声明i而非定义i
int j;//声明并定义j
extern int i = 3;//定义,非声明
//函数声明和定义
extern double max(double d1,double d2); //声明
double max(double d1,double d2){}//定义
13. 哪几种情况必须用到初始化成员列表?
- 初始化一个const成员。
- 初始化一个引用成员。
- 调用一个基类的构造函数,而该函数有一组参数。
- 调用一个数据成员对象的构造函数,而该函数有一组参数。
代码举例分析C++必须使用【初始化列表】初始化数据成员的三种情况
14. strlen和sizeof区别?
- sizeof是运算符,并不是函数,结果在编译时得到而非运行中获得;strlen是字符处理的库函数。
- sizeof参数可以是任何数据的类型或者数据(sizeof参数不退化);strlen的参数只能是字符指针且结尾是’\0’的字符串。
- 因为sizeof值在编译时确定,所以不能用来得到动态分配(运行时分配)存储空间的大小。
int main(int argc, char const *argv[]){
const char* str = "name";
sizeof(str); // 取的是指针str的长度,是8
strlen(str); // 取的是这个字符串的长度,不包含结尾的 \0。大小是4
return 0;
}
15. 常量指针和指针常量区别?
- 常量指针是一个指针,读成常量的指针,指向一个只读变量。如int const *p或const int *p。
- 指针常量是一个不能给改变指向的指针。指针是个常量,不能中途改变指向,如int *const p。
常量指针:
又叫常指针,可以理解为常量的指针,也即这个是指针,但指向的是个常量,这个常量是指针的值(地址),而不是地址指向的值。
关键点:
1.常量指针指向的对象不能通过这个指针来修改,可是仍然可以通过原来的声明修改;
2.常量指针可以被赋值为变量的地址,之所以叫常量指针,是限制了通过这个指针修改变量的值;
3.指针还可以指向别处,因为指针本身只是个变量,可以指向任意地址;
指针常量
定义:
本质是一个常量,而用指针修饰它。指针常量的值是指针,这个值因为是常量,所以不能被赋值。
关键点:
1.它是个常量!
2.指针所保存的地址可以改变,然而指针所指向的值却不可以改变;
3.指针本身是常量,指向的地址不可以变化,但是指向的地址所对应的内容可以变化;
常量指针
//常量指针: 值是常量,值不能改变,指向一个只读变量
//在定义时不需要对它进行初始化,如果需要的话,允许给cptr重新赋值,使其指向另一个const对象。但不能通过cptr修改其所指对象的值:
const double *cptr;
*cptr=40;//报错,*cptr应该是const类型(指向对象值不能变)
//把一个const对象的地址赋给一个普通的,非const对象的指针也会导致编译时的错误:
const double pi=3.14;
double *ptr=π//报错:ptr是一个普通指针
const double *cptr=π//OK: cptr是一个指向const对象的指针
//也不能使用void指针保存const对象的地址,而必须使用const void类型的指针保存const对象的地址:
const int universe=40;
const void *cpv=&universe;//OK
void *pv=&universe;//报错:universe是const常量
//允许把非const对象的地址赋给指向const对象的指针,例如:
double dval=3.14;
const double *cptr=&dval;//OK:但是不能通过cptr来修改dval的值
//然而,事实是可以修改const指针所指向的值,这一点尝尝容易引起误会。
double dval=3.14159;
const double *cptr=3.14159;//报错:cptr是指向const对象的指针
const double *cptr=&dval;//OK: 允许把非cosnt对象的地址赋给指向const对象的指针
double *ptr=&dval; //OK:ptr指向dval
*ptr=2.72;//OK: ptr是普通指针可以修改
cout<<*cptr<<endl;//OK:输出2.72
//不能保证指向const对象指针所指对象的值一定不可以修改。
指针常量
//指针常量: 不能给改变指向的指针,可改变指向对象的值
int errNumb=0;
int *const curErr=&errNumb;//curErr是指向int型对象的指针常量(const指针)
//与其他const量一样,const指针的值不能修改,这就意味着不能使curErr指向其他对象。任何企图给const指针赋值的行为都会导致编译时的错误。并且与任何const量一样,const指针也必须在定义时初始化。
//当然尽管指针本身是const的事实并没有说明是否能使用该指针修改它所指向对象的值。指针所指对象的值能否修改完全取决于该对象的类型。例如curErr指向一个普通的非常量int型对象errNumb,则可使用curErr修改该对象的值:
if(*curErr){
*curErr=10;//OK: 此时errNumb值被修改为10
}
[c++]常量指针和指针常量区别
C++中指针常量和常量指针的区别
16. a和&a有什么区别?
int a[10];
int (*p)[10] = &a;
- a是数组名,是数组首元素地址,+1表示地址值加上一个int类型的大小,如果a的值是0x00000001,加1操作后变为0x00000005。*(a + 1) = a[1]。
- &a是数组的指针,其类型为int (*)[10] (就是前面提到的数组指针),其加1时,系统会认为是数组首地址加上整个数组的偏移(10个int型变量),值为数组a尾元素后一个元素的地址。
- 若(int *)p ,此时输出 *p时,其值为a[0]的值,因为被转为int *类型,解引用时按照int类型大小来读取。
17. 数组名和指针(这里为指向数组首元素的指针)区别?
- 二者均可通过增减偏移量来访问数组中的元素。
- 数组名不是真正意义上的指针,可以理解为常指针,所以数组名没有自增、自减等操作。
- 当数组名当做形参传递给调用函数后,就失去了原有特性,退化成一般指针,多了自增、自减操作,但sizeof运算符不能再得到原数组的大小了。
18. 野指针和悬空指针
都是是指向无效内存区域(这里的无效指的是"不安全不可控")的指针,访问行为将会导致未定义行为。
野指针,指的是没有被初始化过的指针
int main(void) {
// 未初始化
int* p;
std::cout<< *p << std::endl; // 未初始化就被使用
return 0;
}
因此,为了防止出错,对于指针初始化时都是赋值为 nullptr ,这样在使用时编译器就会直接报错,产生非法内存访问。
悬空指针,指针最初指向的内存已经被释放了的一种指针。
int main(void) {
int* p = nullptr;
int* p2 = new int;
p = p2;
delete p2;
}
此时 p和p2就是悬空指针,指向的内存已经被释放。继续使用这两个指针,行为不可预料。需要设置为 p=p2=nullptr 。此时再使用,编译器会直接保错。
避免野指针比较简单,但悬空指针比较麻烦。c++引入了智能指针,C++智能指针的本质就是避免悬空指针的产生。
产生原因及解决办法:
野指针:指针变量未及时初始化 => 定义指针变量及时初始化,要么置空。
悬空指针:指针free或delete之后没有及时置空 => 释放操作后立即置空。
19. 迭代器失效的情况
以vector为例:
插入元素:
1.尾后插入:size < capacity时,首迭代器不失效尾迭代失效(未重新分配空间),size == capacity时,所有迭代器均失效(需要重新分配空间)。
2.中间插入:中间插入:size < capacity时,首迭代器不失效但插入元素之后所有迭代器失效,size == capacity时,所有迭代器均失效。
删除元素:
尾后删除:只有尾迭代失效。
中间删除:删除位置之后所有迭代失效。
deque 和 vector 的情况类似,而list双向链表每一个节点内存不连续, 删除节点仅当前迭代器失效,erase返回下一个有效迭代器;
map/set等关联容器底层是红黑树删除节点不会影响其他节点的迭代器, 使用递增方法获取下一个迭代器 mmp.erase(iter++);
unordered_(hash) 迭代器意义不大, rehash之后, 迭代器应该也是全部失效.
20. C和C++的区别
- C++中new和delete是对内存分配的运算符,取代了C中的malloc和free。
- 标准C++中的字符串类取代了标准C函数库头文件中的字符数组处理函数(C中没有字符串类型)。
- C++中用来做控制态输入输出的iostream类库替代了标准C中的stdio函数库。
- C++中的try/catch/throw异常处理机制取代了标准C中的setjmp()和longjmp()函数。
- 在C++中,允许有相同的函数名,不过它们的参数类型不能完全相同,这样这些函数就可以相互区别开来。而这在C语言中是不允许的。也就是C++可以重载,C语言不允许。
- C++语言中,允许变量定义语句在程序中的任何地方,只要在是使用它之前就可以;而C语言中,必须要在函数开头部分。而且C++允许重复定义变量,C语言也是做不到这一点的
- 在C++中,除了值和指针之外,新增了引用。引用型变量是其他变量的一个别名,我们可以认为他们只是名字不相同,其他都是相同的。
- C++相对与C增加了一些关键字,如:bool、using、dynamic_cast、namespace等等
牛客网回答
设计思想上:
C++是面向对象的语言,而C是面向过程的结构化编程语言
语法上:
C++具有封装、继承和多态三种特性
C++相比C,增加多许多类型安全的功能,比如强制类型转换、
C++支持范式编程,比如模板类、函数模板等
C语言与C++有什么区别?
C语言和C++的区别