栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > C/C++/C#

CC++面试知识点总结

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

CC++面试知识点总结

目录

1、关键字

1.1C语言宏中的#和##

1.2关键词volatile的使用场景

1.3关键词static的作用

1.4extern关键字(声明)

1.5 const 作用

1.6 const使用场景

1.7new/delete和malloc/free的区别

1.8 strlen("")=?0 sizeof("")=?2

1.9 struct 与 union 的区别是什么:结构体和联合体区别

1.10左值和右值

1.11 短路求值

1.12 ++a和a++运算

2.内存

2.1C语言内存分配的方式

2.2堆和栈区别

2.3栈的用处

2.4 C++内存管理

2.5内存泄漏

3.指针

3.1数组指针和指针数组

3.2函数指针和指针函数

3.3数组名与指针

3.4指针常量 常量指针,指向常量的常量指针

 3.5指针和引用区别

3.6野指针

3.7C++中智能指针

4 预处理

4.1 预处理标识符#error

4.2define和const

4.3typedef 和define区别

4.4#include和#include “filename.h”

 4.5头文件作用

4.6 头文件中是否可以定义静态变量

5、变量

5.1全局变量和局部变量区别

5.2 全局变量是否可以被定义在多个.c文件包含的头文件中,为什么?

5.3局部变量是否可以和全局变量重名

6、函数

6.1怎么可以让函数在main函数之前运行

6.2为什么析构函数必须是虚函数


1、关键字

1.1C语言宏中的#和##

#可以将宏定义中的传入参数名转换为双引号括起来的参数名字符串,必须置于宏定义的参数名前
##是将宏定义的多个形参转换成一个实际的参数名

#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 = %sn", var1_p);//var1=var2
    printf("var2 = %sn", var2_p);//var2=var1
        return 0;
}  

1.2关键词volatile的使用场景

volatile(直接存取原始内存地址)(避免对应的变量值在程序不知道的情况下发生改变)

使用场景:

1.并行设备的硬件寄存器(状态寄存器)

2.中断服务子程序会访问的变量

3.多线程应用中被几个任务共享的变量

1.3关键词static的作用

作用:更改函数与变量的作用域,只在内存中分配一次;

1、在函数体内,一个被声明的静态局部变量在函数反复调用时保持值不变

2、模块内,函数体外,一个被声明的全局静态变量只能被模块内函数访问

3、模块内的静态函数可以被模块内的其他函数使用

1.4extern关键字(声明)

1、变量声明情况分类

int i ;声明+定位  需要存储空间
extern int i; 声明 + 定义 不需要内存空间

2、在C语言中,修饰符extern用在变量和函数的声明之前,用来说明此变量/函数是在别处定位的,需要在此处引用。     extern 的应用方式比包含头文件方便很多,想用哪个函数就extern哪个函数,加速编译过程。

3、extern “C” 指示编译器这部分代码按C语言规则编译。

#ifndef __INCvxWorksh 
#define __INCvxWorksh
#ifdef __cplusplus//告诉编译器,这部分代码按C语言的格式进行编译,而不是C++的
extern "C"{
#endif
 

 
#ifdef __cplusplus
}
 
#endif
#endif 

1.5 const 作用

1、定义变量为常量,必须初始化

2、修饰函数的参数,表示在函数体内不能修改这个参数的值

3、修饰函数的返回值(只用于修饰指针变量,一般都是修饰内容不变)

4、相比与宏,const可以避免不必要的内存分配,宏在编译时每次替换都会分配内存空间,const只分配一次。

1.6 const使用场景

2、修饰常数组、常指针、常对象(对象常量、不能被更新)

3、修饰形参

4、修饰函数返回值

1.7new/delete和malloc/free的区别

1.new/delete是c++中的操作符,malloc/free是库函数

2.new和delete对应于构造函数和析构函数,,malloc和free是内存分配

3.new返回指定类型的指针,并且可以自动计算申请内存的大小。而 malloc需要我们计算申请内存的大小,并且在返回时强行转换为实际类型的指针

1.8 strlen("")=?0 sizeof("")=?2

1、strlen是库函数,用于计算字符串长度(),遇到结束。sizeof是关键字,以字节形式给出操作数存储大小。

2、strlen在运行期计算,sizeof在编译期计算。

不用sizeof如何求 int 占用的字节数

#include 
#define Mysizeof(Value) (char*)(&Value+1)-(char*)&value

int main()
{

    int i;
    double f;
    double *q;
    printf("%drn",Mysizeof(i));
    printf("%drn",Mysizeof(f));
    printf("%drn",Mysizeof(a));
    printf("%drn",Mysizeof(q));
    return 0 ;
}

4  8  32  4

(char*)&Value 返回Value的地址的第一个字节,(char*)(&Value+1)返回value 的地址的下一个地址的第一个字节。

不用sizeof求电脑的位数(只需要求两个char指针之间的宽度)

#include 
using namespace std;

int main() {
	void* a;
	void* b;
	int scope = (char*)&a - (char*)&b;
	cout << "&a:" << &a << endl;
	cout << "&b:" << &b << endl;
	cout << "scope:" << scope << endl;
	if(scope==8) {
		cout << "64bits" << endl;
	} else {
		cout << "32bits" << endl;
	}
}

1.9 struct 与 union 的区别是什么:结构体和联合体区别

struct 和union是两个不同的复合结构,区别在于:

1、联合体共用一块地址空间,一个联合体变量的长度等于最长的成员长度;结构体不同成员放在不同的地址中,占用空间是累加的(考虑空间对齐);

2、对联合体不同成员赋值将会对他的其他成员重写,原来的成员的值就不存在了。对结构体的不同成员赋值是互不影响的。

1.10左值和右值

左值可写,右值可读;

1.11 短路求值

提前判断(逻辑与/逻辑或)

#include 
int main()
{
 int i = 6;
 int j = 1;
 if(i>0||(j++)>0);//提前判断
 printf("%Drn",j);
 return 0; }

1.12 ++a和a++运算
int temp = a;
a = a+1;
return temp;
++a的过程
a = a + 1;
return a ;

2.内存

2.1C语言内存分配的方式

栈:函数调用和使用,局部变量;

堆:内存的动态申请和归还 malloc/free

静态数据区:保存局部变量和静态变量

2.2堆和栈区别

1、申请方式(栈上OS自动分配/释放   堆上手动)

2、申请大小的限制(栈小,堆大)

3、地址的生长方向(栈溢出可能会导致栈顶的数据发生覆盖)

4、申请效率(栈快 , 堆慢)

2.3栈的用处

1、存储临时变量和函数参数

2、多线程编程的基石,每个线程都有专属的栈,中断和异常处理也有栈;

2.4 压栈顺序

从右往左

int p;
printf("%d %d  %dn",p++,++p,p++)



2  2  0

2.4 C++内存管理

C++中的虚拟内存分为 代码段,数据段,BSS段,堆区,文件映射区,以及栈区;

代码段:包括只读存储区(存储字符串常量)和文本区(机器代码)

数据段:存储程序中已经初始化的全局变量和静态变量

BSS段:存储未初始化的全局变量和所有被初始化为0的全局变量和静态变量

堆区:调用new/malloc函数在堆区动态分配内存,调用delete和free释放内存

映射区:存储动态链接库

栈:存储函数的返回地址,参数,局部变量,返回值

2.5内存泄漏

内存泄漏就是指申请了一块内存,但是使用完以后没有释放掉

3.指针

3.1数组指针和指针数组

数组指针是指向数组的指针  

//声明了一个指针,该指针指向了一个有9个int型元素的数组

int (*pa)[9]

指针数组是指一个数组,数组中的元素是指针(其实就是相当于数组元素是地址,解引用之后可以得到地址中的值)

3.2函数指针和指针函数

函数指针是一个指针,该指针指向一个函数

int(*p)(int, int);
# include 
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 = %dnb = %dnmax = %dn", 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 
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.2ft",*(p+i));
  
     return 0;
 }
float *find(float(*pionter)[4],int n)
 {
     float *pt;
     pt=*(pionter+n);
     return(pt);
 }

3.3数组名与指针

1.数据保存方面
指针保存的是地址,内存访问偏移量为四个字节,无论其中保存的是何种数据,都是以地址类型进行解析
数组保存的数据,数组名表示的是第一个元素的地址,内存偏移量是保存数据类型的内存偏移量,只有对数组名取地址时,数组名才表示整个数组, 内存访问偏移量为整个数组的大小(sizeof(数组名))
2.数据访问方面
指针是间接访问,使用解引用,*
数组对数据访问是直接访问,通过下标或者数组名加偏移量
3.使用环境
指针多用于动态数据结构,(链表等)和动态内存开辟
数组用于存储固定个数且类型统一的数据结构和隐式分配


3.4指针常量 常量指针,指向常量的常量指针

常量指针:

int const *p = &a;
const int *p = &a;

指针指向的是个常量,指针指向的值就不能被改变

理解如下:

指针常量:指针本身是常量,指向一个确定存在的地方。

int * const p;

int a = 0, b = 0;
int *const p = &a;
*p = 1;//正确,可以改初值
*p = &b;//错误,不可改地址

指向常量的指针常量(地址不可变,值不可变)

int a = 0, b = 0;
const int *const p = &a;
*p = 1;//不可以修改初值
*p = &b;//不可以改地址

 3.5指针和引用区别

同:都是地址的概念,从内存分配上来看,两者都是占用内存的
不同:
1.指针是实体,引用是别名
2.引用只能初始化一次,不能为空,之后不可变,指针可变可空
3.sizeof得到的引用是指向的变量(对象)的大小,指针是指针本身的大小

转换

指针转引用:把指针用*就可以转换成对象,可以用在引用参数中

引用转指针:把引用类型的对象用&取地址就获得指针

int a = 5;
int *p = &a;
void fun (int &x){}

3.6野指针

1.野指针指向不可用内存,创建时没有初始化会随机指向
2.free或delete时,没有将指针指向null,因为只释放了内存
3.指针超过了变量的作用范围
 

避免:

初始化,使用完进行null赋值,
malloc函数分配完内存后需注意:
a. 检查是否分配成功(若分配成功,返回内存的首地址;分配不成功,返回NULL。可以通过if语句来判断)
b. 清空内存中的数据(malloc分配的空间里可能存在垃圾值,用memset或bzero 函数清空内存)

3.7C++中智能指针

智能指针是指向动态对象的指针类,主要有,
unique_ptr C++11引入用来替代auto_ptr,解决不安全问题(auto有拷贝语义,再次访问原对象,会程序崩溃,而unique提供了移动语义)不共享所管理的对象,
shared_ptr 是共享指针,允许多个指针指向同一个对象,除了包括指向对象的指针,还必须包括一个引用计数代理对象的指针
weak_ptr 是配合shared_ptr使用的,观测资源的引用计数,可以用于打破循环引用(比如两个类互相引用),解决内存泄漏的问题
weak_ptr不会增加引用计数,是不能直接访问对象的,访问可以通过lock()函数来创建shared_ptr来引用,shared_ptr能够保证在 shared_ptr 没有被释放之前,其所管理的资源是不会被释放的


4 预处理

4.1 预处理标识符#error

#error预处理指令作用是在编译程序时,只要遇到#error就会生成一个编译错误提示信息,并且停止编译。语法格式为#error error-message。

4.2define和const

define是做文本替换,
1.内存区域不同:define常量的生命周期在编译期,不分配内存,存在于代码段,const常量存在于数据段,并在堆栈中分配空间,可以被调用传递
2.数据类型不同,define常量没有数据类型,const实际存在,并且可以编译器检查
3.define替换方便,但容易出错,const可读性强,便于维护和调试。

4.3typedef 和define区别

两者都是替一个对象取别名,区别在于

1、原理不同;#define是预处理指令,在预处理阶段做简单的字符串替换,没有正确性检查

                        typedef是关键字,在编译时处理。有正确性检查

#define Pi 3.1415926

typedef int INTEGER
INTEGER a,b;

2、功能不同  typedef用于定义类型的别名

                        #define不只是可以为对象取别名,还可以定义常量/变量,编译开关等

3、作用域不同        #define没有作用域的限制,之前定义过的之后都可以使用

                                typedef有自己的定义域

4、对指针的操作不同   #define是没有任何意义的替换,仅为字符串替换

                                        typedef有一定含义

#define INTPTR1 int*
typedef int* INTPTR2;

INTPTR1 p1,p2;    ->   int* P1,p2;
INTPTR2 p3,p4;    ->   int* P3,*p4;

4.4#include和#include “filename.h”

#include是从标准库路径开始搜索

#include“filename.h”是先从工作路径开始搜索,再从标准库路径开始搜索

 4.5头文件作用

1、调用库功能

2、检查类型安全(报错)

4.6 头文件中是否可以定义静态变量

头文件中尽量不要定义变量,会造成空间浪费和程序错误

宏输入两个参数并返回较小的一个
#define MIN(A,B) ((A)<=(B)?(A):(B))

5、变量

5.1全局变量和局部变量区别

1、作用域不同

2、存储方式不同:全局变量(静态全局变量,静态局部变量)分配在全局数据区(静态存储区),后者分配在栈区

3、生命周期不同

4、使用方式不同(全局变量extern就行)

5.2 全局变量是否可以被定义在多个.c文件包含的头文件中,为什么?

可以,在不同的C文件中以static形式来声明同名全局变量

可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件对此变量赋初值

5.3局部变量是否可以和全局变量重名

能,局部会屏蔽全局

在函数内部引用这个变量时,会用局部变量

6、函数

6.1怎么可以让函数在main函数之前运行

attribute可以设置函数属性(FUnction Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)

#include 
void before() _attribute_((constructor));
void after() _attribute_((destructor));

void before(){
    printf("this is function %sn",_func_);
    return;
}

void after(){
    printf("this is function %sn",_func_);
    return;
}

int main(){

printf("this is function %sn",_func_);
    return 0;

}


//输出结果
//this is function before
//this is function main
//this is function after

6.2为什么析构函数必须是虚函数

转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1037333.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号