C++基础知识2[宏定义, 变量声明, 野指针和悬空指针]

本文内容主要基于公众号拓跋阿秀整理

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++的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值