C语言面试基础题,程序员必备面试题~

69 阅读8分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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