2020/8/3
让微信排版变 Nice
1 变量的声明和定义有什么区别
变 量 的 定 义 为 变 量 分 配 地 址 和 存 储 空 间 , 变 量 的 声 明 不 分 配 地 址 。 一 个 变 量 可 以 在
多 个 地 方 声 明 , 但 是 只 在 一 个 地 方 定 义 。 加 入 e x t e r n 修 饰 的 是 变 量 的 声 明 , 说 明
此 变 量 将 在 文 件 以 外 或 在 文 件 后 面 部 分 定 义 。
说 明 : 很 多 时 候 一 个 变 量 , 只 是 声 明 不 分 配 内 存 空 间 , 直 到 具 体 使 用 时 才 初 始 化 , 分
配 内 存 空 间 , 如 外 部 变 量 。
int main()
{
extern int A;
//这是个声明而不是定义,声明A是一个已经定义了的外部变量
//注意:声明外部变量时可以把变量类型去掉如:extern A;
dosth(); //执行函数
}
int A; //是定义,定义了A为整型的外部变量
2 简述#ifdef、#else、#endif和#ifndef的作用
利用#ifdef、#endif将某程序功能模块包括进去,以向特定用户提供该功能。在不需要时用户可轻易将其屏
蔽。
#ifdef MATH
#include "math.c"
#endif
在子程序前加上标记,以便于追踪和调试。
#ifdef DEBUG
printf ("Indebugging......!");
#endif
应对硬件的限制。由于一些具体应用环境的硬件不一样,限于条件,本地缺乏这种设备,只能绕过硬件,直
接写出预期结果。
注 意 : 虽 然 不 用 条 件 编 译 命 令 而 直 接 用 i f 语 句 也 能 达 到 要 求 , 但 那 样 做 目 标 程 序 长
( 因 为 所 有 语 句 都 编 译 ) , 运 行 时 间 长 ( 因 为 在 程 序 运 行 时 间 对 i f 语 句 进 行 测 试 ) 。
https://mdnice.com
1/28
2020/8/3
让微信排版变 Nice
而 采 用 条 件 编 译 , 可 以 减 少 被 编 译 的 语 句 , 从 而 减 少 目 标 程 序 的 长 度 , 减 少 运 行 时
间 。
3 写出int 、bool、 float 、指针变量与 “零值”比较的if 语句
//int与零值比较
if ( n == 0 )
if ( n != 0 )
//bool与零值比较
if (flag) // 表示flag为真
if (!flag) // 表示flag为假
//float与零值比较
const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON) //其中EPSINON是允许的误差(即精度)。
//指针变量与零值比较
if (p == NULL)
if (p != NULL)
4 结构体可以直接赋值吗
声 明 时 可 以 直 接 初 始 化 , 同 一 结 构 体 的 不 同 对 象 之 间 也 可 以 直 接 赋 值 , 但 是 当 结 构 体
中 含 有 指 针 “ 成 员 ” 时 一 定 要 小 心 。
注 意 : 当 有 多 个 指 针 指 向 同 一 段 内 存 时 , 某 个 指 针 释 放 这 段 内 存 可 能 会 导 致 其 他 指 针
的 非 法 操 作 。 因 此 在 释 放 前 一 定 要 确 保 其 他 指 针 不 再 使 用 这 段 内 存 空 间 。
5 sizeof 和strlen 的区别
sizeof是一个操作符,strlen是库函数。
sizeof的参数可以是数据的类型,也可以是变量,而strlen只能以结尾为‘\0’的字符串作参数。
编译器在编译时就计算出了sizeof的结果,而strlen函数必须在运行时才能计算出来。并且sizeof计算的是数
据类型占内存的大小,而strlen计算的是字符串实际的长度。
数组做sizeof的参数不退化,传递给strlen就退化为指针了
6 C 语言的关键字 static 和 C++ 的关键字 static 有什么区别
https://mdnice.com
2/28
2020/8/3
让微信排版变 Nice
在 C 中 s t a t i c 用 来 修 饰 局 部 静 态 变 量 和 外 部 静 态 变 量 、 函 数 。 而 C + + 中 除 了 上
述 功 能 外 , 还 用 来 定 义 类 的 成 员 变 量 和 函 数 。 即 静 态 成 员 和 静 态 成 员 函 数 。
注 意 : 编 程 时 s t a t i c 的 记 忆 性 , 和 全 局 性 的 特 点 可 以 让 在 不 同 时 期 调 用 的 函 数 进
行 通 信 , 传 递 信 息 , 而 C + + 的 静 态 成 员 则 可 以 在 多 个 对 象 实 例 间 进 行 通 信 , 传 递 信
息 。
7 C 语言的 malloc 和 C++ 中的 new 有什么区别
new 、delete 是操作符,可以重载,只能在C++ 中使用。
malloc、free 是函数,可以覆盖,C、C++ 中都可以使用。
new 可以调用对象的构造函数,对应的delete 调用相应的析构函数。
malloc 仅仅分配内存,free 仅仅回收内存,并不执行构造和析构函数
new 、delete 返回的是某种数据类型指针,malloc、free 返回的是void 指针。
注 意 : m a l l o c 申 请 的 内 存 空 间 要 用 f r e e 释 放 , 而 n e w 申 请 的 内 存 空 间 要 用
d e l e t e 释 放 , 不 要 混 用 。
8 写一个 “标准”宏MIN
# d e f i n e m i n ( a , b ) ( ( a ) < = ( b ) ? ( a ) : ( b ) )
9 ++i和i++的区别
+ + i 先 自 增 1 , 再 返 回 , i + + 先 返 回 i , 再 自 增 1
10 volatile有什么作用
状态寄存器一类的并行设备硬件寄存器。
一个中断服务子程序会访问到的非自动变量。
多线程间被几个任务共享的变量。
注 意 : 虽 然 v o l a t i l e 在 嵌 入 式 方 面 应 用 比 较 多 , 但 是 在 P C 软 件 的 多 线 程 中 ,
v o l a t i l e 修 饰 的 临 界 变 量 也 是 非 常 实 用 的 。
11 一个参数可以既是const又是volatile吗
可 以 , 用 c o n s t 和 v o l a t i l e 同 时 修 饰 变 量 , 表 示 这 个 变 量 在 程 序 内 部 是 只 读 的 , 不
能 改 变 的 , 只 在 程 序 外 部 条 件 变 化 下 改 变 , 并 且 编 译 器 不 会 优 化 这 个 变 量 。 每 次 使 用
这 个 变 量 时 , 都 要 小 心 地 去 内 存 读 取 这 个 变 量 的 值 , 而 不 是 去 寄 存 器 读 取 它 的 备 份 。
https://mdnice.com
3/28
2020/8/3
让微信排版变 Nice
注 意 : 在 此 一 定 要 注 意 c o n s t 的 意 思 , c o n s t 只 是 不 允 许 程 序 中 的 代 码 改 变 某 一 变
量 , 其 在 编 译 期 发 挥 作 用 , 它 并 没 有 实 际 地 禁 止 某 段 内 存 的 读 写 特 性 。
12 a 和&a 有什么区别
& a : 其 含 义 就 是 “ 变 量 a 的 地 址 ” 。
* a : 用 在 不 同 的 地 方 , 含 义 也 不 一 样 。
在声明语句中,*a只说明a是一个指针变量,如int *a;
在其他语句中,*a前面没有操作数且a是一个指针时,*a代表指针a指向的地址内存放的数据,如b=*a;
*a前面有操作数且a是一个普通变量时,a 代 表 乘 以 a , 如 c = b a。
13 用C 编写一个死循环程序
while(1)
{ }
注 意 : 很 多 种 途 径 都 可 实 现 同 一 种 功 能 , 但 是 不 同 的 方 法 时 间 和 空 间 占 用 度 不 同 , 特
别 是 对 于 嵌 入 式 软 件 , 处 理 器 速 度 比 较 慢 , 存 储 空 间 较 小 , 所 以 时 间 和 空 间 优 势 是
选 择 各 种 方 法 的 首 要 考 虑 条 件 。
14 结构体内存对齐问题
请 写 出 以 下 代 码 的 输 出 结 果 :
#include
struct S1
{
int i:8;
char j:4;
int a:4;
double b;
};
struct S2
{
int i:8;
char j:4;
double b;
int a:4;
};
https://mdnice.com
4/28
2020/8/3
让微信排版变 Nice
struct S3
{
int i;
char j;
double b;
int a;
};
int main()
{
printf("%d\n",sizeof(S1)); // 输出8
printf("%d\n",sizeof(S1); // 输出12
printf("%d\n",sizeof(Test3)); // 输出8
return 0;
}
sizeof(S1)=16
sizeof(S2)=24
sizeof(S3)=32
说 明 : 结 构 体 作 为 一 种 复 合 数 据 类 型 , 其 构 成 元 素 既 可 以 是 基 本 数 据 类 型 的 变 量 , 也
可 以 是 一 些 复 合 型 类 型 数 据 。 对 此 , 编 译 器 会 自 动 进 行 成 员 变 量 的 对 齐 以 提 高 运 算 效
率 。 默 认 情 况 下 , 按 自 然 对 齐 条 件 分 配 空 间 。 各 个 成 员 按 照 它 们 被 声 明 的 顺 序 在 内 存
中 顺 序 存 储 , 第 一 个 成 员 的 地 址 和 整 个 结 构 的 地 址 相 同 , 向 结 构 体 成 员 中 s i z e 最 大
的 成 员 对 齐 。
许 多 实 际 的 计 算 机 系 统 对 基 本 类 型 数 据 在 内 存 中 存 放 的 位 置 有 限 制 , 它 们 会 要 求 这 些
数 据 的 首 地 址 的 值 是 某 个 数 k ( 通 常 它 为 4 或 8 ) 的 倍 数 , 而 这 个 k 则 被 称 为 该 数 据 类
型 的 对 齐 模 数 。
15 全局变量和局部变量有什么区别?实怎么实现的?操作系统和编译器是怎
么知道的?
全局变量是整个程序都可访问的变量,谁都可以访问,生存期在整个程序从运行到结束(在程序结束时所占
内存释放);
而局部变量存在于模块(子程序,函数)中,只有所在模块可以访问,其他模块不可直接访问,模块结束
(函数调用完毕),局部变量消失,所占据的内存释放。
操作系统和编译器,可能是通过内存分配的位置来知道的,全局变量分配在全局数据段并且在程序开始运行
的时候被加载.局部变量则分配在堆栈里面。
16 简述C、C++程序编译的内存分配情况
https://mdnice.com
5/28
2020/8/3
让微信排版变 Nice
从静态存储区域分配:
内 存 在 程 序 编 译 时 就 已 经 分 配 好 , 这 块 内 存 在 程 序 的 整 个 运 行 期 间 都 存 在 。 速 度 快 、
不 容 易 出 错 , 因 为 有 系 统 会 善 后 。 例 如 全 局 变 量 , s t a t i c 变 量 , 常 量 字 符 串 等 。
在栈上分配:
在 执 行 函 数 时 , 函 数 内 局 部 变 量 的 存 储 单 元 都 在 栈 上 创 建 , 函 数 执 行 结 束 时 这 些 存 储
单 元 自 动 被 释 放 。 栈 内 存 分 配 运 算 内 置 于 处 理 器 的 指 令 集 中 , 效 率 很 高 , 但 是 分 配
的 内 存 容 量 有 限 。 大 小 为 2 M 。
从堆上分配:
即 动 态 内 存 分 配 。 程 序 在 运 行 的 时 候 用 m a l l o c 或 n e w 申 请 任 意 大 小 的 内 存 , 程
序 员 自 己 负 责 在 何 时 用 f r e e 或 d e l e t e 释 放 内 存 。 动 态 内 存 的 生 存 期 由 程 序 员 决
定 , 使 用 非 常 灵 活 。 如 果 在 堆 上 分 配 了 空 间 , 就 有 责 任 回 收 它 , 否 则 运 行 的 程 序 会 出
现 内 存 泄 漏 , 另 外 频 繁 地 分 配 和 释 放 不 同 大 小 的 堆 空 间 将 会 产 生 堆 内 碎 块 。
一 个 C 、 C + + 程 序 编 译 时 内 存 分 为 5 大 存 储 区 : 堆 区 、 栈 区 、 全 局 区 、 文 字 常 量
区 、 程 序 代 码 区 。
17 简述strcpy、sprintf 与memcpy 的区别
操作对象不同,strcpy 的两个操作对象均为字符串,sprintf 的操作源对象可以是多种数据类型, 目的操作
对象是字符串,memcpy 的两个对象就是两个任意可操作的内存地址,并不限于何种数据类型。
执行效率不同,memcpy 最高,strcpy 次之,sprintf 的效率最低。
实现功能不同,strcpy 主要实现字符串变量间的拷贝,sprintf 主要实现其他数据类型格式到字 符串的转
化,memcpy 主要是内存块间的拷贝。
注 意 : s t r c p y 、 s p r i n t f 与 m e m c p y 都 可 以 实 现 拷 贝 的 功 能 , 但 是 针 对 的 对 象 不
同 , 根 据 实 际 需 求 , 来 选 择 合 适 的 函 数 实 现 拷 贝 功 能 。
18 请解析(*(void (*)( ) )0)( )的含义
void (*0)( ) : 是一个返回值为void,参数为空的函数指针0。
(void (*)( ))0: 把0转变成一个返回值为void,参数为空的函数指针。
*(void (*)( ))0: 在上句的基础上加*表示整个是一个返回值为void,无参数,并且起始地址为0的函
数的名字。
(*(void (*)( ))0)( ): 这就是上句的函数名所对应的函数的调用。
19 C语言的指针和引用和c++的有什么区别?
指针有自己的一块空间,而引用只是一个别名;
https://mdnice.com
6/28
2020/8/3
让微信排版变 Nice
使用sizeof看一个指针的大小是4,而引用则是被引用对象的大小;
作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引 用的修改都会改变引用所指向的对
象;
可以有const指针,但是没有const引用;
指针在使用中可以指向其它对象,但是引用只能是一个对象的引用,不能 被改变;
指针可以有多级指针(**p),而引用止于一级;
指针和引用使用++运算符的意义不一样;
如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄露。
20 typedef 和define 有什么区别
用法不同:typedef 用来定义一种数据类型的别名,增强程序的可读性。define 主要用来定义 常量,以及
书写复杂使用频繁的宏。
执行时间不同:typedef 是编译过程的一部分,有类型检查的功能。define 是宏定义,是预编译的部分,其
发生在编译之前,只是简单的进行字符串的替换,不进行类型的检查。
作用域不同:typedef 有作用域限定。define 不受作用域约束,只要是在define 声明后的引用 都是正确
的。
对指针的操作不同:typedef 和define 定义的指针时有很大的区别。
注 意 : t y p e d e f 定 义 是 语 句 , 因 为 句 尾 要 加 上 分 号 。 而 d e f i n e 不 是 语 句 , 千 万 不
能 在 句 尾 加 分 号 。
21 指针常量与常量指针区别
指 针 常 量 是 指 定 义 了 一 个 指 针 , 这 个 指 针 的 值 只 能 在 定 义 时 初 始 化 , 其 他 地 方 不 能 改
变 。 常 量 指 针 是 指 定 义 了 一 个 指 针 , 这 个 指 针 指 向 一 个 只 读 的 对 象 , 不 能 通 过 常 量
指 针 来 改 变 这 个 对 象 的 值 。 指 针 常 量 强 调 的 是 指 针 的 不 可 改 变 性 , 而 常 量 指 针 强 调
的 是 指 针 对 其 所 指 对 象 的 不 可 改 变 性 。
注 意 : 无 论 是 指 针 常 量 还 是 常 量 指 针 , 其 最 大 的 用 途 就 是 作 为 函 数 的 形 式 参 数 , 保 证
实 参 在 被 调 用 函 数 中 的 不 可 改 变 特 性 。
22 简述队列和栈的异同
队 列 和 栈 都 是 线 性 存 储 结 构 , 但 是 两 者 的 插 入 和 删 除 数 据 的 操 作 不 同 , 队 列 是 “ 先 进
先 出 ” , 栈 是 “ 后 进 先 出 ” 。
注 意 : 区 别 栈 区 和 堆 区 。 堆 区 的 存 取 是 “ 顺 序 随 意 ” , 而 栈 区 是 “ 后 进 先 出 ” 。 栈 由
编 译 器 自 动 分 配 释 放 , 存 放 函 数 的 参 数 值 , 局 部 变 量 的 值 等 。 其 操 作 方 式 类 似 于 数
据 结 构 中 的 栈 。 堆 一 般 由 程 序 员 分 配 释 放 , 若 程 序 员 不 释 放 , 程 序 结 束 时 可 能 由
https://mdnice.com
7/28
2020/8/3
让微信排版变 Nice
O S 回 收 。 分 配 方 式 类 似 于 链 表 。 它 与 本 题 中 的 堆 和 栈 是 两 回 事 。 堆 栈 只 是 一 种 数
据 结 构 , 而 堆 区 和 栈 区 是 程 序 的 不 同 内 存 存 储 区 域 。
23 设置地址为0x67a9 的整型变量的值为0xaa66
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa66;
注 意 : 这 道 题 就 是 强 制 类 型 转 换 的 典 型 例 子 , 无 论 在 什 么 平 台 地 址 长 度 和 整 型 数 据 的
长 度 是 一 样 的 , 即 一 个 整 型 数 据 可 以 强 制 转 换 成 地 址 指 针 类 型 , 只 要 有 意 义 即 可 。
24 编码实现字符串转化为数字
编 码 实 现 函 数 a t o i ( ) , 设 计 一 个 程 序 , 把 一 个 字 符 串 转 化 为 一 个 整 型 数 值 。 例 如 数
字 : “ 5 4 8 6 3 2 1 ” , 转 化 成 字 符 : 5 4 8 6 3 2 1 。
int myAtoi(const char * str)
{
int num = 0; //保存转换后的数值
int isNegative = 0; //记录字符串中是否有负号
int n =0;
char *p = str;
if(p == NULL) //判断指针的合法性
{
return -1;
}
while(*p++ != '\0') //计算数字符串度
{
n++;
}
p = str;
if(p[0] == '-') //判断数组是否有负号
{
isNegative = 1;
}
char temp = '0';
for(int i = 0 ; i < n; i++)
{
char temp = *p++;
if(temp > '9' ||temp < '0') //滤除非数字字符
{
https://mdnice.com
8/28