参考答案:
答案,仅供参考:
1.1 这道题主要考察#的功能,S是一个表达式.TRACE()的作用就是在DEBUG状态下,计算表达式S的值之前先打印S.
1.2 #error用于向编译器报错,并输出它后面带的错误信息.例如:
#ifndef SOMETHING
#error SOMETHING not defined!
#endif
如果在这段代码之前未定义过SOMETHING,则在编译时出错,并给出"SOMETHING not defined!"的错误信息.
1.3 #define NELEMENTS(array) (sizeof(array) /sizeof((array)[0]))
1.4 #define OFFSET(structure, member) ((int)&(((structure *)0)-member))
2 (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 to an array of 10 integers:int(*a)[10];
(g) A pointer to a function that takes an integer as an argumentand returns an integer:int (*a)(int);
(h) An array of 10 pointers to functions that take an integerargument and return an integer: int (*a[10])(int);
3 char (*(*x())[])();
这道题来自"The C Programming Language"中的一个例子.
首先,确定标识符:x
x是一个函数,没有参数:x()
返回值是一个指针:*x()
这个指针指向一个数组:(*x())[]
数组中的每个元素是指针:*(*x())[]
指向一个不带参数的函数:(*(*x())[])()
函数的返回值是char:char (*(*x())[])()
这里,要知道*、()和[]的优先级.
4 这个定义有点怪,它的意思是:jmp_buf这种类型是一个数组,只有一个元素,元素类型为struct{...}.数组名作为函数参数时,应该是传递地址/指针.
(第五道题的两种解答)
解答一:
5 在编译源文件时,C编译器和C++编译器都会对符号(函数或变量)名作某些修正,但两者采用的修正方法不同,所以两者生成的目标文件不能互相链接.在 C++中使用extern"C"可以让C++符号获得C链接特性.由于C++编译器会自动定义__cplusplus宏,所以在C语言头文件中采用这种 结构可以保证无论使用何种编译器,生成的目标文件都具有C链接特性,能够与标准C编译器所生成的目标文件相链接.
解答二:
5.对于#ifdef xxx
#define xxx
...
#endif
是为了防止重复包含,通过选择性编译实现同一头文件用在多个c文件的工程中
这种用法可以参见ucos中的ucos.c文件
而__cpluspls 的用法:
#ifdef __cpluspls
...
#else
extern "C" {
...
}
#endif
是为了区别C代码和C++代码,由于C++的重载和名称空间机制,特别是重载,编译时会给函数加上一些特殊的修饰符,使用extern "C"是为了在连接时使用C连接性来连写该模块的代码,C连接性的特点之一就是保持函数原本的标识符,这可以使C++中的C库函数(加c去.h的那些个文件)在以C++或C方式编译时均可被使用.
6 (1)用于全局变量:外部静态变量,只能在本源文件中被引用,不能被其它源文件所引用.
(2)用于局部变量:局部静态变量,在函数返回后存储单元不释放;下一次调用该函数时,该变量为上次函数返回时的值.
(3)用于函数:内部函数,只能被本源文件中的函数所调用,不能被其它源文件调用.
7.1 const关键字在C语言中用于声明"常变量",其值不可修改,但具有确定的数据类型.C编译器总是为其分配相应的存储单元.
在C++中,const关键字用于声明常量,C++编译器视具体情况决定是为其分配存储单元还是仅将其作为编译期间的常量.
7.2 const int 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不可变),指向一个整型常量.等同于const int * const a5;
8.1 volatile关键字用于声明内存映射的易失型变量,这类变量的值随时可能由于某种编译器所不知道的原因(例如,外部设备对其写入)所改变,所以编译器在进行代码优化时不能对其做任何的假设和依赖.
8.2 volatile可以和const一起使用,不过很少见.
const关键字的意思是限制编程者自己不能修改变量的值;两者并不矛盾.
例如一个内存映射的、只读的硬件寄存器,假设它的地址是p,则可以这样声明:volatileconst UINT32 *p;
9 sizeof(pmsg) = 指针变量的长度
sizeof(msg) = 2 (字符数组的长度)
sizeof("A") = 2 (字符串的长度)
sizeof(ch) = 1 (字符变量的长度)
sizeof(‘A’) = 整型变量的长度 (在C语言中,字符常量的数据类型实际上是int;在C++中,它的数据类型是char,从而原式等于1)
sizeof(param) = 指针变量的长度 (数组名作参数时,传递的是数组的起始地址)
10 这种写法是和编译器&操作系统相关的,所以不应当这样写.在WIN2K+VC环境下debug程序时会出现异常.
不过这样写,编译器不会报错.按道理,"hello..."的类型是const char [N],它是不能赋值给char *的,
因为会丢失常量属性.但在const关键字成为C标准之前,大家都这样写程序,所以char*pmsg = "hello..."
这种写法被给予特别豁免,即使在C++中也是如此,在"The C++ Programming Language"的附录里对此有讨论.
"hello, world!"是字符串常量(string literal),它的类型是const char [N],N为字符串的长度(包括结尾的0).
"The C Programming Language"指出,写字符串常量的结果是未定义的(undefined).所以在有些平台(操作系统+编译器)
上程序不会出错,而在其它平台上程序出现异常.
GNU手册里这样说:
Writing into string constants is a very badidea; "constants" should be constant.
不过在GNU中它提供另外的选择:使用-fwritable-strings进行编译就可以.
那么,为什么不允许修改字符串常量呢(它不也在内存中吗)?
这可能和另外一个特点有关,即重复字符串的合并.现在的编译器应该会主动帮助我们合并程序中相同的字符串常量
以节省内存.如果string literal可写,就会出现问题.例如:
void foo()
{
printf("%s\n", "how areyou?");
}
void bar()
{
char *p = "how are you?";
strcpy(p, "WHO ARE YOU?");
}
调用foo()当然会打印"how are you".但如果编译器合并字符串,那么先调用bar(),再调用foo(),foo()打印的就是
"WHO ARE YOU?".这当然不是我们想要的结果.
另外一方面,这样写也有问题(确实有人这么写):
if (func() == "something")
...
func()是:
char *func()
{
...
return "something";
}
这就假设编译器一定会帮我们合并字符串,然而那也不一定.
11 输出" 6".
混合运算时的数据类型转换次序:int -- unsigned -- long -- double.
另外,char和short必定转换为int,float必定转换为double.
12 p = (int *)((unsigned int)a + 1);
代码的意图是想使p指向数组的第二个元素,但通常的写法是:p=a+1.这里存在这样的问题:a是个常量地址,
a+1指向下一个数组元素,而((unsigned int)a + 1)指向下一个内存地址.如果地址是字节计数的,则p指向的
是数组第一个元素的第二个字节.还有一个效果就是:在RISC上该printf语句会出异常,因为不允许非对齐访问
(mis-aligned access).对齐访问就是访问2字节变量的地址要能被2整除,4字节变量的地址要能被4整除,etc.
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。