开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情
预处理
1、什么是预编译?何时需要预编译?
(1)预编译又称预处理,是做些代码文本的替换工作,即程序执行前的一些预处理工作。主要处理 # 开头的指令,如拷贝#include包含的文件代码、替换#define定义的宏、条件编译#if等。
(2)何时需要预编译:
a.总是使用不经常改动的大型代码体;
b.程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。
2、写一个“标准”宏,这个宏输入两个参数并返回较小的一个
#define MIN(x, y) ((x)<(y)?(x):(y)) //注意结尾没有;C语言面试知识
3、#与##的作用?
# 是把宏参数转化为字符串的运算符,## 是把两个宏参数连接的运算符。
例如:
#define STR(arg) #arg //则宏STR(hello)展开时为”hello”
#define NAME(y) name_y //则宏NAME(1)展开时仍为name_y
#define NAME(y) name_##y //则宏NAME(1)展开为name_1
#define DECLARE(name, type) typename##_##type##_type,
//则宏DECLARE(val, int)展开为int val_int_type
4、如何避免头文件被重复包含?
例如,为避免头文件my_head.h被重复包含,可在其中使用条件编译:
#ifndef _MY_HEAD_H
#define _MY_HEAD_H /空宏/
/其他语句/
#endif
5、#include<file.h> 与 #include "file.h"的区别?
前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。
关键字
1、register:
(1)register关键字的作用:
请求CPU尽可能让变量的值保存在CPU内部的寄存器中,减去CPU从内存中抓取数据的时间,提高程序运行效率。
(2)register作用的实现原理:
扩展:CPU组成,计算机系统组成,数据处理流程 。
(3)什么时候使用register?
一般,我们将频繁被访问的变量,用 register 修饰。
(因为CPU内存资源是有限的,是稀缺的,不可能将所有变量都声明为register变量)
(4)使用register关键字应注意什么?
a. 只有局部变量才可以被声明用register 修饰;
(register不能修饰全局变量和函数的原因:全局变量可能被多个进程访问,而用register修饰的变量,只能被当前进程访问)
b. 不能用取地址获取用register 修饰的变量的地址;
(原因:变量保存在寄存器中,而取地址获取的地址的是内存的地址)
c. 用register 修饰的变量一定要是CPU 所接受的数据类型。
2、static:
(1)static关键字的作用:
static既可以修饰变量,也可以修饰函数,修饰变量时,既可以修饰局部,也可修饰全局。
static修饰静态局部变量,延长变量的生命周期,直至程序结束,这个变量才释放;
static修饰全局变量,使其只可在本文件可访问,其他文件不可见;
(static修饰的变量都保存在数据段静态数据区中,未初始化时,系统将默认初始化为0)
static修饰函数,使其只可在本文件可调用,其他文件不可调用;
(2)什么时候使用static修饰变量?
当希望一个变量直至程序结束才释放时,用static修饰静态局部变量;
当希望一个全局变量只可在本文件可访问,其他文件不可见时,用Static修饰全局变量;
当希望一个函数只可在本文件可调用,其他文件不可调用时,用Static修饰函数;
3、extern:
(1)extern关键字的作用:
extern 用来外部声明一个全局变量,这个全局变量在另一个文件中被定义。
(2)使用extern关键字应注意什么?
标明数据类型,例:extern int count;
(3)什么时候使用extern修饰变量?
在a.c文件中想使用b.c文件中的全局变量时,用extern 外部声明。
(4)用于extern "C"
当文件为CPP文件时,通过extern “C”告诉C++编译器,extern “C”{}里包含的函数都用C的方式来编译:
a.可以是单一语句;
extern "C" double sqrt(double);
b.可以是复合语句, 相当于复合语句中的声明都加了extern "C";
extern "C"
{
double sqrt(double);
int min(int, int);
}
c.可以包含头文件,相当于头文件中的声明都加了extern "C";
extern "C"
{
#include
}
d.不可以将extern "C" 添加在函数内部;
e.如果函数有多个声明,可以都加extern "C", 也可以只出现在第一次声明中,后面的声明会接受第一个链接指示符的规则;
f.除extern "C", 还有extern "FORTRAN" 等。
4、const:
(1)const关键字的作用:
const修饰变量,是这个变量变成只读变量,变量对应的空间的值是可变的,但不能用变量名来修改空间中的值。
(2)使用const关键字应注意什么?
使用const关键字修饰的变量,一定要对变量进行初始化。
int *const p = # p++ ✘
const int *p = # (*p)++ ✘
int const *p = # (*p)++ ✘
(离谁进,谁就不可以改变)
(3)什么时候使用const修饰变量?
a. 声明常变量,使得指定的变量不能被修改;
const int a = 5;/a的值一直为5,不能被改变/
const int b; b = 10;/b的值被赋值为10后,不能被改变/
const int *ptr; /ptr为指向整型常量的指针,ptr的值可以修改,但不能修改其所指向的值/
int *const ptr;/ptr为指向整型的常量指针,ptr的值不能修改,但可以修改其所指向的值/
const int *const ptr;/ptr为指向整型常量的常量指针,ptr及其指向的值都不能修改/
b. 修饰函数形参,使得形参在函数内不能被修改,表示输入参数;
如int fun(const int a);或int fun(const char *str);
c. 修饰函数返回值,使得函数的返回值不能被修改。
const char *getstr(void);使用:const *str= getstr();
const int getint(void); 使用:const int a =getint();
(4)const与#define 相比,有何优点?
(1)const 作用:定义常量、修饰函数参数、修饰函数返回值。
被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
(2)const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
(3)有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
5、typedef:
(1)typedef关键字的作用:
声明一个已经存在的数据类型的同义字,给数据类型定义一个新名字,提高了移植性;简化复杂的类型声明,提高编码效率;解释数据类型的作用。
6、voliate:
(1)voliate关键字的作用:
volatile指定的变量可能被系统、硬件、进程/线程改变,强制编译器每次从内存中取得该变量的值,而不是从被优化后的寄存器中读取。
简单来说,就是自己所定义的变量在程序运行过程中一直会变,如果希望这个值被正确处理,就需要每次从内存中去读这个值,这样就不会有错误了。
(2)什么情况下用volatile关键字?
a.中断服务程序中修改的供其他程序检测的变量需要加volatile;
b.多任务环境下各任务间共享的标志应该加volatile;
c.存储器映射的硬件寄存器通常也要加volatile说明,因此每次对它的读写都可能有不同意义。
(3)一个参数既可以是const还可以是volatile吗?
可以,例如只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
(4)一个指针可以是volatile 吗?
可以,尽管这并不常见。例如当一个中服务子程序修该一个指向buffer的指针时。
7、inline:
(1)inline关键字的作用:
内联inline是给编译器的优化提示,如果一个函数被编译成inline的话,那么就会把函数里面的代码直接插入到调用这个函数的地方,而不是用调用函数的形式。
(2)使用inline关键字应注意什么?
内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。
8、sizeof:
(1)sizeof关键字的作用:
sizeof是在编译阶段处理,且不能被编译为机器码。sizeof的结果等于对象或类型所占的内存字节数。sizeof的返回值类型为size_t。
(2)sizeof的取值:
变量:int a; sizeof(a)为4;
指针:int *p; sizeof(p)为4;
数组:int b[10]; sizeof(b)为数组的大小,4*10;int c[0]; sizeof(c)等于0;
结构体:struct (int a; char ch;)s1; sizeof(s1)为8 ,因为结构体字节需要对齐。
注意:不能对结构体中的位域成员使用sizeof
sizeof(void)等于1
sizeof(void *)等于4