一.C语言基础考题
1.预处理
1.预编译最先工作的是啥?
答:编译#开头的指令,,如 拷贝#include 包含的头文件代码,#define 宏定义的替换,条件编译 ifndef
2.c 语言中 # 与 ##的区别以及作用?
答::# :把宏参数变成一个字符串; ## :把两个宏参数连接到一起(只能两个
3.如何避免头文件被重复包含?
答:应用#ifndef #define #endif
2.关键字
1. static 关键字的作用?
C语言中static关键字的作用 - Practical - 博客园
2.const关键字的作用?
①对变量加以限定不能被修改,常量必须在定义的时候同时被初始化。
②const 和指针一起使用,
const int *p1;
int const *p2;
int *const p3;
在三种情况中,第三种指针是只读的,p3 本身的值不能被修改; 第一二种情况,指针所指向的数据是只读的,p1,p2 的值可以修改,但指向的数 据不能被修改。
③const 和函数形参一起使用 使用 const 单独定义变量可以用#define 命令替换,const 通常放在函数形参中。 如果形参是一个指针,为了防止在函数内部修改指针指向的数据就可以用 const 来限制
3.volatile 关键字的作用?
编译优化的方法有:将内存变量缓存到寄存器;调整指令顺序充分利用 CPU 指 令流水线。
答:因为访问寄存器要比访问内存单元要快的多,编辑器会作减少存取的优化。 当使用 volatile 声明函数变量的时候,系统总是重新从它所在的内存读取数据。 遇到这个关键字声明的变量,编译器对访问该变量的代码不再进行优化,从而提 供对特殊地址的稳定访问; 如果不使用 valatile,编译器将对所声明的语句进行 优化,以免出错。
4. extern 关键字的作用?
答: ①函数内的局部变量,函数外定义的变量为全局变量,为静态存储方式,生存周 期为整个程序,有效范围为定义变量的位置开始到本源文件结束。 如果在定义前想要引用该全局变量,则应该加上 extern 作为 “外部变量声明”。 多个源文件的工程想要引用一个源文件的外部变量也只许引用变量的文件中加 入 extern 关键字加以声明,但是可以在引用的模块内修改其变量的值,慎用。 ②extern “C”: C++代码调用 C 语言代码。在 C++的头文件中使用
3.结构体
1.结构体位域的好处?
①并不需要完整的字节,节省存储空间,处理简单;
②方便利用位域把一个变量按位域分解
2.*计算一个结构体的大小
①结构体偏移量的概念: 结构体中的偏移量指的是一个成员的实际地址和结构体首地址之间的距离。
②结构体大小的计算方法: 结构体会涉及到字节对齐,(目的是为了让计算机快速读取,用空间交换速度), 即最后一个成员的大小+最后一个成员的偏移量+末尾的填充节数。
第一步:结构体成员的偏移量必须是成员大小的整数倍(0被认为任何数的整数倍)
第二步:结构体大小必须是所有成员的整数倍(数组和结构体除外)
列子1:
Struct s1{
char ch1; // 1
int i; // 4+3
char ch2;
}
计算出的结构体大小是 12,而不是 9。
列子2:
Struct s2{
char ch1; // 1
int i; // 4+3
Struct s1{
char ch1;
int i; }
float ch2; //4
}
此时,结构体大小为12,中间的声明不占空姐,而
Struct s2{
char ch1; // 1
int i; // 4+3
Struct s1{
char ch1;
int i;
}temp;
float ch2; //4
}
此时结构体的大小为20。
注:联合体只算其中成员最大的。
#pragma pack(4) //指定向4对齐,如果最大成员大于4,按4对齐,如果最大成员小于4,则按最大成员对齐
4.c语言基本概率
1. 引用与指针的区别:
①引用必须初始化,指针不必初始化
②引用初始化后不能改变,但是指针可以改变所指的对象
③不存在空值的引用,但是存在空值的指针
2. .h 头文件中, ifndef /define /endif 的作用
①防止头文件被重复调用
3. include和 includ”file.h”的区别?
①前者从编译器自带的库函数中寻找文件,从标准库路径开始搜索文件
②后者是从自定义的文件中寻找文件,寻找不到再到库函数中寻找文件
4. 全局变量和局部变量的区别?
①全局变量->存储在静态数据区,占用静态的存储单元
②局部变量->存储在栈中,只有在函数被调用过程中才开始分配存储单元
5. 堆栈溢出的原因有哪些?
①函数调用层次太深,函数递归调用时,系统要在栈中不断保存函数调用时的线 程和产生的变量,递归调用太深,会造成栈溢出,这是递归无法返还。
②动态申请空间使用后没有释放。由于 C 语言中没有垃圾资源自动回收机制,因 此需要程序员主动释放已经不再使用的动态地址空间。
③数组访问越界,C 语言没有提供数组下标越界检查,如果在程序中出现数组下 标访问超出数组范围,运行过程中可能会存在内存访问错误。
④指针非法访问,指针保存了一个非法地址,通过这样的指针访问所指向的地址 时会产生内存访问错误
7. 局部变量能否与全局变量重名?
能,局部变量会屏蔽 全局变量,要使用全局变量,需使用”::”; 局部变量可以与全局变量同名,在函数内引
8. 如何引用一个已经定义了的全局变量?
①用 extern 关键字方式
②用引用头文件方式,前提是其中只能有一个 C 文件中对此变量赋初值,此时连 接不会出错。
9. 全局变量可不可以定义在可被多个.c 文件包含的头文件中,为啥?
可以,在不同的 C 文件中各自用 static 声明的全局变量,变量名可能相同,但是 各自 C 文件中的全局变量的作用域为该文件,所以互不干扰用这个变量时,会用到同名的局部变 量,而不会用到全局变量
11.static 关键字在 全局变量、局部变量、函数的区别?
①全局变量+static :改变作用域,改变(限制)其使用范围。 只初始化一次,防止在其他文件单元中被引用。 全局变量的作用域是整个 源程序,在各个源文件中都是有效的,而加了静态后的全局变量的作用域 仅限 于一个源文件中。
②局部变量+static :改变看它的存储方式,也就是改变了它的生存期。
③普通函数+static :作用域不同,仅在本文件。 只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该 在当前源文件中说明和定义,对于可在当前源文件以外使用的函数,应该在一个 头文件中说明,要使用这些函数的源文件要包含这个头文件
12.程序的内存分配(常考) 前言:c 语言程序.c 文件经过编译连接后形成编译、链接后形成的二进制映像文 件由堆,、栈、数据段(只读数据段,未初始化数据段 BSS,已初始化数据段三 部分)、代码段组成。
①栈区 (stack):由编译器进行管理,自动分配和释放,存放的是函数调用过 程中的各种参数,局部变量,返回值以及函数返回地址。
②堆区(heap) :用于程序动态申请分配和释放空间,malloc 和 free,程序员 申请的空间在使用结束后应该释放,则程序自动收回。
③全局(静态)存储区:分为 DATA(已经初始化),BSS(未初始化)段,DATA 段存放的是全局变量和静态变量; BSS(未初始化)存放未初始化的全局变量和 静态变量。 程序运行结束后自动释放,其中 BSS(全部未初始化区)会被系统自动 清零。
④文字常量区 :存放常量字符串,程序结束后由系统释放。
⑤程序代码段:存放程序的二进制代码
13.解释”堆”和”栈”的区别:
注:被问到这个问题的时候可以从这几个方面进行阐述 ①申请方式②申请后的系统反应③申请内存的大小限制④申请效率⑤存储内容⑥分配方式
①申请方式: Strack(栈): 由编译器自带分配释放,存放函数的参数值,局部变量等。 Heap(堆):程序员自己申请,并指名大小-->malloc 函数。
②申请后的系统响应 Strack(栈):只要栈剩余空间>所申请空间,都会提供。 Heap(堆):操作系统有记录空闲内存的链表:收到申请->遍历链表->寻找->申请空间的堆 结点
③申请内存的大小限制 Strack(栈):向低地址扩展的数据结果,连续内存区域,栈 获得的空间较小。 Heap(堆):向高地址扩展的,不连续内存区域;链表遍历方向为(低地址->高地址)。 栈获得空间灵活,空间也大。
④申请效率 Strack(栈):系统自由分配,速度快。 Heap(堆):速度慢,容易产生内存碎片。
⑤存储内容 Strack(栈):第一进栈 :主函数中的下一条指令的地址 -->函数的各个参数,参数由右往左 进栈。-->函数的局部变量(静态变量不入栈)。调用结束后,顺序相反,局部变量先出栈。 Heap(堆): 程序员自己安排
⑥分配方式 Strack(栈):栈 有两种分配方式,静态分配和动态分配。静态分配是编译器完成的,比如局 部变量的分配,动态分配由 alloca 函数进行分配,但栈的动态分配和堆是不同的,栈的动态 内存由编译器进行释放,无需手工实现。 Heap(堆):堆都是动态分配的,没有静态分配的堆。
14. 结构体和联合体的区别: ①结构体和联合体:都是由不同的数据类型成员组成,但是在同一时刻,联合体只存放了一 个被选中的成员;而结构体成员都存在(不同成员存放地址不同)。
②联合体不同成员赋值,会对其他成员重写,原来成员的值会不存在。 结构体的不同成员赋值是互不影响的
补充:
各种指针的定义:
1.一个指向指针的指针,他指向的指针指向一个整型数 int **a;
2.一个有10个指针的数组,每个指针指向一个整型数 int *a[10];
3.一个指向10个整型数的数组的指针 int (*a)[10];
4.一个指向指针的指针,被指向的指针指向一个有10个整型数的数组 int (**a)[10];
5.一个指向数组的指针,该数组有10个整型指针 int *(*a)[10];
6.一个指向函数的指针,该函数有一个整型参数并返回一个整型数 int (*a)(int);
7.一个有10个指针的数组,每个指针指向一个函数,该函数有一个整型参数并返回一个整型数
int (*a[10])(int);
8.一个函数的指针,指向函数的类型是有两个整型参数并且返回一个函数指针的函数,返回的函数指针指向有一个整型参数并返回整型数 的函数 int (*(*a)(int,int))(int);
9.数组指针: int (*p)[4];
10.指针数组:int *p[4];