概述
目录 - C语言面试集锦
- 1. 编译器
- 1.1 什么是GNU?
- 1.2 什么是GCC?
- 1.3 GCC的编译流程?
- 1.4 GCC的各个编译步骤的作用?
- 2. 如何使用GDB调试器?
- 3. 静态库/动态库的区别、生成和使用?
- 3.1 库的命令方式?
- 3.2 静态库和动态库的比较?
- 3.3 制作方法?
- 1)静态库的制作
- 2)动态库的制作
- 3.4 使用方法?
- 1)静态库的使用(e.g. libtest.a)
- 2)动态库的使用(e.g. libtest.so)
- 3.5 动态库的加载方法?
- 4. 关键字
- 4.1 什么是空类型?
- 1) void
- 4.2 const
- 4.3 extern
- 4.4 volatile
- 1)变量用volatile声明的含义:
- 2)遇到volatile关键字声明的变量:
- 4.5 register
- 5. 数组、指针、函数(数组指针、指针数组、函数指针...)
- 6. 作用域
- 7. 内存空间分布
- 8. 内存安全
- 9. 代码规范
1. 编译器
1.1 什么是GNU?
GNU(“GNU is Not Unix”的递归缩写)是一个自由的操作系统,其软件内容完全以GPL方式发布。创始人Stallman宣称GNU的发音应当为’Guh-NOO’(注:Gnu在英文中原意为非洲牛羚,发音与new相同)。
GNU作为操作系统,但其发展尚未完成,GNU的内核(称为Hurd)是主要拖后腿那个东东。在现实中,GNU系统核心多半使用Linux内核、FreeBSD等替代方案。
1.2 什么是GCC?
Richard Stallman编写GCC的最初愿望是希望有一个C语言的编译器,那时的GCC含义仅是GNU C Compiler而已。多年发展走来,GCC除了能支持C语言,还支持Objective-C、C++、Java等。GCC不单指GNU C语言编译器,俨然成为了GNU编译器家族(GNU Compiler Collection)了。
编译器通过文件的扩展名来分辨程序所用的语言。
1.3 GCC的编译流程?
GCC的编译流程分为4步:
GCC的命令格式
gcc [命令选项] [目标文件] [源文件]
GCC选项 | 作用 |
---|---|
-o | 指定GCC的输出目标文件 |
-E | 编译器在预处理结束后,停止编译流程 |
-S | 编译器在编译完成后,停止编译流程 |
-c | 编译器在完成编译和汇编后,停止编译流程 |
-Wall | 打开所有类型语法警告 |
-On | 使用级别n来优化代码(GCC不同版本的优化效果可能不同) -O : 主要进行线程跳转和延迟退栈 进行优化 -O2 : 继承O1的优化效果; 还有处理器相关的(e.g. 指令调度)优化等 -O3: 继承O2的优化效果; 还有循环展开等优化; 建议: 在程序调试完成后,再打开优化选项,避免优化问题和bug信息同时处理起来比较困难 |
-g | 添加代码的gdb调试功能 |
1.4 GCC的各个编译步骤的作用?
GCC支持的编译处理方式表
文件后缀 | 含义 | GCC编译流程 | GCC编译命令 |
---|---|---|---|
.c | c语言源程序 | ||
.m | Objective-C语言源程序 | ||
.C/.cc/.cxx/.cpp | c++语言源程序 | ||
.i | 预处理过的C程序 | 预处理、编译、汇编、链接 | gcc -E -o test.i test.c 预处理: 1) 去注释 2) 处理“#”开头的预处理部分(如:条件编译宏替换、头文件包含) 3) 预处理变量的处理(如: __file__, __FILE__, __LINE__, __STDC__ …) |
.ii | 预处理过的C++程序 | 预处理、编译、汇编、链接 | |
.s/.S | (已经编译过的)汇编程序 | 预处理、编译、汇编、链接 | gcc -S -o test.s test.c/test.i 编译: 1) 检查程序规范性和语法错误等 2) 翻译生成汇编文件".s/.S" |
.o | 已经汇编过的二进制文件 | 预处理、编译、汇编、链接 | gcc -c -o test.o test.s 汇编: 1) 生成二进制文件"*.o" |
.a/.so | 编译后的库文件 | 预处理、编译、汇编、链接 | gcc -o test test.o 链接: 1) 指定函数库路径 2) 多文件整合 |
2. 如何使用GDB调试器?
GDB是GNU开源组织发布的一个Linux下强大的程序调试工具。
使用步骤 | 相关命令 |
---|---|
1) 带参‘-g’编译源程序test.c,生成a.out | gcc -g -o a.out test.c |
2) 使用gdb工具运行程序a.out,进入gdb模式 | gdb a.out |
3) 使用gdb命令 |
gdb命令 | 相关释义 |
---|---|
l | 查看源代码 |
b 30 | 设置第30行为断点 |
info b | 查看断点的信息 |
p val | 查看变量 ‘val’ 的值 |
r | 运行 |
c | 继续程序的运行 |
n | 单步运行 |
q | 退出 |
3. 静态库/动态库的区别、生成和使用?
3.1 库的命令方式?
- lib[库的名称][.a/.so]
3.2 静态库和动态库的比较?
库的类别 | 特点/作用 |
---|---|
动态库(后缀".so") | 动态库不会在编译后包含到可执行代码中,而是在程序运行是才被临时载入,因此程序运行时需要动态库存在,但是程序代码体积较小 |
静态库(后缀".a") | 静态库在编译后被编译到生成的可执行文件中,因此可执行文件体积会庞大,但程序运行时就不需要库。 |
3.3 制作方法?
1)静态库的制作
步骤 | 释义 |
---|---|
1) 生成目标文件 | gcc -c -o test.o test.c |
2) 将目标文件编译生成静态库 | ar -crs libtest.a test.o |
2)动态库的制作
步骤 | 释义 |
---|---|
1) 生成目标文件, 该文件是位置无关代码 | gcc -c -fPIC -o test.o test.c |
2) 将目标文件编译生成动态库 | gcc -shared -o libtest.so test.o |
3.4 使用方法?
1)静态库的使用(e.g. libtest.a)
gcc -static -o a.out main.c -L /usr/lib -ltest
- -L : 用于指定库文件的路径
- -l(小写字母): 用于指定编译时链接的库的名称
- -static: 选项表示强制使用静态库编译(当有同名同路径的动态库和静态库存在时,系统默认选择动态库)
2)动态库的使用(e.g. libtest.so)
gcc -o main main.c -L . -ltest
- -L: 用于指定库文件的路径
- -l(小写字母): 用于指定编译时链接的库的名称
3.5 动态库的加载方法?
三种加载方法 | 示例 |
---|---|
方法1 | 将库移动到 /usr/lib 或者 /lib下 |
方法2 | 1) 在LD_LIBRARY_PATH环境变量中加上库所在路径export LD_LIBRARY_PATH=~/work/lib/ 2) 查看环境变量: echo $LD_LIBRARY_PATH |
方法3 | 1) 在/etc/ld.so.conf.d/ 目录中添加一个文件,文件内容为动态库路径2) 执行 ldconfig 重新启动配置文件 |
4. 关键字
4.1 什么是空类型?
空类型是C语言的数据类型中的一种(基本类型、空类型、构造类型);目前空类型分类中只有void。空类型并非无类型,本身也是一种数据结构,常用在类型转换和参数传递的过程中。
1) void
- 对函数返回值的限定
- 返回值为void,表示函数没有返回值
- 对一般变量的限定
- void* 可以指向任何类型的指针变量(如:
int a=0; void* p = &a
; p则为“无类型指针,指向变量a的地址”)。 - 但是不能直接使用void来修饰一个变量:
void a;
在编译的时候会报错。
- void* 可以指向任何类型的指针变量(如:
4.2 const
const修饰的变量特点 | |
---|---|
1 | const修饰的变量为常变量不是 |
2 | const修饰变量时(const int a = 1; 等价于int const a = 1; ),一旦被初始化后,不能再被直接赋值修改,否则编译器会报错。 |
3 | const 修饰指针本身时(int* const p; ),( ‘const’的位置:在 ‘*’ 的右侧,靠近指针变量本身),指针p 本身不能被改变。 |
4 | const 修饰指针指向的对象时(int const *p; ),( ‘const’的位置:在 ‘*’ 的左侧,远离指针变量本身),指针p指向的内容不能被改变。 |
4.3 extern
序号 | extern作用 |
---|---|
1 | 修饰外部变量 : 1) 防止重复定义 2) 声明(引用)外部变量 |
2 | 修饰外部函数(注:可省略) 1) 向编译器声明函数来源外部 2) 可省略:现代编译器自动会在所有参与编译的源文件中查找函数对应的声明 |
3 | 修饰C++中调用的C语言函数 1) 向C++编译器声明:参与编译的C函数按照C++编译方式命名 |
4.4 volatile
volatile 声明变量时,该变量在编译时会“免优化”。
1)变量用volatile声明的含义:
变量值可被某些编译器未知的因素更改,如:操作系统、硬件或者其它线程等。
2)遇到volatile关键字声明的变量:
- 编译器对访问该变量的代码就不再进行优化;
- 系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。
4.5 register
register 修饰的变量可存放在cpu寄存器中(变量是否存贮在寄存器中取决于实际寄存器的富余程度,应当尽量减少使用该关键字来修饰变量),便于快速访问那些高频访问的数值。
5. 数组、指针、函数(数组指针、指针数组、函数指针…)
6. 作用域
7. 内存空间分布
一个程序的内存空间分布 | 释义 |
---|---|
stack |/ … heap /| | 1. 为函数内部的局部变量提供存储空间。 2. 进行函数调用时,存储“过程活动记录”。 3. 用作暂时存储区。(如:计算一个很长的算术表达式时,可以将部分计算结果压入堆栈。) |
bss | BSS (Block Started by Symbol)段 1. 存储未初始化 或 初始化为0的全局变量、静态变量 2. 未初始化的数据并不分配空间,只是记录所需空间大小 3. bss数据段不占用可执行文件的空间,其内容由操作系统初始化(清零) |
data | 1. 数据段存储经过初始化的全局和静态变量 2. data数据段占用可执行文件的空间 3. data和.bss在加载时合并到一个Segment(Data Segment)中,这个Segment是可读可写的 |
text | 1. 二进制形式的程序代码、函数; 2. 在其中.rodata段也可包含一些只读的常数变量,例如字符串常量等。 3.有些常量并不在只读数据区(如:立即数与指令编译在一起直接放在代码段) 4. 程序加载运行时,.rodata段和.text段通常合并到一个Segment(Text Segment)中,操作系统将这个Segment的页面只读保护起来,防止意外的改写 |
#include <stdlib.h>
int a=1; //a, 在数据段(全局已初始化数据区)
char *p; //p,在BSS(未初始化全局变量)
int main()
{
int b; //b 在栈区(局部变量)
char s[]="abc"; //s在栈区(局部数组变量); "abc"在常量区(.rodata)
char *p1; //p1在栈区(局部变量)
char *p2="123"; //p2在栈区,"123"在常量区(.rodata)
static int c; //c在bss段; 静态局部变量会自动初始化(因为BSS区自动用0或NULL初始化)
p1=(char*)malloc(10); //分配得来的10个字节的区域在堆区
free(p1);
p1=NULL; //显式地将p1置为NULL,避免以后错误地使用p1
}
8. 内存安全
9. 代码规范
最后
以上就是背后奇异果为你收集整理的面试达人手册 - C语言知识点(持续更新)的全部内容,希望文章能够帮你解决面试达人手册 - C语言知识点(持续更新)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复