495-21-30
21.怎样建立和理解非常复杂的声明?例如定义一个包含N个指向返回指向字符的指针的函数的指针的数组。
答:这个问题至少有以下三种答案:
(1).
char *(*(*a[N]));
(2).用typedef逐步完成声明
typedef char *pc;
typedef pc fpc();
typedef fpc *pfpc;
typedef pfpc fpfpc();
typedef fpfpc *pfpfpc;
pfpfpc a[N];
(3).使用cdecl程序,他可以在英文描述和C语言源码之间相互翻译,你只需要提供用自然语言描述的类型,cdecl就能翻译成对应的C语言声明。
C语言令人困惑的原因在于,他们由两个部分组成:基本类型和声明符,后者包含了被声明的标识符。声明符也可以包含字符* 、[]和()。表明这个名称是基本类型的指针,数组,以及为返回类型的函数或者某种组合。例如,在:
char *p;
中,基本类型是char,标识符是p,声明符是* p。这表明* p是一个char。
解读复杂C声明的一种方法是遵循“从内到外”的阅读方法,并谨记[]和()比 * 的结合度更紧,例如,对声明:
char *(*pfpc )();
我们可以看出pfpc是一个函数(从()看出)的指针(从内部的 * 看出),而函数则返回char类型的指针(从外部 * 可以看出)。当我们后来使用pfpc的时候 * (* pfpc)() (pfpc所指的函数的返回值指向的值)是一个char型。
另一种分析这种复杂声明的方法是,遵循“声明模仿使用”的原则逐步分解声明:
- (* pfpc)() 是一个char
(* pfpc)() 是一个 指向char的指针
(* pfpc) 是一个返回char型指针的函数
pfpc 是一个 指向返回char型指针的函数的指针
如果你希望将复杂声明想这样表达的更清楚,可以用一系列typedef把上面的分析表达出来。
22.如何声明返回指向同类型函数的指针的函数?我在设计一个状态机,用函数表示没种状态,每个函数都会返回一个指向下一个状态的函数的指针,可我找不到任何方法来声明这样的函数-感觉我需要一个返回指针的函数,返回的指针指向的又是返回指针的函数·····,如此往复,以至无穷
答:你不能直接完成这个任务。一种方法是让函数返回一个一般的函数指针。然后在传递这个指针的时候进行适当的类型转换:
typedef int (*funcptr)();
typedef funcptr (*ptrfuncptr)();
funcptr start(), stop();
funcptr state1(), state2(), state3();
void statemachine() {
ptrfuncptr state = start;
while(state != stop)
state = (ptrfuncptr)(*state)();
}
funcptr start() {
return (funcptr)state1;
}
(第二个类型定义ptrfuncptr隐藏了一些十分隐晦的语法。如果没有这个定义,变量state就必须声明为funcptr(* state)(),而调用的时候就得用(funcptr(* )())(* state)()这样令人困惑的类型转换了)
另一种方法是让每个函数都返回一个结构,结构中仅包含了一个返回该结构的函数和指针
struct funtchunk {
struct functhunk (*func)();
}
struct functhunk start(), stop();
struct functhunk state1(), state2(), state3();
void statemachine() {
struct functhunk state = {start};
while(state.func != stop)
state = (*state.func());
}
struct functhunk start () {
struct functhunk ret;
ret.func = state1;
return ret;
}
23.能否声明和传入数组大小一致的局部数组,或者有其他参数指定大小的参数数组。
答,办不到
25.函数只定义了一次,调用了一次,但编译器提示非法重声明了
答:在作用域内没有声明就调用(可能是第一次调用在函数的定义之前)的函数被认为声明为:
extern int f();
即未声明的函数被认为返回int型且接受个数不定的参数,但是参数个数必须确定,且其中不能有“窄”类型。如果之后函数的定义不同,则编译器就会警告类型不符,返回非int型,接受任何“窄”参数类型或可变参数的函数都必须在调用前声明。(最安全的方法就是声明所有函数,这样就可以用函数原型来检查参数传入是否正确)
另一个可能的原因是该函数与某个头文件中声明的另一个函数同名
30。如何判断哪些标识符可以使用,哪些被保留了?
答:命名空间的管理有些麻烦。问题是你不能使用那些已经被实现使用过的标识符,这会导致一堆“重复定义”错误,或者更坏的情况下,静悄悄地替换了实现的标识符,然后把一切都搞的一团糟。同时你可能也想确保后续的版本不会侵占你所保留的名称。(拿一个已经调试的,正常工作的生成程序在新版的编译器下编译,链接,结果却因为命名空间或其他问题导致编译失败。)