c语言常见笔试题总结 -- 带答案

时间:2021-04-15 14:39:40
【1 使用宏】
1.1宏
#ifdef NDEBUG
#define TRACE(S) S
#else
#define TRACE(S) printf(“%s;\n”, #S); S
#endif
问:以上TRACE()宏的作用是什么?
【1 使用宏】
1.1宏
#ifdef NDEBUG

当打开宏时, 可以用于 打印 S


1.2 #error的作用?

答: #error 预处理指令的作用是,编译程序时,只要遇到#error 就会生成一个编译错误提
示消息,并停止编译。其语法格式为:
#error error-message
注意,宏串error-message 不用双引号包围。遇到#error 指令时,错误信息被显示,可能同时

还显示编译程序作者预先定义的其他内容。

#define ab "hello"
#ifdef ab
#error "ab has been defined."
#endif




1.3 定义一个宏,求出给定数组中的元素的个数
#define NELEMENTS(array) ??

答:
#define NELEMENTS(array)  (szieof(array) / sizeof(  (array)[0]) )


1.4 定义一个宏,求出给定结构中给定成员的偏移量
#define OFFSET(structure, member) ??

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)     


加上系统有自带的变量类型 的头文件
"stm32f10x.h"---u8,u16
stdint.h----int8_t,uint8_t,   uint32_t 等



【2 数据声明和定义】
给定以下类型的变量a的定义式:
a) An integer
int a;
b) A pointer to an integer
int *a;
c) A pointer to a pointer to an integer
int **a;
d) An array of 10 integers
int a[10];
e) An array of 10 pointers to integers
int *a[10]
f) A pointer t o an array of 10 integers
int (*a)[10];

g) A pointer to a function that takes an integer as an argument and returns an integer

int (*a)(int)

h) An array of ten pointers to functions that take an integer argument and return an integer
int (*a)[10](int)



数组指针和指针数组的区别

数组指针(也称行指针)
定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。

如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
 p=a;        //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
 p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

所以数组指针也称指向一维数组的指针,亦称行指针。

指针数组
定义 int *p[n];
[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
如要将二维数组赋给一指针数组:
int *p[3];
int a[3][4];
for(i=0;i<3;i++)
p[i]=a[i];
这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
所以要分别赋值。

这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
比如要表示数组中i行j列一个元素:
*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]

 

优先级:()>[]>*





【3 复杂类型(1)】
有如下表达式:

char (*(*x())[])();
请用文字描述x是什么。

char (         *    (   *x( )  )      []         )   (  );

x是个函数,该函数返回 指向函数数组的指针
   ??

--------------------------------------

这道题来自TheC ProgrammingLanguage”中的一个例子。
首先,确定标识符:x
x
是一个函数,没有参数:x()
返回值是一个指针:*x()
这个指针指向一个数组:(*x())[]
数组中的每个元素是指针:*(*x())[]
指向一个不带参数的函数:(*(*x())[])()
函数的返回值是charchar(*(*x())[])()
这里,要知道*()[]的优先级。




【4 复杂类型(2)】
jmp_buf的定义:
typedef struct _jmp_buf
{
REG_SET reg;
int extra[3];
} jmp_buf[1];
setjmp函数的原型:
extern int setjmp (jmp_buf __env);
问:调用setjmp时传递__env的内容,还是传递指针?

答 : jmp_buf 是个数组 , 数组名做为函数参数时, 是传递 指针。

【5 头文件】
问:为什么标准头文件都有类似以下的结构?
#ifndef __INCvxWorksh
#define __INCvxWorksh
#ifdef __cplusplus
extern “C” {
#endif
/*…*/
#ifdef __cplusplus
}
#endif
#endif /* __INCvxWorksh */

使c++编译出的目标文件具有 c链接特性

【6 static关键字】
请说出static关键字的3种用处:
(1)用于全局变量;
(2)用于局部变量;
(3)用于函数。
/* file.c */
static int a;
int b;
static int fn()
{
static int x;
int y;
}

【7 const关键字】
7.1 const关键字的意义是什么?



const关键字在C语言中用于声明常变量,其值不可修改,但具有确定的数据类型。C编译器总是为其分配相应的存储单元。
C++中,const关键字用于声明常量,C++编译器视具体情况决定是 为其分配存储单元 还是仅将其作为编译期间的常量。

 


7.2 解释以下的变量定义:
const int a1;
int const a2;
const int *a3;
int * const a4;
int const * const a5;



constint a1; a1是整型常量。

int const a2; a2是整型常量。等同于const int a2;
const int *a3;    a3
是指针(a3是可变的),指向一个整型常量。等同于int   const *a3;
int * const a4;   a4
是常量指针(a4不可变),指向一个整型变量。
int  const * const a5; a5
是常量指针(a5不可变),指向一个整型常量。等同于constint * const a5;





【8 volatile关键字】
8.1 volatile意义?例如
volatile int *p;


*p指向volatile型参数。


一个定义为volatile的 变量是说这变量可能会被意想不到地改变,这样, 编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在 寄存器里的备份。下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量



8.2 volatile能和const一起使用吗?例如
volatile const int *p;


const关键字的意思是限制编程者自己不能修改变量的值;两者并不矛盾。
例如一个内存映射的、只读的硬件寄存器,假设它的地址是p,则可以这样声明:volatileconst UINT32 *p;



【9 sizeof()】
有以下定义:
char *pmsg = “A”;
char msg[] = “A”;
char ch = ‘A’;
问:
sizeof(pmsg) = ?
sizeof(msg) = ?
sizeof(“A”) = ?
sizeof(ch) = ?
sizeof(‘A’) = ? (在C++中等于多少?)
void f(char param[100])
{
// sizeof(param) = ?
}


sizeof(pmsg)=指针变量的长度  (32 位机为 4,  64位机为 8
sizeof(msg)= 2 (
字符数组的长度)
sizeof(”A”)= 2 (
字符串的长度)
sizeof(ch)= 1 (
字符变量的长度)
sizeof(‘A’)=
整型变量的长度(

C语言中,字符常量的数据类型实际上是int

C++中,它的数据类型是char,从而原式等于1)

sizeof(param)=指针变量的长度 (32 位机为 4,  64位机为 8  (数组名作参数时,传递的是数组的起始地址)




【10 字符串】
有以下代码
char *pmsg = “hello, world!”;
strcpy(pmsg, “hi, there.”);
试评论该代码。


这种写法是和编译器&操作系统相关的,所以不应当这样写。在WIN2K+VC环境下debug程序时会出现异常。
不过这样写,编译器不会报错。按道理,hello…”的类型是constchar [N],它是不能赋值给char*的,
因为会丢失常量属性。但在const关键字成为C标准之前,大家都这样写程序,所以char*pmsg = “hello…”
这种写法被给予特别豁免,即使在C++中也是如此,在TheC++ Programming Language”的附录里对此有讨论。

hello,world!”是字符串常量(stringliteral),它的类型是constchar [N]N为字符串的长度(包括结尾的0)
“TheC ProgrammingLanguage”
指出,写字符串常量的结果是未定义的(undefined)。所以在有些平台(操作系统+编译器)上程序不会出错,而在其它平台上程序出现异常。

GNU手册里这样说:
Writinginto string constants is a very bad idea; “constants” should beconstant.
不过在GNU中它提供另外的选择:使用-fwritable-strings进行编译就可以。

那么,为什么不允许修改字符串常量呢(它不也在内存中吗)
这可能和另外一个特点有关,即重复字符串的合并。现在的编译器应该会主动帮助我们合并程序中相同的字符串常量
以节省内存。如果stringliteral可写,就会出现问题。例如:
voidfoo()
{
printf(”%s\n”, “how are you?”);
}
voidbar()
{
char *p = “how are you?”;
strcpy(p, “WHO AREYOU?”);
}
调用foo()当然会打印howare you”。但如果编译器合并字符串,那么先调用bar(),再调用foo()foo()打印的就是
“WHOARE YOU?”
。这当然不是我们想要的结果。
另外一方面,这样写也有问题(确实有人这么写)
if(func() == “something”)

func()
是:
char*func()
{

return“something”;
}
这就假设编译器一定会帮我们合并字符串,然而那也不一定。











【11 混合运算】
有以下代码:
void foo()
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts(“> 6″) : puts(” < = 6″);

}
请问调用foo()的输出?


混合运算时的数据类型转换次序:int–> unsigned –> long –>double
另外,charshort必定转换为intfloat必定转换为double



【12 内存访问】
有以下代码:
void fn()
{
int a[100];
int *p;
p = (int *)((unsigned int)a + 1);
printf(“p=0x%x\n”, *p);
}
试评论以上代码。



代码的意图是想使p指向数组的第二个元素,但通常的写法是:p=a+1

这里存在这样的问题:a是个常量地址,
a+1
指向下一个数组元素,而((unsignedint)a +1)指向下一个内存地址。

如果地址是字节计数的,则p指向的
是数组第一个元素的第二个字节。还有一个效果就是:在RISC上该printf语句会出异常,因为不允许非对齐访问
(mis-alignedaccess)
。对齐访问就是访问2字节变量的地址要能被2整除,4字节变量的地址要能被4整除,etc






【13 C库函数】
请说明以下函数的意义:
void perror(const char *__s); perror函数只是将你输入的一些信息和现在的errno所对应的错误一起输出。
fdprintf(int, const char *, …);


isspace(), 
检查参数c是否为空格字符,也就是判断是否为空格(' ')、水平定位字符
('\t')、归位键('\r')、换行('\n')、垂直定位字符('\v')或 翻页('\f')的情况。 [1] 
返回值
若参数c为空格 字符,则返回TRUE,否则返回NULL(0)。
附加说明
此为 宏定义,非真正函数。


isxdigit(),
检查参数c是否为16进制数字,只要c为下列其中一个情况则返回非零值,否则返回0。

strerr(),
stderr与stdin,stdout一样,是流。 

  具体就是stdin是标准输入流,默认为键盘, 
  stdout是标准输出流,默认为屏幕, 
  stderr是标准错误流,一般把屏幕设为默认, 
  也可以输出到文件。



sprintf()