C/C++

预处理

预处理器标识#error的目的是什么?

#error预处理指令的作用是,编译程序时,只要遇到#error就会生成一个编译错误提示消息并停止编译。其语法格式为:#error error-message。

下面举个例子:
程序中往往有很多的预处理指令

#ifdef XXX
...
#else
#endif

当程序比较大时,往往有些宏定义是在外部指定的(如makefile),或是在系统头文件中指定的,当你不太确定当前是否定义了 XXX 时,就可以改成如下这样进行编译:

#ifdef XXX
...
#error "XXX has been defined"
#else
#endif

这样,如果编译时出现错误,输出了XXX has been defined,表明宏XXX已经被定义了。

定义常量谁更好?# define还是 const?

尺有所短,寸有所长, define与 const都能定义常量,效果虽然一样,但是各有侧重。

define既可以替代常数值,又可以替代表达式,甚至是代码段,但是容易出错,而 const的引入可以增强程序的可读性,它使程序的维护与调试变得更加方便。具体而言,它们的差异主要表现在以下3个方面。

  1. define只是用来进行单纯的文本替换, define常量的生命周期止于编译期不分配内存空间,它存在于程序的代码段,在实际程序中,它只是一个常数;而 const常量存在于程序的数据段,并在堆栈中分配了空间, const常量在程序中确确实实存在,并且可以被调用、传递

  2. const常量有数据类型,而 define常量没有数据类型。编译器可以对 const常量进行类型安全检査,如类型、语句结构等,而 define不行。

  3. 很多IDE支持调试 const定义的常量,而不支持 define定义的常量由于 const修饰的变量可以排除程序之间的不安全性因素,保护程序中的常量不被修改,而且对数据类型也会进行相应的检查,极大地提高了程序的健壮性,所以一般更加倾向于用const来定义常量类型

typedef和 define有什么区别?

typedef与 define都是替一个对象取一个别名,以此来增强程序的可读性,但是它们在使用和作用上也存在着以下4个方面的不同。

  1. 原理不同

    define是C语言中定义的语法,它是预处理指令,在预处理时进行简单而机械的字符串替换,不做正确性检査,不管含义是否正确照样代入,只有在编译已被展开的源程序时,才会发现可能的错误并报错。

    例如,# define Pl3.1415926,当程序执行area=Pr*r语句时,PI会被替换为3.1415926。于是该语句被替换为area=3.1415926*r*r。如果把# define语句中的数字9写成了g,预处理也照样代入,而不去检查其是否合理、合法。

    typedef是关键字,它在编译时处理,所以 typedef具有类型检查的功能。它在自己的作用域内给一个已经存在的类型一个别名,但是不能在一个函数定义里面使用标识符 typedef。例如, typedef int INTEGER,这以后就可用 INTEGER来代替int作整型变量的类型说明了,例如:INTEGER a,b;

用 typedef定义数组、指针、结构等类型将带来很大的方便,不仅使程序书写简单而且使意义更为明确,因而增强了可读性。例如:typedef int a[10];

表示a是整型数组类型,数组长度为10。然后就可用a说明变量,例如:语句a s1,s2;完全等效于语句 int s1[10],s2[10].同理, typedef void(*p)(void)表示p是一种指向void型的指针类型。

  1. 功能不同

    typedef用来定义类型的别名,这些类型不仅包含内部类型(int、char等),还包括自定义类型(如 struct),可以起到使类型易于记忆的功能。

例如:typedef int (*PF)(const char *, const char*)

定义一个指向函数的指针的数据类型PF,其中函数返回值为int,参数为 const char*。typedef还有另外一个重要的用途,那就是定义机器无关的类型。例如,可以定义一个叫REAL的浮点类型,在目标机器上它可以获得最高的精度:typedef long double REAL,在不支持 long double的机器上,该 typedef看起来会是下面这样:typedef double real,在 double都不支持的机器上,该 typedef看起来会是这样:typedef float REAL

define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。

  1. 作用域不同

define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,而 typedef有自己的作用域。

程序示例如下:

void fun()
{
    #define A int
}
void gun()
{
     //这里也可以使用A,因为宏替换没有作用域,但如果上面用的是 typedef,那这里就不能用
     //A,不过,一般不在函数内使用 typedef
}
  1. 对指针的操作不同

两者修饰指针类型时,作用不同。

#define INTPTR1 int*
typedef int* INTPTR2;
INTPTR1  pl, p2;
INTPTR2 p3, p4;

INTPTR1 pl, p2和INTPTR2 p3, p4的效果截然不同。 INTPTR1 pl, p2进行字符串替换后变成int*p1,p2,要表达的意义是声明一个指针变量p1和一个整型变量p2.而 INTPTR2 p3, p4,由于 INTPTR2是具有含义的,告诉我们是一个指向整型数据的指针,那么p3和p4都为指针变量,这句相当于int*pl,*p2.从这里可以看出,进行宏替换是不含任何意义的替换,仅仅为字符串替换;而用 typedef为一种数据类型起的别名是带有一定含义的。

程序示例如下

#define INTPTR1  int*
typedef int* INTPTR2  
int a=1;
int b=2;
int c=3;
const INTPTR1  p1=&a; 
const INTPTR2 p2=&b;
 INTPTR2 const p3=&c;

上述代码中, const INTPTR1 p1表示p1是一个常量指针,即不可以通过p1去修改p1指向的内容,但是p1可以指向其他内容。而对于 const INTPTR2 p2,由于 INTPTR2表示的是个指针类型,因此用 const去限定,表示封锁了这个指针类型,因此p2是一个指针常量,不可使p2再指向其他内容,但可以通过p2修改其当前指向的内容。 INTPTR2 const p3同样声明的是一个指针常量。

如何使用 define声明个常数,用以表明1年中有多少秒(忽略闰年问题)

#define SECOND_PER_YEAR (60*60*24*365)UL

# include< filename. h>和# nclude" filename. h"有什么区别?

对于 include< filename. h>,编译器先从标准库路径开始搜索 filename.h,使得系统文件调用较快。而对于# include“ filename.h"”,编译器先从用户的工作路径开始搜索 filename.h,然后去寻找系统路径,使得自定义文件较快。

头文件的作用有哪些?

头文件的作用主要表现为以下两个方面:

  1. 通过头文件来调用库功能。出于对源代码保密的考虑,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口是怎么实现的。编译器会从库中提取相应的代码。

  2. 头文件能加强类型安全检查。当某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,大大减轻程序员调试、改错的负担。

在头文件中定义静态变量是否可行,为什么?

不可行,如果在头文件中定义静态变量,会造成资源浪费的问题,同时也可能引起程序错误。因为如果在使用了该头文件的每个C语言文件中定义静态变量,按照编译的步骤,在每个头文件中都会单独存在一个静态变量,从而会引起空间浪费或者程序错误所以,不推荐在头文件中定义任何变量,当然也包括静态变量。

写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个?

#define MIN(A,B) ((A) <= (B) ? (A) : (B))

不使用流程控制语句,如何打印出1~1000的整数?

宏定义多层嵌套(10 * 10 * 10),printf多次输出。

#include <stdio. h>
#define B P,P,P,P,P,P,P,P,P,P
#define P L,L,L,L,L,L,L,L,L,L
#define L I,I,I,I,I,I,I,I,I,I,N
#define I printf("%3d",i++)
#define N printf("n")
int main()
{
    int i = 1;
    B;
    return 0;
}

简便写法,同样使用多层嵌套

#include<stdio. h>
#define A(x)x;x;x;x;x;x;x;x;x;
int main ()
{
    int n=1;
    A(A(A(printf("%d", n++);
    return 0;
}

变量

全局变量和静态变量的区别是什么?

  1. 全局变量的作用域为程序块,而局部变量的作用域为当前函数。

  2. 内存存储方式不同,全局变量(静态全局变量,静态局部变量)分配在全局数据区(静态存储空间),后者分配在栈区。

  3. 生命周期不同。全局变量随主程序创建而创建,随主程序销毁而销毁,局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在了。

  4. 使用方式不同。通过声明为全局变量,程序的各个部分都可以用到,而局部变量只能在局部使用。

全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?

可以,在不同的C文件中以static形式来声明同名全局变量

可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错。

局部变量能否和全局变量重名?

能,局部会屏蔽全局。

局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。

对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。

联系作者

关于作者

作者在准备秋招的过程中,凭借这份资料,最后顺利拿到了oppo,小米,兆易创新,全志科技,海康威视等十余家家公司的offer。现将这部分资料分享出来,希望能对大家有帮助!

github仓库

这份资料总共有七个部分,分别为:C/C++数据结构与算法分析Arm体系与架构Linux驱动开发操作系统网络编程名企笔试真题。所有内容均会同步更新到github仓库中。

https://github.com/ZhongYi-LinuxDriverDev/EmbeddedSoftwareEngineerInterview