C语言面试题(二)--------华为

 

1. 什么是预编译,何时需要预编译:

1、总是使用不经常改动的大型代码体。

2、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。


2. 上述三个有什么区别?

char * const p;

char const * p

const char *p

解答:我们先来看看解题思路

*********************************************************

Bjarne在他的The C++ Programming Language里面给出过一个助记的方法: 
把一个声明从右向左读。 

char * const cp; ( * 读成 pointer to ) 
cp is a const pointer to char 

const char * p; 
p is a pointer to const char; 

char const * p; 
同上因为C++里面没有const*的运算符,所以const只能属于前面的类型。 


C++标准规定,const关键字放在类型或变量名之前等价的。

const int n=5;    //same as below
int const m=10;


const int *p;    //same as below  const (int) * p
int const *q; // (int) const *p


char ** p1; 
// pointer to pointer to char
const char **p2;
// pointer to pointer to const char
char * const * p3;
// pointer to const pointer to char
const char * const * p4;
// pointer to const pointer to const char
char ** const p5;
// const pointer to pointer to char
const char ** const p6;
// const pointer to pointer to const char
char * const * const p7;
// const pointer to const pointer to char
const char * const * const p8;
// const pointer to const pointer to const char
 

以下是有关const的扩展,以便更好的掌握const的用法和含义

关键字const有什么含意? 
我只要一听到被面试者说:“const意味着常数”,我就知道我正在和一个业余者打交道。去年Dan   Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded   Systems   Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。) 
如果应试者能正确回答这个问题,我将问他一个附加的问题: 
下面的声明都是什么意思? 
const   int   a; 
int   const   a; 
const   int   *a; 
int   *   const   a; 
int   const   *   a   const; 
前两个的作用是一样,a是一个常整型数

第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)

第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)

最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。

如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由: 
1. 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。) 
2. 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 
3. 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。

****************************************************************************

根据上面的推理,我们不难得出答案:

char * const p;      //p是指向字符型的常量指针,p的值不可以修改

char const * p//p是指向字符常量的指针,指向的字符常量值不可以改

const char *p //char const *p一样


 

3. 解释下列输出结果

 

char str1[] = "abc";

 

char str2[] = "abc";

 

const char str3[] = "abc";

 

const char str4[] = "abc";

 

const char *str5 = "abc";

 

const char *str6 = "abc";

 

char *str7 = "abc";

 

char *str8 = "abc";

 

cout << ( str1 == str2 ) << endl;

 

cout << ( str3 == str4 ) << endl;

 

cout << ( str5 == str6 ) << endl;

 

cout << ( str7 == str8 ) << endl;

结果是:0 0 1 1

解答:str1,str2,str3,str4是数组变量,它们有各自的内存空间;

str5,str6,str7,str8是指针,它们指向相同的常量区域。



 

4. 以下代码中的两个sizeof用法有问题吗?[C]

 

void UpperCase( char str[] ) //  str 中的小写字母转换成大写字母

 

{

 

for( size_t i=0; i<sizeof(str)/sizeof(str[0]); ++i )

 

if( 'a'<=str[i] && str[i]<='z' )

 

str[i] -= ('a'-'A' );

 

}

 

char str[] = "aBcDe";

 

cout << "str字符长度为: " << sizeof(str)/sizeof(str[0]) << endl;

 

UpperCase( str );

 

cout << str << endl;

 

答:函数内的sizeof有问题。根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的str是一个静态定义的数组,因此其大小为6,函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4



 

5. 指出下面代码的输出,并解释为什么。

 

main()

 

{

 

int a[5]={1,2,3,4,5};

 

int *ptr=(int *)(&a+1);

 

 

 

printf("%d,%d",*(a+1),*(ptr-1));

 

}

 

输出:2,5

 

*(a+1)就是a[1]*(ptr-1)就是a[4],执行结果是25

 

&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5int

 

int *ptr=(int *)(&a+1);

 

ptr实际是&(a[5]),也就是a+5

 

原因如下:

 

&a是数组指针,其类型为 int (*)[5];

 

而指针加1要根据指针类型加上一定的值,

 

不同类型的指针+1之后增加的大小不同

 

a是长度为5int数组指针,所以要加 5*sizeof(int)

 

所以ptr实际是a[5]

 

但是prt(&a+1)类型是不一样的(这点很重要)

 

所以prt-1只会减去sizeof(int*)

 

a,&a的地址是一样的,但意思不一样,a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].




 

6.请问以下代码有什么问题:

 

1).

 

int main()

 

{

 

char a;

 

char *str=&a;

 

strcpy(str,"hello");

 

printf(str);

 

return 0;

 

}

 

没有为str分配内存空间,将会发生异常

 

问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。

 

2).

 

char* s="AAA";

 

printf("%s",s);

 

s[0]='B';

 

printf("%s",s);

 

有什么错?

 

"AAA"是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。

 

cosnt char* s="AAA";

 

然后又因为是常量,所以对是s[0]的赋值操作是不合法的。




7. 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)

答: #define      SECONDS_PER_YEAR     (60 * 60 * 24 * 365)UL
我在这想看到几件事情:
1) #define语法的基本知识(例如:不能以分号结束,括号的使用,等等)
2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少
秒而不是计算出实际的值,是更清晰而没有代价的
3)意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数
4)如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。

 


 

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


9.float(*p[3])(int)的含义

答:p[3]是一个数组.
p[3]存储的值是指针.
该指针指向一个函数.
该函数有一个int型的型参.
函数返回一个float型的值.
合起来念就是:
p[3]是一个指针数组.其值是指向以int为型参,返回值为float型的函数的指针.


10有以下表达式:

 

int a=248; b=4;int const c=21;const int *d=&a;

 

int *const e=&b;int const *f const =&a;

 

请问下列表达式哪些会被编译器禁止?为什么?

 

*c=32;    d=&b;     *d=43;   e=34;    e=&a;    f=0x321f;

答:除了d = &b, 其他都是错误的.

 

*c 这是个什么东东,禁止

 

*d 说了是const 禁止

 

e = &a 说了是const 禁止

 

const *f const =&a; 禁止

 

posted on 2011-12-11 17:10  朝寒雨晚来风  阅读(313)  评论(0编辑  收藏  举报

导航