面试题简记

本文通过一道面试题分析了在C语言中同一个表达式中使用多次自增运算可能导致的非预期结果。在一个for循环中,由于unsigned char类型的变量ucCmdNum在表达式中被多次自增,导致超出其取值范围,从而产生死循环。这提醒程序员注意自增运算符的使用,避免在同一个表达式中对同一变量进行多次操作。
1.Which of the following assignment is not correct? (java)
   A. float f = 11.1;X          //(11.1默认是double)
   B. double d = 5.3E12;
   C. double d = 3.14159;
   D. double d = 3.14D.

A ,浮点数的赋值是带有小数点的数字缺省是double型的,如果在浮点数后面加f或者F则是float,后面加d或者D则是double,科学计数法形式的浮点数也是double型的,而double的精度比float高,将一个高精度的double赋值给一个低精度的float时需要进行强制类型转换,反之则不需要。


2.给定一个整形变量a,写两段代码,第一个设置a的bit 3,第二个清除a的bit 3,在以上两操作中要求保持其它位不变。
 
 
#define BIT3 (0x1<<3)
static int a;
void set_bit3(void)
{a|=BIT3;}
void clear_bit3(void)
{a&=~BIT3;}

析:
|操作,当操作数为1时,结果为1,当操作数为0时,结果不变.
&操作,当操作数为1时,结果不变,当操作数为0时,结果为0
因此,设置a的bit 3为1的方法就是将a和00001000作|操作,这样其他位都不变,只有第3位变成1
清除a的bit3的方法就是将a和11110111作&操作,这样第3位变成0,其他位不变
3
int *p=NULL;// 对于指针的赋值操作,NULL和0等价
1.为什么指针变量定义时一定要初始化? 
答:因为你首先要理解一点.内存空间不是你分配了才可以使用
只是你分配了之后使用才安全,为什么要进行对他初始化呢
因为,如果你没对他初始化,而引用这个指针并却其指向的内存进行修改
因为指针未被初始化,所以指针所指向的也是随机的,他是个野指针,如果你引用指针,并修改这个指针所指向的内容,而如果这个指针所指向的内容恰好是另外一个程序的数据的话,你将其进行修改了,就会导致另外一个程序可能不能正常运行了.所以使用前一定要进行初始化
2.指针变量初始化为NULL是什么意思?
答:意思是说,强指针变量置空,初始化为NULL,使它不指向任何内容,这样引用她也不会出现上面的问题

总之一点,记住在使用指针之前要对它进行初始化操作就可以了

4.试题6:下列程序执行后的结果是什么?【美国某著名计算机嵌入式公司2005年9月面试题】 //ps:(注意)
#include <stdio.h>
char *getMemory(void)
{
	char p[] = "hello world";
	return p;
}
int main(void)
{
	char *str = NULL;
	str = getMemory();
	printf("%d\n", *str);
	return 0;
}


答案:可能是乱码,也有可能正常输出。因为p[]数组为函数内的局部自动变量,getMemory返回的是指向“栈内存”的指针,该指针的地址不是NULL,在函数返回后,内存已经被释放,其原先的内容已经被清除,新内容不可知,可能是乱码。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。
5.	试题8:  //ps:###############################注意分析
void Test( void )
{
	char *str = (char *)malloc( 100 );
	strcpy( str, "hello" );
	free( str ); 
	... //省略的其它语句
}
解答:
	试题8结果是:(1)能够输出hello (2)未进行内存是否申请成功的判断(3)指针str可能变成野指针
  	试题8存在与试题7同样的问题,在执行 char *str = (char *) malloc(100); 后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:str = NULL;
6、下面调用的三个函数有问题吗?如果有,是什么问题?
static int *foo_1()
{
	int x = 10; 
	return &x;
}
static int *foo_2()
{
	int *ptr; //野指针
	//int *p = NULL; //空指针
	*ptr = 10; //段错误
	return ptr;
}
static int *foo_3()
{
	int *ptr = (int *)malloc(sizeof *ptr);
	return ptr;
}
static void test26()
{
	foo_1();
//	foo_2();
//	foo_3();
答:
f1显然有问题,它返回一个局部变量的指针,局部变量是保存在stack中的,退出函数后,局部变量就销毁了,保留其指针没有意义,因为其指向的stack空间可能被其他变量覆盖了
f2也有问题, ptr是局部变量,未初始化,它的值是未知的,*ptr不知道指向哪里了,直接给*ptr赋值可能会覆盖重要的系统变量,这就是通常说的野指针的一种.
6.以下是求一个数的平方的程序,请找出错误:
#define SQUARE(a)((a)*(a))
int a=5;
int b;
b=SQUARE(a++);

结果是25,我怎么觉得应该是30啊?

经过预处理器处理后,代码变成了 int b = (a++)*(a++);也就是下面那个代码的形式, 而C标准并未规定编译器在一个表达式中何时进行自增运算,故结果可能是5*5(先把a取出,最后进行两次自增),也可能是(5*6)(先取出第一个a,自增后取出第二个a),输出25说明你的编译器采用了前面那种方式罢了。

所以建议不要在同一个表达式中对同一变量施行多次自增运算

一个表达式中多个自增运算与编译器有关

7、 #define Max_CB 500 void LmiQueryCSmd() { unsigned char ucCmdNum; for(ucCmdNum=0;ucCmdNum<Max_CB;ucCmdNum++) { printf("ok\n"); } } 死循环,unsigned char的取值范围是0~255

char 范围是-128~127, unsigned char范围是0~255;
8.以下代码中的两个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' );                     
}
int main()
{
	char str[] = "aBcDe";
	cout << "str字符长度为: " << sizeof(str)/sizeof(str[0]) << endl;
	UpperCase( str );
	printf("%s\n", str);
	return 0;
}


答:
函数内的sizeof有问题。根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的str是一个静态定义的数组,因此其大小为6,函数内的str实际只是一个指向字符的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。
注意:数组名作为函数参数时,退化为指针。数组名作为sizeof()参数时,数组名不退化,因为sizeof不是函数.
一个32位的机器,该机器的指针是多少位,只要看地址总线的位数就行了。80386的机子是32的地址总线。所以指针的位数就是4个字节了。
9、下面的程序会有什么样的输出呢?    //////////////ps:(注意)
#include <stdio.h>
int main()
{
    int i=43;
    printf("%d\n",printf("%d",printf("%d",i)));
    return 0;
}


参考答案:程序会输出4321,你知道为什么吗?要知道为什么,你需要知道printf的返回值是什么。printf返回值是输出的字符个数。

10、请问下面的程序输出什么?  /////////////////////////ps:(注意)
#include <stdio.h>
int main()  
{
    int i = 10;
    printf("i : %d\n",i);
    printf("sizeof(i++) is: %d\n",sizeof(
i++));
    printf("i : %d\n",i); //10
    return 0;
}
参考答案:如果你觉得输出分别是,10,4,11,那么你就错了,错在了第三个,第一个是10没有什么问题,第二个是4,也没有什么问题,因为是32位机上一个int有4个字节。但是第三个为什么输出的不是11呢?居然还是10?原因是 ,sizeof不是一个函数,是一个操作符,其求i++的类型的size,这是一件可以在程序运行前(编译时)完成的事情,所以,sizeof(i++)直接就被4给取代了,在运行时也就不会有了i++这个表达式。


11、考查sizeof和自加操作  //////////////////////////////////ps:(注意)
main()

int i=3; 
int j; 
//double k;
//j = sizeof(++i + ++k); 
j = sizeof(++i + ++i); 
printf("i=%d j=%d", i ,j);
}


这段程序的输出是:
(a) i=4 j=2
(b) i=3 j=2
(c) i=3 j=4
(d) i=3 j=6


答案(c)
sizeof 操作符给出其操作数需要占用的空间大小,它是在编译时就可确定的,所以其操作数即使是一个表达式,也不需要在运行时进行计算.     
( ++i + ++i )是不会执行的,所以 i 的值还是3

12.
//extern可以引用其他文件中的全局变量,也可以引用该文件下面声明的全局变量。
1、代码作用域是一件很诡异的事,下面这个函数返回值是什么?    ////////////////////////////////////////ps:(注意)
int x = 5;
int f() 
{
int x = 3;
{
  extern int x;
  return x;
}
}
答案:5


12。 C++中经典的单向链表反转 (自己先试试)

  struct linka {
 int data;
3linka* next;
 };
void reverse(linka*& head) {
 if(head ==NULL)
return;
 linka *pre, *cur, *ne;
 pre=head;
 cur=head->next;
 while(cur)
 {
 ne = cur->next;
 cur->next = pre;
 pre = cur;
 cur = ne;
 }
 head->next = NULL;
 head = pre;
 }
其中比较难理解的是linka*& head,传入的其实就是linka *的类型就可以了,linka *是表示linka类型的指针,&表示head的地址,也就是linka的指针www.2cto.com

另外需要熟悉的是head->next,其实有点像C#中的head.Next,就是structure中的一个属性.

首先定义3个指针,分别是前中后,然后当中间那个指针非空,就是当前不是空,就做循环里的事情

注意的是这个算法里面next是在循环里面赋值的

每次循环都把current指向previous了,然后大家都往后移一个,next=current->next必须在current改变方向之前做,否则改变了方向之后current的next就变成previous了。

最后跳出循环之后,将header的next首先置空,因为head变成了最后一个node了。然后head就变成了previous,因为当时 current和next都为NULL了,只有previous为最后一个节点(或者说这时候应该是第一个非空节点,也就是head)

终于把整个算法理解了一遍,最后想想其实挺简单,但是能用c++写出来也不太容易,特别是在面试的时候。


13.9、如果有“int a=5, b=3”,则在执行“!a&&b++”后,a和b的值分别是多少?【中国某著名综合软件公司2005年面试题】 /////////////P:
答案: 
5和3.  这是逻辑短路问题。因为“!a”运算结束后,整个表达式的值已肯定为假,所以不必再去计算后面的式子了。
//总结:(表达式1)&&(表达式2)  当表达式1为假时,表达式2不再执行
//      (表达式1)||(表达式2)  当表达式1为真时,表达式2不再执行 



14.

int main()
{
int i=-20;
unsigned j=10;
cout<<i+j<<endl;
system("pause");
return 0;
}

输出结果:4 294 967 286

2^32-10=4 294 967 296-10=4 294 967 286

两个兼容的不同类型的操作,哪个能表示更大的数就转为哪个类型。例如short+long,就要转为long;unsigned+signed,就要转为unsigned。

在32位机上
unsigned int 最大可表示2^32 - 1
int最大可表示2^31-1
这样int就转为了unsigned int,由于y<0,即y的最高位符号位是1,转为unsigned int后最高为不再是符号位,而是一个最高位的正数,于是两者相加就会是一个很大的正数了。

也许你会问为什么“哪个能表示更大的数就转为哪个类型”?

因为计算机(注意,是计算机,不是编译器,也不是操作系统)总是希望能尽量大的囊括结果,防止溢出产生错误(尽管有人会说向下溢出怎么办,但别忘了,负数加减换成补码也成了正数的加运算)。

int -20在32位计算机中的存储形式为:1111 1111 1111 1111 1111 1111 1110 1100 第一位1为符号位,负

unsigned 10在32位计算机中的存储形式为:0000 0000 0000 0000 0000 0000 0000 1010

unsigned+int 后结果转为unsigned存储,故int -20转化为unsigned int,由于符号位为1,转为unsigned int 后最高位不再是符号位,而是一个最高正的位数,两者相加为

1111 1111 1111 1111 1111 1111 1111 0110 即为4 294 967 286。


14、 下面的代码输出是什么,为什么? (中国台湾某著名CPU生产公司2005年面试题)
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? printf("> 6") : printf("<= 6");
}
这个问题测试你是否懂得C语言中的整数自动转换原则。
混合运算时的数据类型转换次序:int –> unsigned –> long –> double。
另外,char和short必定转换为int,float必定转换为double。
不管如何,这无符号整型问题的答案是输出是 ">6"。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。 unsigned int类型的数据与int类型的数据相运算后,自动转化为unsigned int类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6,是一个unsignged int类型的数4294967282。这一点对于频繁用到无符号数据类型的嵌入式系统来说是非常重要的。因此需要弄明白“C语言中的自动类型转换的规则”。



15.
#include<stdio.h>
int main()
{
char *p1 =(char *)0x00008000;
int *P2 =(int *)0x00008000;
    p1++;
    P2++;
printf("p1=0x%x---p2=0x%x",p1,P2);
//getchar();
return 0;
}

结果:
p1=0x8001  p2=0x8004
P1加一表示指针向后移动1个字节
P2加一表示指针向后移动4个字节
 
 
16.

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

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

printf("%d",*(p-1)); 

这个问题的关键是理解 &a 
a是一个数组名,也就是数组的首地址。
对a进行取地址运算符,得到的是一个指向数组的指针!!!!这句话尤为重要!
也就相当于
int (*p) [5] = &a;
p是一个指针,它指向的是一个包含5个int元素的数组!!
那么执行p+1后,p的偏移量相当于 p + sizeof(int) * 5 !!
而程序中强制将指针p转换成一个int* 那么 p -1 其实就是 p - sizeof(int)
所以,p -1 指向了数组中得最后一个元素,也就是 5

答案 5

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值