C/C++面试知识点总结
1.关键字
(1)C语言中的#和##
#可以将宏定义中的传入参数名转换为双引号括起来的参数名字符串,必须置于宏定义的参数名前
##是将宏定义的多个形参转换成一个实际的参数名
#define STRCPY(a, b) strcpy(a ## _p, #b)
int main()
{
char var1_p[20];
char var2_p[30];
strcpy(var1_p, "aaaa");
strcpy(var2_p, "bbbb");
STRCPY(var1, var2);//转换为strcpy(var1_p,"var2")
STRCPY(var2, var1);
printf("var1 = %s\n", var1_p);//var1=var2
printf("var2 = %s\n", var2_p);//var2=var1
return 0;
}
(2)关键词volatile的使用场景
1.并行设备的硬件寄存器,因为存储器映射的硬件寄存器随时可能被外设硬件修改,声明指向设备寄存器的指针时要用volatile,防止编译器对这个地址的数据假设。
2.一个中断服务程序中修改的供其他程序检测的变量,保证编译器每次存储变量,直接从变量的内存地址进行读取
3.多线程应用被几个任务共享的变量,
(3)关键词static的作用
总的来说,static能够更改变量和函数的作用域,只在内存中分配一次,分情况讨论如下
在函数体内,一个被声明的静态局部变量在函数反复调用维持值不变
在模块内,在函数体外,一个被声明的静态全局变量,只能被模块内的函数访问
在模块内的静态函数,只能被模块内被其他函数调用
(4)extern作用
extern可以对变量做外部全局变量说明,即使全局变量的定义在调用处的后面。
extern ”C“可以告诉编译器将这部分代码按照c代码进行编译,而不是c++
#ifndef __INCvxWorksh /*防止该头文件被重复引用*/
#define __INCvxWorksh
#ifdef __cplusplus//告诉编译器,这部分代码按C语言的格式进行编译,而不是C++的
extern "C"{
#endif
/*…*/
#ifdef __cplusplus
}
#endif
#endif /*end of __INCvxWorksh*/
(5)const的作用
1.定义变量为常量
2.修饰函数的参数,表示在函数体内不能修改这个参数的值
3.修饰函数的返回值(只用来修饰指针变量,一般都是修饰内容不变
const char GetString() //定义一个函数
char *str= GetString() //错误,因为str没有被 const修饰
const char *str=GetString() //正确
4.相比宏,const可以避免不必要的内存分配。宏在编译时每次替换都会分配内存空间,const只分配一次
#define PI 3.14159//该宏用来定义常量
const doulbe Pi=3.14159//此时并未将P放入只读存储器中
double i=Pi//此时为Pi分配内存,以后不再分配
double I=PI//编译期间进行宏替换,分配内存
double j=Pi//没有内存分配再次进行宏替换,又一次分配内存
(6)什么时候使用const
1.一般变量
2.修饰常数组
3.修饰常对象,对象常量,(对象不能被更新)
4.修饰常指针(指针常量即引用,常量指针)
5.修饰形参
6.修饰函数返回值
7.在另一个连接文件中引用const常量
(7)new/delete和malloc/free的区别
1.new/delete是c++中的操作符,malloc/free是库函数
2.new和delete对应于构造函数和析构函数,,malloc和free是内存分配
3.new返回指定类型的指针,并且可以自动计算申请内存的大小。而 malloc需要我们计算申请内存的大小,并且在返回时强行转换为实际类型的指针
(8)左值和右值的概念
左值可写,右值可读
(9)短路求值
提前判断如
#include <stdio.h>
int main()
{
int i = 6;
int j = 1;
if(i>0||(j++)>0);//提前判断
printf("%D\r\n",j);
return 0; }
2.内存
(1)堆和栈的区别
1.申请方式(手动还是自动)
2.申请大小的限制
3.地址的生长方向(栈溢出可能会导致栈顶处的数据发生覆盖)
4.申请效率
(2)函数指针和指针函数
函数指针是一个指针,这个指针用来指向函数
定义方式为:
int(*p)(int, int);
使用如下:
# include <stdio.h>
int Max(int, int); //函数声明
int main(void) {
int(*p)(int, int); //定义一个函数指针
int a, b, c;
p = Max; //把函数Max赋给指针变量p, 使p指向Max函数
printf("please enter a and b:");
scanf("%d%d", &a, &b);
c = (*p)(a, b); //通过函数指针调用Max函数
printf("a = %d\nb = %d\nmax = %d\n", a, b, c);
return 0; }
int Max(int x, int y) //定义Max函数
{
int z;
if (x > y)
{
z = x;
}
else
{
z = y;
}
return z;
}
指针函数是一个返回值为指针类型的函数
定义为:
int *pfun(int, int);
使用方式为‘
#include <stdio.h>
float *find(float(*pionter)[4],int n);//函数声明
int main(void)
{
static float score[][4]={{60,70,80,90},{56,89,34,45},{34,23,56,45}};
float *p;
int i,m;
printf("Enter the number to be found:");
scanf("%d",&m);
printf("the score of NO.%d are:\n",m);
p=find(score,m-1);
for(i=0;i<4;i++)
printf("%5.2f\t",*(p+i));
return 0;
}
float *find(float(*pionter)[4],int n)/*定义指针函数*/
{
float *pt;
pt=*(pionter+n);
return(pt);
}
数组名和指针的区别
1.数据保存方面
指针保存的是地址,内存访问偏移量为四个字节,无论其中保存的是何种数据,都是以地址类型进行解析
数组保存的数据,数组名表示的是第一个元素的地址,内存偏移量是保存数据类型的内存偏移量,只有对数组名取地址时,数组名才表示整个数组, 内存访问偏移量为整个数组的大小(sizeof(数组名))
2.数据访问方面
指针是间接访问,使用解引用,*
数组对数据访问是直接访问,通过下标或者数组名加偏移量
3.使用环境
指针多用于动态数据结构,(链表等)和动态内存开辟
数组用于存储固定个数且类型统一的数据结构和隐式分配
指针和引用的区别
相同:都是地址的概念,从内存分配上来看,两者都是占用内存的
不同:
1.指针是实体,引用是别名
2.引用只能初始化一次,不能为空,之后不可变,指针可变可空
3.sizeof得到的引用是指向的变量(对象)的大小,指针是指针本身的大小
野指针的产生及如何避免
1.野指针指向不可用内存,创建时没有初始化会随机指向
2.free或delete时,没有将指针指向null,因为只释放了内存
3.指针超过了变量的作用范围
避免:初始化,使用完进行null赋值,
malloc函数分配完内存后需注意:
a. 检查是否分配成功(若分配成功,返回内存的首地址;分配不成功,返回NULL。可以通过if语句来判断)
b. 清空内存中的数据(malloc分配的空间里可能存在垃圾值,用memset或bzero 函数清空内存)
智能指针的概念
智能指针是指向动态对象的指针类,主要有,
unique_ptr C++11引入用来替代auto_ptr,解决不安全问题(auto有拷贝语义,再次访问原对象,会程序崩溃,而unique提供了移动语义)不共享所管理的对象,
shared_ptr 是共享指针,允许多个指针指向同一个对象,除了包括指向对象的指针,还必须包括一个引用计数代理对象的指针
weak_ptr 是配合shared_ptr使用的,观测资源的引用计数,可以用于打破循环引用(比如两个类互相引用),解决内存泄漏的问题
weak_ptr不会增加引用计数,是不能直接访问对象的,访问可以通过lock()函数来创建shared_ptr来引用,shared_ptr能够保证在 shared_ptr 没有被释放之前,其所管理的资源是不会被释放的
预处理
define和const的区别
define是做文本替换,
1.内存区域不同:define常量的生命周期在编译期,不分配内存,存在于代码段,const常量存在于数据段,并在堆栈中分配空间,可以被调用传递
2.数据类型不同,define常量没有数据类型,const实际存在,并且可以编译器检查
3.define替换方便,但容易出错,const可读性强,便于维护和调试。
typedef和 define有什么区别
头文件的作用
通过头文件调用库功能,实现多文件编程,更加安全,对接口的实现进行加密。
提供类型安全检查,某个接口被实现和使用的时候,编译器可以指出错误