C/C++面试汇总---C语法篇

1、变量/函数的声明和定义之间有什么区别

变量/函数的声明仅声明变量/函数存在于程序中的某个位置也就是后面程序会知道这个函数或者变量的类型,但不分配内存。
关于定义,当我们定义变量/函数时,除了声明的作用外,它还为该变量/函数分配内存。

2、#include<> 与#include ""的区别?

include<>到系统指定⽬录寻找头⽂件,#include ""先到项⽬所在⽬录寻找头⽂件,如果没有找再到系统指定的⽬录下寻找

3、typdef 和 #define 区别

#define是预处理命令,在预处理是执行简单的替换,不做正确性的检查
typedef是在编译时处理的,它是在自己的作用域内给已经存在的类型一个别名

typedef (int*) pINT;
#define pINT2 int*

pINT a,b;//的效果同int *a; int *b;	表示定义了两个整型指针变量。
pINT2 a,b;//的效果同int *a, b;	表示定义了一个整型指针变量a和整型变量b。

4、定义和声明的区别

声明是告诉编译器变量的类型和名字,不会为变量分配空间
定义需要分配空间,同一个变量可以被声明多次,但是只能被定义一次

5、结构体struct和共同体union(联合)的区别

结构体:将不同类型的数据组合成一个整体,是自定义类型
共同体:不同类型的几个变量共同占用一段内存
1)结构体中的每个成员都有自己独立的地址,它们是同时存在的;
共同体中的所有成员占用同一段内存,它们不能同时存在;
2)sizeof(struct)是内存对齐后所有成员长度的总和,sizeof(union)是内存对齐后最长数据成员的长度、

5.1、结构体为什么要内存对齐呢?

1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
2.硬件原因:经过内存对齐之后,CPU的内存访问速度大大提升。

6、C语言参数传递方式:

void swap1(int,int); //值传递;按值传递则不会修改原值
void swap2(int *p1,int *p2);// 地址OR指针传递函数的主要的作用就是对存储在地址中的变量进行直接的操作
void swap3(int &a,int &b); //引用传递;引用则相当于间接寻址,他直接传递的是地址

7、c语言函数调用过程

一个C语言程序的执行过程可以认为是多个函数之间的相互调用过程,它们形成了一个或简单或复杂的调用链条。这个链条的起点是 main(),终点也是 main()。
总结起来整个过程就三步:
(1)根据调用的函数名找到函数入口;
(2)在栈中申请调用函数中的参数及函数体内定义的变量的内存空间
(3)函数执行完后,释放栈中的申请的参数和变量的空间,最后返回值

8、用变量 a 给出下面的定义

(a) 一个整型数
int a;
(b)一个指向整型数的指针
int *a;
(c)一个指向指针的的指针,它指向的指针是指向一个整型数
int **a;
(d)一个有 10 个整型数的数组
int a[10]
(e)一个有 10 个指针的数组,该指针是指向一个整型数的。
int *a[10]
(f) 一个指向有 10 个整型数数组的指针
int(*a)[10]
(g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数
int *a(int)
(h)一个有 10 个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回-6)个敕形数
int (*a[10])(int)

9、数组相关概念

9.1、数组名作为类型、作为地址、对数组名取地址的 区别?

数组名作为类型:代表的是整个数组的⼤⼩
数组名作为地址:代表的是数组⾸元素的地址
对数组名取地址:代表的是数组的⾸地址

9.2、数组首地址与数组首元素地址

char arr[]={‘1’,‘2’,‘3’,‘4’,‘5’,‘6’};
char * b = arr; //只能写成这样而不能写成 = &arr
arr //获得数组首元素地址等同于&arr[0]

arr、arr[0]、(arr+0) //获得数组首元素值

&arr //获得数组地址
arr+1 //获得数组第二个元素地址
arr[1]、*(arr+1) //获得数组第二个元素值

10、static关键字

全局变量存储在静态存储区中,局部变量在堆栈中

静态局部变量:和普通局部变量不同。静态局部变量也是定义在函数内部的,静态局部变量所在的函数在调用多次时,只有第一次才经历变量定义和初始化,以后多次在调用时不再定义和初始化,而是维持之前上一次调用时执行后这个变量的值。下次接着来使用。但是他的作用域仅限于当前函数内。
静态全局变量也只初始化一次,但是作用域在当前文件/模块中。每次调用当前文件时,会使用上一次保存的值。
静态函数:只在本模块或者文件中使用。被限定了范围。

11、各种指针

11.1、NULL指针

NULL用于指示指针未指向有效位置。理想情况下,如果在声明时不知道指针的值,则应将指针初始化为NULL。
当由它指向的内存在程序中间被释放时,我们应该使指针为NULL。

11.2、悬空指针

悬空指针是没有指向正确内存位置的指针 当删除或释放对象时,如果不修改指针的值或者不置为NULL,就会出现悬空指针。
这时这个指针指向的内存可能被分配给了其他变量就会造成错误。所以是比较危险的。

11.3、野指针

野指针不是NULL指针,是未初始化或者未清零的指针,它指向的内存地址不是程序员所期望的,可能指向了受限的内存。
成因:
(1)指针变量没有被初始化
(2)指针指向的内存被释放了,但是指针没有置NULL
(3)指针超过了变量了的作用范围,比如b[10],指针b+11

11.4、指针常量与常量指针

int * const p //指针常量:指针在前,常量在后
这个p只能指向一个位置,而不能指向其他位置。指向的变量值可以改变。
const int *p = &a; //常量指针 : 常量在前,指针在后
指向的变量值不能改变,但是可以改变这个指针指向的位置。

12、“引用”与指针的区别是什么?

引用是C++的概念在C中叫取地址符号
本质:引用是别名,指针是地址
指针是独立的可以指向空值,这时我们为指针分配了内存。而引用必须初始化指定的对象自始至终只能依附于同一个变量,他只是别名。标准没有规定引用要不要占用内存,也没有规定引用具体要怎么实现,具体随编译器

13、结构体的浅拷⻉与深拷⻉

当结构体中有指针成员的时候容易出现浅拷⻉深拷⻉的问题。

浅拷贝存在的问题:当出现类的等号赋值时,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的。但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次free函数,此时teacher2已经是野指针,指向的内存空间已经被释放掉,再次free会报错;这时,必须采用深拷贝

深拷⻉就是,让两个结构体变量的指针成员分别指向不同的堆区空间,只是空间内容拷⻉⼀份,这样在各个结构体变量释放的时候就不会出现多次释放同⼀段堆区空间的问题

14、编译过程(GCC)?

gcc编译过程分为4个阶段:预处理、编译、汇编、链接。
预处理:头⽂件包含、宏替换、条件编译、删除注释
编译:主要进⾏词法、语法、语义分析等,检查⽆误后将预处理好的⽂件编译成汇编⽂件。
汇编:将汇编⽂件转换成 ⼆进制⽬标⽂件
链接:将项⽬中的各个⼆进制⽂件+所需的库+启动代码链接成可执⾏⽂件

15、内存分区

C语言开发对内存使用有区域划分,分别是栈区(stack)堆区(heap)BSS数据段(data)代码段(text)

BSS段 未初始化全局变量,未初始化全局静态变量
数据段 已初始化全局变量、已初始化全局静态变量、局部静态变量、常量数据
代码段 可执行代码、字符串常量

在这里插入图片描述

16、堆和栈的区别

16.1、管理方式不同

栈 stack: 存放函数的参数值、局部变量,由编译器自动分配释放
堆heap:是由new分配的内存块,由应用程序控制,需要程序员手动利用delete释放,容易产生内存泄漏。如果没有,程序结束后,操作系统自动回收。

16.2、是否有碎片

堆的分配需要使用频繁的new/delete,造成内存空间的不连续,会有大量的碎片

16.3、生长方向

对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方式是向下的,是向着内存地址减小的方向增长。

16.4、空间大小不同

一般来讲,在32位系统下面,堆内存可达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。
对于栈来讲,一般都是有一定空间大小的,例如,在vc6下面,默认的栈大小好像是1M。当然,也可以自己修改:打开工程。 project–>setting–>link,在category中选中output,然后再reserve中设定堆栈的最大值和 commit。

16.5、分配效率不同

栈 stack:是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高
堆heap:是c/c++库函数提供的,机制很复杂。库函数会按照一定的算法进行分配。显然,堆的效率比栈要低得多

16.6、堆和栈中的存储内容

:局部变量和形参
:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。

17、内存泄漏的几种情况

内存泄漏是指动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
(1)类的构造函数和析构函数中new和delete没有配套
(2)在释放对象数组时没有使用delete[],使用了delete
(3)没有将基类的析构函数定义为虚函数,当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确释放,因此造成内存泄露
(4)没有正确的清楚嵌套的对象指针

18、栈溢出的原因以及解决方法

栈溢出是指函数中的局部变量造成的溢出(注:函数中形参和函数中的局部变量存放在栈上)。

栈的大小通常是1M-2M,所以栈溢出包含两种情况,一是分配的的大小超过栈的最大值,二是分配的大小没有超过最大值,但是接收的buf比原buf小。

(1)函数调用层次过深,每调用一次,函数的参数、局部变量等信息就压一次栈
(2)局部变量体积太大。

解决办法大致说来也有两种:
(1)增加栈内存的数目;如果是不超过栈大小但是分配值小的,就增大分配的大小
(2) 使用堆内存;具体实现由很多种方法可以直接把数组定义改成指针,然后动态申请内存;也可以把局部变量变成全局变量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值