c基础面试题汇总

1. gcc 编译器编译的完整流程,分别有什么作用?
gcc 编译器编译的完整流程有四步,分别是 预处理、编译、汇编、链接
预处理 :处理源文件中的 #ifdef #include #define 等预处理命令,该阶段会生成一个中间文件 .i
gcc  - E  hello.c–o  # 可以使用 -E 选项生成 .i 文件
编译 gcc 把预处理后的结果编译成汇编语言代码,输入的是 .i ,编译后生成汇编语言文件 .s
gcc  - S  hello.i –o hello.s
汇编 :编译器把编译出来的汇编语言汇编成具体 CPU 上的目标代码(机器代码)。输入汇编代码文
.s ,输出目标代码文件 .o .obj。
gcc  – c hello.s –o hello.o
链接 :把多个目标代码模块链接成一个大的目标代码模块。输入目标代码文件 .o (与其它的目标
代码文件、库文件、引导代码),汇集成一个可执行的二进制代码文件。
gcc  hello.o –o hello
2. 什么是回调函数?
回调函数就是一个通过函数指针调用的函数。所以当一个函数作为参数使用的时候 , 这个函数就是
回调函数。
3. 地址能否使用 printf 函数中的 %u 形式打印?
可以,但是还是 %p 是最标准的。
4. 结构体与共用体的区别
首先结构体与共用体都是构造型数据类型,它们的成员变量都可以定义为不同类型的。
结构体可以同时存储多种变量类型,而共同体同一个时间只能存储和使用多个变量类型的一种,当
另一时间,变量类型被改变后,原来的变量类型和值将被舍弃。
共用体的作用是同一个数据项可以使用多种格式,可以节省空间。

结构体与共用体的主要区别在于存储方式的不同:

结构体在定义后,声明变量时系统为该变量申请内存空间为所有成员变量所占用的内存空间之和。

共用体变量声明时,变量占有空间不变,在同一时间,只能存储某一个成员的信息。

5. static const volatile 关键字有什么作用?

static

1 )修饰全局变量:变量只在本模块内可见,在定义不需要与其他文件共享的全局变量时,加上
static 关键字能够有效地降低程序模块之间的耦合,避免不同文件同名变量的冲突,且不会误使
用。
2 )修饰局部变量:变量在全局数据区分配内存空间,编译器自动对其初始化,其作用域为局部
作用域,当定义它的函数结束时,其作用域随之结束。
3 )修饰函数:函数的使用方式与全局变量类似,在函数的返回类型前加上 static,就是静态函
数,静态函数只能在声明它的文件中可见,其他文件不能引用该函数,不同的文件可以使用相同名
字的静态函数,互不影响。

const

C 编译器中:被 const 修饰的变量是只读变量,本质还是变量,有人称其为常变量,和普通变量的
区别在于常变量不能用于左值,其余的用法和普通常量一样。
C++ 编译器中:被 const 修饰的变量,变量名和初始值会直接放到符号表中,当使用变量名的时候
会直接从符号表中读取其值。相较于 C 编译器,在 C++ 编译器中 const 修饰的变量更像是常量。

volatile

volatile 关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地
址的稳定访问。系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数
据。而且读取的数据立刻被保存。
应用:当两个线程都要用到某一个变量且该变量的值会被改变时,应该用 volatile 声明,该关键字
的作用是防止优化编译器把变量从内存装入 CPU 寄存器中。
6. 声明变量和定义变量的区别
变量的声明不开辟内存空间,变量的定义开辟内存空间并且也有可能同时为其提供初始值。
// 举例:
  extern int i // 声明而不是定义,因为不分配存储空间。
  extern int i = 1 ; // 定义,因为分配了存储空间
7. 赋值与赋初值有什么不同?
1 )赋初值,也就是初始化,只能在定义的时候操作,形式为
         int  a = 10 ; // 其中 =init_value 就是赋初值了
         赋值,是在定义后,改变变量的值,形式为
         a = 20 ;
(2) 给变量赋初值,也叫初始化,只能用 = ,而赋值语句除了 = 还包括复合赋值语句,如 += -= 等,
自加,自减也可以算赋值语句。
8. 局部变量和全局变量能否重命名
可以重名。在程序中如果出现了相同的两个变量,一个是局部变量,一个是全局变量,在子函数中
默认调用的是局部变量,如果想访问全局变量,在全局变量之前加上 :: 就可以了。
9. 如何引用一个已经定义过的外部变量
1 )用 extern 关键字方式
2 )用引用头文件的方式,可以在不同的 C 文件中声明同名的全局变量,前提是其中只能有一个 C
文件中对此变量赋初值,此时连接不会出错。
10. 全局变量和局部变量的存储方式有什么区别?
1 )作用域不同:全局变量的作用域为整个程序,而局部变量的作用域为当前函数或循环。
2 )内存存储方式不同:全局变量存储在全局数据区中,局部变量存储在栈区。
3 )生命期不同:全局变量的生命期和主程序一样,随程序的销毁而销毁,局部变量在函数内部
或循环内部,随函数的退出或循环退出就不存在了。
4 )使用方式不同:全局变量在声明后程序的各个部分都可以用到,但是局部变量只能在局部使
用。函数内部会优先使用局部变量再使用全局变量。
// 引申:
C 语言经过编译之后将内存分为以下五个区域
1.栈:由编译器进行管理,自动分配和释放,存放函数调用过程中的各种参数,局部变量,返回值及函数返回地址。操作方式类似数据结构中的栈。
2.堆:用于程序动态申请分配和释放空间。C语言中的malloc和free,C++中的new和delete均是在堆
中进行的。正常情况下,程序员申请的空间在使用结束后应该释放,若程序员没有释放空间,则程序结束时系统自动回收。注意,这里的”堆”并不是数据结构中”堆”。
3.全局(静态)存储区:分为DATA段和BSS段。DATA段(全局初始化区)存放初始化的全局变量和静态变量;BSS段(全局未初始化区)存放未初始化的全局变量和静态变量。程序运行结束时自动释放。其中,BSS段在程序执行之前会被系统自动清零,所以未初始化全局变量和静态变量在程序执行之前已经为0。
4.文字常量区:存放常量字符串。程序结束后由系统释放。
5.程序代码区:存放程序的二进制代码。
11. const # defifine 相比有何优点?

 

12. 数组与指针的区别是什么?
指针与数组是两个完全不同的概念,当用指针表示数组时,二者都有下标表示法和 * 号表示法,数
组名是一数组的首地址,是常量,指针变量可以指向数组,是变量。
13. 为什么作为函数形参的数组和指针可以互换?
C 语言形参中数组名实质上就是指针变量。不论其形式如何,它与函数内定义的数组名是不同的。
其中的 int a[10] 等价于, int *a ;可以进行指针的各种运算,包括赋值运算。但 int b[10] 就是数组
名。
14. 形参和实参有什么区别?

 

15. 指针、数组和地址之间的关系是的什么?
数组是保存在一片连续内存单元中的,而数组名就是这片连续内存单元的首地址,内存单元的地址
就是指针,因此数组名也是一个指针数组是由多个数组元素组成,元素按其数组类型的不同,所占连续内存的大小也不同。
一个数组的元素的首地址就是其所占连续内存单元的首地址指针变量既可以指向一个数组,也可
以指向一个数组元素。
将数组名或数组的第一个元素的地址赋给指针,指针就指向了一个数组。
如果想使指针变量指向第 i 个元素,就可以把 i 元素首地址赋给它。
16. void 指针就是空指针吗?他有什么作用?
Void 指针一般称为通用指针,要与空指针严格区分, void 指针用于指向一个不属于任何类型的对
象,所以 void 指针称为通用指针
空指针一般应用于以下三种情况:
(1) 用空指针终止对递归数组结构的间接引用
(2) 用空指针作为函数调用失败时的返回值
(3) 用空指针作为警戒值
17. 与内存息息相关的重要概念有哪些?(野指针、栈( stack )、堆( heap )、静态区)
1 )野指针:指向 垃圾 内存的指针造成野指针的几种情况
a. 指针变量没有被初始化
b. 指针的操作超过可变量的作用范围
c. 指针被释放或者删除后,没有被置为 NULL
2 )栈( stack ):栈是用来保存局部变量,栈上的内容只在函数的范围内存在,函数运行结束
这些内容也会被销毁,栈的特点就是效率高,但空间大小有限。
3 )堆( heap ):堆是由 malloc ()、 calloc ()、等函数或者 new 操作符获得的内存,由
free ()函数和 delete ()函数释放内存、堆的特点就是效率高,但空间大小有限。
4 )静态区:静态区用于保存自动全局变量和 static变量,静态区的内容在整个程序中都存在,由编译器在编译的时候分配内存。
18. #include<> #include”” 有什么区别?
#include<>, 用到头文件中的常量或者函数时,需要系统到存放 c 库函数头文件所在的目录中寻找包
含的文件,这称之为标准方式,当在系统提供的文件中找不到该头文件是,就会再次搜索程序自己
的头文件。
#include”” ,系统先在用户当前目录中寻找要包含的文件,若找不到,再按照标准方式查找。为
了节省查找时间,需要在调用库函数时 <> 包含相关的头文件。如果包含用户自己编写的头文件,
最好用双引号 “”
19. x=x+1 , x+=1 , x++ 哪个效率高?
x=x+1 先读取等号右边的 x 的地址,计算 x+1 的值,然后读取等号左边的 x 地址,最后将等号右边的
值传递给等号左边的值
x+=1 读取等号右边的 x 的地址,计算 x+1 的值,将得到的值传给左边的 x ,之前已读过,故省去传
值的过程
x++ 读取 x 的地址,然后 x 自增 1 X++ 的效率最高
20. 为无符号类型变量赋值时,数据类型应怎样转换?
当表达式中存在有符号类型和无符号类型时,所有的操作数都自动转换为无符号数。