曲径通幽论坛

标题: 常见 C 语言面试题目 [打印本页]

作者: beyes    时间: 2009-6-9 16:24
标题: 常见 C 语言面试题目
试题一:请解释关键字 static,并说明至少两种 static 的用途

解答
静态变量( 以 static 作为修饰符的变量 ) 分为两种:全局静态变量和局部静态变量。全局静态变量是在所有函数之外定义的静态变量,局部静态变量是在某个函数内( 如 main 函数 )定义的变量。

静态变量存储在内存的静态存储区,静态存储区在程序的整个运行期间都存在。未经初始化的静态变量会被程序自动初始化为 0 ( 自动对象的值是任意的,除非被显式初始化 )。

全局静态变量的作用域是从定义之处开始到文件结尾,全局静态变量对其它文件是不可见的。而局部静态变量只是在定义它的函数内有效。

static 的用途为

(1)限制变量的作用域
(2)设置变量的的存储域
作者: beyes    时间: 2009-6-9 17:06
例题二:请分析程序是否正确,如果错误指出原因,如果正确写出程序运行结果并说明原因
#include <stdio.h>

int a = 3;

void f()
{
    int a = 1, i;
    printf("%d\\n",a);

    for(i=0; i<1; i++)
    {
        int a = 2;
        printf("%d\\n", a);
    }

}

int main()
{
    f();
    printf("%d\\n",a);
}
说明
程序正确。这里注意一点的是,在 f() 函数里边的 a 是局部变量,在 main() 之外的是全局变量。局部变量和全局变量同名无所谓,各自输出各自的值。
作者: beyes    时间: 2009-6-9 18:04
例题三:static 全局变量与普通的全局变量有什么区别? static 局部变量和普通局部变量有什么区别?static 函数与普通函数有什么区别

解答
在定义变量时,全局变量之前再冠以 static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。两者在存储方式上并无不同。两者的区别在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的 函数使用,因此可以避免其他源文件使用该变量。把普通的全局变量改变为静态全局变量是改变了它的作用域,限制了它的使用范围。

普通全局变量所在的函数每次被调用都会被重新等一并分配存储空间,而 static 局部变量不会,它的值始终保存着。static 局部变量只被初始化一次,下一次使用时依旧是上次的值。

static 函数( 即静态函数,在函数定义时加上了 static 关键字 )与普通函数作用域不同,它仅存在于本文件中。只在当前源文件中使用的函数应该说明为内部函数( 即加上 static 关键字 )。内部函数应该在当前源文件中声明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这个函数的源文件要包含这个头文件。

程序的普通局部变量存在于堆栈中,全局变量、static 局部变量存在于静态存储区中。
作者: beyes    时间: 2009-6-9 19:02
例题四:写出下面程序的运行结果
#include <stdio.h>

int inc(int a)
{
    return (++a);
}

int multi(int *a, int *b, int *c)
{
    return ( *c = *a * *b );
}

int (*p)(int);

void show( int(*fun)(int*, int*, int*), int arg1, int *arg2)
{
    p = inc;
    int temp = p(arg1);
    fun(&temp, &arg1, arg2);
    printf("%d\\n", *arg2);
}

int main()
{
    int a;
    show(multi, 10, &a);
    return 0;
}
      
说明
首先,函数输出结果为 110 。
这里,有一点,  p = inc; 这个语句和 p = &inc;的一样的意思。这里主要考察的是函数指针的用法。
作者: beyes    时间: 2009-6-10 00:40
例题五:清找出下面代码的所有错误。这段代码的功能是把一个字符串到序,如 "abcd" 倒叙后变为 "dcba" 。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    char *src = "hello world";
    char *dest = NULL;
   
    int len = strlen(src);

    dest = (char *)malloc(len);
    char *d = dest;
    char *s = &src[len];

    while(len-- != 0)
        *d++ = *s--;

    printf("%s\\n",dest);
   
    free(dest);
   
    return 0;
}
分析
上面程序中有几处错误:
1、dest = (char *)malloc(len); 这一句应写成 dest = (char *)malloc(len + 1);这个函数的本意是向系统申请一块内存以便存储 "hello, world"这个字符串的倒序。strlen()这个函数返回的是字符串的实际大小,即不包括每个字符串最后都有的结束字符 '\\0' 。使用 malloc 申请内存时必须申请 len + 1 个字节以便存放整个字符串。

2、char *s = &src[len];这个语句使用不恰当。注意数组的元素的个数与下标的关系。这里 len下标实际上是指向了 '\\0',所以要改为 char *s = &src[len - 1];

3、在 *d++ = *s-- 后面还要加上一句 *d = '\\0' 以把 '\\0' 复制过去。这句语句等效于
*d = *s;
d++;
s--;

4、printf() 函数后要增加一个语句 free(dest),使用完 malloc() 分配内存后要使用 free() 函数将其释放,否则可能造成内存泄露。

作者: beyes    时间: 2009-6-10 15:52
例题六:请写出下面程序的运行结果
[font=[object htmloptionelement]]
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void fun(char str[100])
{
    printf("%d\
", sizeof(str));
}
int main()
{
    char str[] = "Hello";
    char *p1 = str;
    int n = 10;
    char *p2 = (char *)malloc(100);
    printf("%d\
", sizeof(str)); /*求数组长度,包括'\\0'*/
    printf("%d\
", strlen(str)); /*求字符串长度,不包括'\\0'*/
    printf("%d\
", sizeof(p1));  /*求char型指针长度*/
    printf("%d\
", strlen(p1));  /*求字符串长度,不包括'\\0'*/
    printf("%d\
", sizeof(n));   /*求 n 这个整型变量的长度*/
    printf("%d\
", sizeof(p2));  /*求 p2 指针类型长度*/
    fun(p2);
    return 0;
}
运行及输出
beyes@linux-beyes:~/C> ./sizeof_and_strlen.exe
6
5
4
5
4
4
4
说明
此种题在招聘 C 语言软件开发工程师时很常见。主要测试对 sizeof() 和 strlen() 的理解。
第一个输出为 6,测的是 str 这个数组的长度,包括末尾的 '\\0'.
第二个输出为 5,测的是字符串自身,不包括结尾的 '\\0'.
第三个输出为 4,由于 p1 是一个指针,sizeof() 求的是变量类型的长度。一个指针变量存放的是一个地址,而 32 位机器的一个地址都是 32 位即 4 个字节。
第四个输出为 5,测的是 p1 指向的字符串的长度,和第二个一样。
第五个输出为 4,一个整型变量的存储长度一般一般是 4 个字节,早期的机器使用 2 个字节来保存 int 型变量。
第六个输出为 4, 这里同样求的是指针变量 p2 的长度,和第三个输出一样。
第七个输出为 4,这里调用了 fun 函数。因为数组作为函数的参数时,对系统来说,char str[100] 和 char *str 是一样的。

作者: beyes    时间: 2009-6-10 16:03
例题七:下面的这段小程序有什么问题?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    char a;
    char *str = &a;
   
    strcpy(str, "hello");
    printf("%s\\n", str);
   
    return 0;
}
说明
这个程序虽然可以编译通过,但运行时会导致程序崩溃,提示 ”段错误“ 。这里,str 指向的变量只有 1 个字节,而复制的字符串 "hello" 需要 6 个字节的空间。应该在 strcpy  函数调用前先分配动态内存并赋给指针 str :
str = (char *) malloc( strlen("hello") + 1 );
在函数返回之前,释放申请的动态内存:
free(str);

作者: beyes    时间: 2009-6-10 16:13
例题八: int( *s[10] )(int) 表示的是什么?

首先,[ ] 符号的优先级比 * 号的高,所以 *s[] 表示的是一个指针数组。而 int (*)(int) 表示的是一个指向参数为 int 类型并且返回值也同为 int 类型的函数指针。
所以,整个语句的含义是:定义了一个含有 10 个函数指针的数组。数组的长度为 10,数组元素的类型是指向函数的指针。
作者: beyes    时间: 2009-6-10 16:24
例题九:假设只知道一个数组名,如何确定这个数组的长度

假定这个数组的数组名是 array,可以用一下语句确定一个数组的长度:

 int length = sizeof( array ) / sizeof( array[0] );

sizeof( array ) 是数组的总长度,而 sizeof( array[0] ) 是数组的第一个元素的长度。
作者: beyes    时间: 2009-6-10 17:43
例题十:“ char a[10]; int len = strlen(a); ”,len 等于多少?

上面这两条语句虽然语法上是合法的,但这种用法是错误的。
strlen 从数组 a 首地址出发一直到遇到 '\\0' 才结束,计算从数组 a 的第一个元素开始到 '\\0' 总共有多少个字符(不包括字符 '\\0')。由于字符数组没有初始化,数组元素的值是未知的,所以 len 的值也是未定的。

如果对数组进行初始化:
char a[10] = "hello";
int len = strlen(a);
则 len 的值是确定的,为 5 。此时 sizeof(a) 的值为 10,它获取的是数组 a 的总长度。
作者: beyes    时间: 2009-6-10 18:50
例题十一:下面的程序编译时会报错,请指出错误并改正
#include <stdio.h>

int main(void)
{
    int **p;
    int array[ 100 ];
   
    p = &array;
   
    return 0;
}
gcc 编译提示
beyes@linux-beyes:~/C> gcc -c temp.c
temp.c: In function ‘main’:
temp.c:8: warning: assignment from incompatible pointer type
由报告信息可知,这是指针类型不兼容的警告。
两个指针只有在指向的数据的类型一致时才可以互相赋值。

&array 是一个指向长度为 100 的数组的指针,而  p 是一个指向 “整型指针” 的指针,所以两者不匹配。主函数可以修正如下:
#include <stdio.h>

int main(void)
{
    int **p, *q;
    int array[ 100 ];
   
    q = array;

    p = &q;
   
    return 0;
}
这里,用指针 q 进行了过渡。array 是数组首地址,而数组中的内容又是整型数,那么当然可以把这个数组首地址赋给整型指针 q 。
作者: beyes    时间: 2009-6-10 21:29
例题十二:写一个程序,以递归方式反序输出一个字符串。如给定字符串 "abc" 输出 "cba".

测试程序
 #include <stdio.h>
#include <string.h>
void reverse(char *p)
{
    if(*p == '\\0' )
        return;
       
    reverse(p + 1);
   
    printf("%c", *p);
}
int main()
{
    char str[100];
    int len;
    char *p;
    printf("请输入字符串:");
    fgets(str, 100, stdin);
    len = strlen(str);
    if(str[len - 1] == '\
'
)
        str[len-1] = '\\0';
    p = str;
    reverse(p);
    printf("\
");
    return 0;
}
      
说明
程序中,之所以用 fgets() 而不用 gets() 是因为前者是个安全的函数,而后者因为不检查边界,所以很危险,不使用。
    if(str[len - 1] == '\\n')
        str[len-1] = '\\0';
上面两行语句是因为用 fgets() 会读取进一个回车符 '\\n',这里将其吃掉。
      
作者: beyes    时间: 2009-6-10 22:59
例题十三:下面这个程序是否有问题?
#include <stdio.h>
#define MAX 255

int main()
{
    unsigned char a[MAX], i;
   
   for(i=0; i<=MAX; i++)
              printf("%d ", a[i]);

   printf("\\n");
   return 0;
}
程序分析
使用 gcc 编译,可以编译通过。但如果运行程序会导致数组越界死循环。这个看似简单的程序,实则“暗藏杀机”。程序里存在两个问题:
1、数组越界
上面的 i<=MAX 应该写成 i<MAX

2、死循环
这点比较隐蔽。注意到,a 和 i 变量也是无符号字符型,这种类型的数据占据一个字节内存即 8 位,所表示的数组的范围 ( 以二进制表示):00000000~11111111,也就是 0 ~255。
数组 a 的元素范围为 a[0] 到 a[254] 。当 i = 255 时,这是可以的,因为数组并不检查对数组元素的访问是否越界。然而,当 i 执行 i++ 操作时,i 的值班不会变成 256,而是变为 0 了!这是因为无符号字符型的存储空间只有 8 位的缘故: 11111111 + 1 = 100000000,最高位 1 舍去。所以,在执行判断语句 i<=MAX 时,条件又成立了, i 又从 0 开始循环。如此周而复始,导致死循环。
作者: beyes    时间: 2009-6-11 16:13
例题十四
写一个实现字符串拷贝的函数。给定字符串拷贝函数 strcpy 的原型:
char *strcpy(char *dest, char *src)

程序如下
char *strcpy(char *dest, char *src)
{
    char *ret_string;
   
    if ( ( dest == NULL ) || ( src == NULL ) )   {
         printf("arg wrong");
         return NULL;
    }
    ret_string = dest;
    while( ( *dest++  =  *src++) ) != '\\0' );
    return ret_string;
}
说明
(1) 一开始如果不检查指针 dest 和 src 是否为 NULL ,那说明不注重代码的健壮性。

(2) 检查指针是否有效时,使用 if ( ( !dest) || (!src) ) 或者 if ( ( dest == 0 ) || ( src == 0 ) ) ,则表现为书写代码不规范,说明答题者不知道使用 NULL 常量的好处,因为使用 0 而没有用 NULL ,会降低程序的可维护性。

(3) 字符拷贝时,不要忘记了拷贝最后的 '\\0'。上面程序中 while 语句是最精简的写法,但为了提高程序的可读性和可维护性,最好还是尽量写得容易让别人看懂:
while ( *src != '\\0' )
{
    *dest = *src;
     dest++;
     src++;
}
*dest = '\\0'




欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) Powered by Discuz! X3.2