1.关键字static的作用是什么?
有三个明显的作用:
1)在函数体内,一个被声明为静态的变量在这个函数被调用过程中维持其值不变
2)在模块内(但在函数体外),静态的变量可以被模块内所有函数访问,但不能被模块外其他函数访问
3)在模块内,一个被声明为静态的函数只能被这一模块内的其他函数调用,也就是,这个函数被限制在声明它的模块的本地范围内使用
2. 如何保证头文件的内容只被包含一次,请举例说明。
#ifndef HEAD_TEST_H
#define HEAD_TEST_H
// 在这里写你的头文件内容
#endif
3.请写出一个实现c语言可变参数函数的实例(任意)。
#include
#include
#include
#include
int average( int first, ... )
{
int count=0,
int i=first;
int sum=0;
va_list maker; //va_list即是char*表明maker是一个字符型的指针。
va_start(maker,first); // maker指向first之后的第一个可变的参数,而frist是作为一个固定参数,因为它在…之前。
while(i!=-1)
{
sum+=i;
count++;
i=va_arg(maker,int);//返回maker列表的当前值,并指向列表的下一个位置
}
va_end(maker);
return sum/count;
}
void main(void)
{
printf( "Average is: %d
", average( 2, 3, 4,4, -1 ) );
}
va_start(arg_ptr, argN):使参数列表指针arg_ptr指向函数参数列表中的第一个可选参数,说明:argN是位于第一个可选参 数之前的固定参数,函数参数列表中参数在内存中的顺序与函数声明时的顺序是一致的。如果有一va函数的 声明是void va_test(char a, char b, char c, …),则它的固定参数依次是a,b,c,最后一个固定参数argN为 c,因此就是va_start(arg_ptr, c)。
va_arg(arg_ptr, type):返回参数列表中指针arg_ptr所指的参数,返回类型为type,并使指针arg_ptr指向参数列表中 下一个参数。
va_end(arg_ptr):清空参数列表,并置参数指针arg_ptr无效。说明:指针arg_ptr被置无效后,可以通过调用 va_start()、va_copy()恢复arg_ptr。每次调用va_start() / va_copy()后,必须得有相应的 va_end()与之匹配。参数指针可以在参数列表中随意地来回移动,但必须在va_start() … va_end()之内。
4.请定义一个二叉树数据结构,要求包括一个联合类型的成员。
并说明一下 联合和结构的区别。
结构和联合有下列区别:
1) 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合转只存放了一个被选中的成员, 而结构的所有成员都存在。
2) 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。
5 请说明下面声明的含义
char *(*(*f(char *(*para)(char *)))[2])();
参考:
复杂指针解析
因为C语言所有复杂的指针声明,都是由各种声明嵌套构成的。如何解读复杂指针声明呢?右左法 则是一个既著名又常用的方法。不过,右左法则其实并不是C标准里面的内容,它是从C标准的声明规定中归纳出来的方法。C标准的声明规则,是用来解决如何创 建声明的,而右左法则是用来解决如何辩识一个声明的,两者可以说是相反的。右左法则的英文原文是这样说的:
The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.
这段英文的翻译如下:
右左法则:首先从最里面的圆括 号看起,然后往右看,再往左看。每当遇到圆括号时,就应该掉转阅读方向。一旦解析完圆括号里面所有的东西,就跳出圆括号。重复这个过程直到整个声明解析完毕。
笔者要对这个法则进行一个小小的修正,应该是从未定义的标识符开始阅读,而不是从括号读起,之所以是未定义的标识符,是因为一个声明里面可能有多个标识 符,但未定义的标识符只会有一个。
现在通过一些例子来讨论右左法则的应用,先从最简单的开始,逐步加深:
int (*func)(int *p);
首先找到那个未定义的标识符,就是func,它的外面有一对圆括号,而且左边是一个*号,这说明 func是一个指针,然后跳出这个圆括号,先看右边,也是一个圆括号,这说明(*func)是一个函数,而func是一个指向这类函数的指针,就是一个函 数指针,这类函数具有int*类型的形参,返回值类型是 int。
int (*func)(int *p, int (*f)(int*));
func被一对括号包含,且左边有一个*号,说明func是一个指针,跳出括号,右边也有个括号,那么func 是一个指向函数的指针,这类函数具有int *和int (*)(int*)这样的形参,返回值为int类型。再来看一看func的形参int (*f)(int*),类似前面的解释,f也是一个函数指针,指向的函数具有int*类型的形参,返回值为int。
int (*func[5])(int *p);
func右边是一个[]运算符,说明func是一个具有5个元素的数组,func的左边有一 个*,说明func的元素是指针,要注意这里的*不是修饰 func的,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合,因此*修饰的是func[5]。跳出这个括号,看右边,也 是一对圆括号,说明func数组的元素是函数类型的指针,它所指向的函数具有int*类型的形参,返回值类型为int。
int (*(*func)[5])(int *p);
func被一个圆括号包含,左边又有一个*,那么func是一个指针,跳出括号,右边是一 个[]运算符号,说明func是一个指向数组的指针,现在往左看,左边有一个*号,说明这个数组的元素是指针,再跳出括号,右边又有一个括号,说明这个数 组的元素是指向函数的指针。总结一下,就是:func是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向具有int*形参,返回值为int类 型的函数。
int (*(*func)(int *p))[5];
func是一个函数指针,这类函数具有int*类型的 形参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组。
要注意有些复杂指针声明是非法的,例如:
int func(void) [5];
func是一个返回值为具有5个int元素的数组的函数。但C语言的函数返回值不能为数组,这是因为如 果允许函数返回值为数组,那么接收这个数组的内容的东西,也必须是一个数组,但C语言的数组名是一个右值,它不能作为左值来接收另一个数组,因此函数返回 值不能为数组。
int func[5](void);
func是一个具有5个元素的数组,这个数组的元素都是函数。这也 是非法的,因为数组的元素除了类型必须一样外,每个元素所占用的内存空间也必须相同,显然函数是无法达到这个要求的,即使函数的类型一样,但函数所占用的 空间通常是不相同的。
作为练习,下面列几个复杂指针声明给读者自己来解析。
int (*(*func)[5][6])[7][8];
int (*(*(*func)(int *))[5])(int *);
int (*(*func[7][8][9])(int*))[5];
实际当中,需要声明一个复杂指针时,如果把整个声明写成上面所示的形式,对程序可读性是一大损害。应该用typedef来对声明逐层分解,增强可读性,例 如对于声明:
int (*(*func)(int *p))[5];
可以这样分解:
typedef int (*PARA)[5];
typedef PARA (*func)(int *);
这样就容易看得多了。
答案,同时给出用typedef的分解方法:
int (*(*func)[5][6])[7][8];
func 是一个指向数组的指针,这类数组的元素是一个具有5X6个int元素的二维数组,而这个二维数组的元素又是一个二维数组。
typedef int (*PARA)[7][8];
typedef PARA (*func)[5][6];
int (*(*(*func)(int *))[5])(int *);
func是一个函数指针,这类函数的返回值是一个指向数组的指针,所指 向数组的元素也是函数指针,指向的函数具有int*形参,返回值为int。
typedef int (*PARA1)(int*);
typedef PARA1 (*PARA2)[5];
typedef PARA2 (*func)(int*);
int (*(*func[7][8][9])(int*))[5];
func是一个数组,这个数组的元素是函数指针,这类函数具有int*的形 参,返回值是指向数组的指针,所指向的数组的元素是具有5个int元素的数组。
typedef int (*PARA1)[5];
typedef PARA1 (*PARA2)(int*);
typedef PARA2 func[7][8][9];
6.请从下面两方面描叙一下linux下生成core dump的配置方法
1)core文件的生成开关和大小限制 ulimit -c filesize 设置生成core文件的大小
2)core文件的名称和生成路径
proc/sys/kernel/core_pattern可以控制core文件保存位置和文件名格式
"/corefile/core-%e-%p-%t"
%p - 添加pid
%u -添加当前uid
%g - 添加当前gid
%s -添加导致产生core的信号
%t -添加core文件生成时的unix时间
%h -添加主机名
%e -添加命令名
7. 给出如下C程序,在linux下使用gcc编译:
1 #include "stdio.h"
2 #include "sys/types.h"
3 #include "unistd.h"
4
5 int main()
6 {
7 pid_t pid1;
8 pid_t pid2;
9
10 pid1 = fork();
11 pid2 = fork();
12
13 printf("pid1:%d, pid2:%d
", pid1, pid2);
14 }
已知从这个程序执行到这个程序的所有进程结束这个时间段内,没有其它新进程执行。
1) 请说出执行这个程序后,将一共运行几个进程。
四个进程
2) 如果其中一个进程的输出结果是“pid1:1001, pid2:1002”,写出其他进程的输出结果(不考虑进程执行顺序)。
pid1:0, pid2:1002 pid1:1001, pid2:0 pid1:0, pid2:0