实验室招新系列(一)

这次放题目出来的时间不太好,临近期末,而且距离第一次出题中间隔的时间也有点长。原本我是想要早点将题目出完的,在招新计划上出了点小分歧,导致最后放题目时间完了点。
但是不管怎样,题目质量还是有的(至少我这么认为咯),其中很多是华为的面试题,在这里作为练习用的题目。

1.请写出下列代码的输出内容

#include  <stdio.h> 
int main(void) 
{ 
int a,b,c,d; 
a=10; 
b=a++; 
c=++a; 
d=10*a++; 
printf("b,c,d:%d%d%d",b,c,d); 
return 0; 
}
10 12 120

点评:这个是可以用来直接面试的,题目难度不大,主要考察对++的理解

2.全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
这个题在招新群里有很多人提出疑问,提出对题目不理解,对此表示略无语;
同时也有很多人在交上来的答案中直接写“可以”或者“不可以”,把后面的为什么三个字直接扔掉不看;
对于一直强调自己没有学过的人,我只想说,你都学了,那我还出这题出来,是我吃多了?!没学过自己去查去啊。
对于这个题,我原本的意思是让自己去敲代码,然后测试能不能被包含。但是事实证明,我对刚入校学习不到半年的学弟学妹期望太高,并不是所有人都会有这样的思维,这个确实是我的过。

题目的关键点也在于“为什么”,而不是可以与不可以。
题解:可以,在不同的C文件中以static形式来声明同名全局变量。   可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错
程序的局部变量存在于(堆栈)中,全局变量存在于(静态区 )中,动态申请数据存在于( 堆)中。

在这里就不给出参考程序了。根据上面提到的思路自己再写一写代码测试一下,这里仅仅提供思路。

遇到程序方面的问题,解决方法:搜索引擎 && 写程序测试

3.对static的理解
(1)static全局变量与普通的全局变量有什么区别?
答:全局变量的说明之前再加以static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。
(2)static局部变量和普通局部变量有什么区别?
答:static局部变量只被初始化一次,下一次依据上一次结果值;
(3)static函数与普通函数作用域有什么不同点?
答:仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件。
(4)static函数与普通函数有什么区别?
答:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝程序的局部变量存在于(堆栈)中,全局变量存在于(静态区)中,动态申请数据存在于(堆)中。

点评:本题侧重底层原理。其实任何一门语言,都是会有static的用法的,都大同小异,所以在这里着重给出一个关于static用法的题,是希望能够对static以及底层原理有一个更深层次的理解。

4.找错:(有点难度的~)
分别找出三个函数中的错误~

void test1()   
{ 
    char string[10]; 
char* str1="0123456789";
strcpy(string, str1);   
} 
这里string数组越界,因为字符串长度为10,还有一个结束符‘\0’。所以总共有11个字符长度。string数组大小为10,这里越界了。   PS:使用strcpy函数的时候一定要注意前面目的数组的大小一定要大于后面字符串的大小,否则便是访问越界
void test2() 
{
         char string[10], str1[10];   
for(i=0; i<10;i++)   
{   
str1[i] ='a';   
} 
         strcpy(string, str1);   
}
这里有一个一眼就能看出的问题,那就是变量i没有定义,这在代码编译阶段编译器可以帮你发现,很容易搞定。然而很多问题是自己造成的漏洞,编译器是帮不上什么忙的。这里最大的问题还是str1没有结束符,因为strcpy的第二个参数应该是一个字符串常量。该函数就是利用判断第二个参数的结束符来得到是否拷贝完毕。所以在for循环后面应加上str1p[9] = '\0'; 
PS:字符数组和字符串的最明显的区别就是字符串会被默认的加上结束符‘\0’
void test3(char* str1)   
{ 
char string[10];   
if(strlen(str1)<=10)   
{
strcpy(string, str1);  
     }   
} 
这里的问题仍是越界问题。strlen函数得到字符串除结束符外的长度。如果这里是<=10话,就很明显越界了。 

小结:上面的三个找错的函数,主要是考查对字符串和字符数组的概念的掌握以及对strcpy函数和strlen函数的理解。本题在大一这个时候,如果能全部独立地找出错误,说明功底非常好!

5.指出下面代码的输出,并解释为什么。
(对地址掌握的深入挖潜)

int main() 
{ 
int a[5]={1,2,3,4,5}; 
int *ptr=(int *)(&a+1); 
printf("%d,%d",*(a+1),*(ptr-1)); 
} 

详解:
(a+1)就是a[1],(ptr-1)就是a[4],执行结果是2,5

&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
int ptr=(int )(&a+1);
则ptr实际是&(a[5]),也就是a+5 原因如下:
&a是数组指针,其类型为 int (*)[5];
而指针加1要根据指针类型加上一定的值, 不同类型的指针+1之后增加的大小不同
a是长度为5的int数组指针,所以要加 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.func(1) = ?

int func(int a)
{
    int b;
    Switch(a)
{
    case 1: b=30;
    case 2: b=20;
    case 3: b=10;
    case 4: b=0;
}
return b;
}

7.搞笑题(轻松一下):
C语言中有一种非常神奇的符号,叫做趋向于,长这个样子“–>”。

#include<stdio.h>
int main(void)
{
    int i=10;
    while (i-->1)   //i趋向于1
    {
        printf ("%d",i);
    }
    return 0;
}

这样可以输出987654321.
请写出你对–>的理解^_^
题解:第六题第七题都是拿来高校的。没有趋向于这个符号,捏造的。六七题看不出来的,可以自挂东南枝。2333333

8.这是一道很严肃的题:
请想出一种方法,让程序输出1/3+1/6=0.5
聪明的你,一定可以找到解决的办法~~
那么接下来问题又来了,你知道在计算机中1/3和1/6是怎么存储的吗?
1/3+1/6在计算机中又是怎么算出等于0.5的呢?
题解:这个题贴近底层原理,涉及到进制转换;在这里作为了解,主要是希望能够通过搜索引擎自己去查找相关资料。

具体说来:
1/3和1/6都是无限循环小数,但是在CPU中,位数都是有限的;所以实际上1/3和1/6在内存里不是长度无限的。
在64位机中测试:
1/3在内存中是0x3eaaaaab
1/6在内存中是0x3E2AAAAB
换算成小数的时候分别是0.3333333432…和0.1666666716…即在内存中就是不精确的
因为计算机内部都是二进制的,这两个数值用二进制表示就是:
1/3 -> 0.010101010101010101010110
1/6 -> 0.001010101010101010101011
现在计算机做浮点都是硬浮点,那么具体的计算就是0.100000………01
因为这个时候精度就已经多出一位,此时CPU就要做一个取舍,,最后的位数是01的默认舍掉,所以最后的结果刚好就是0.1000……0,转成十进制就是0.5

参考代码:

#include <stdio.h>
#include <string.h>
int main(void)
{
    /**
     *memcpy是c和c++使用的内存拷贝函数,
     *memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。
     *关于这个函数更详细的内容,自行百度
     */
     /**
      *这里的x,y都必须在一个数字上用到小数位,才能准确表示浮点型
      */
    floatx=1.0/3, y=1.0/6;
    unsigned inta, b;
    memcpy(&a,&x, sizeof(a));
    memcpy(&b,&y, sizeof(b));
    printf("%.100f\n %.100f\n %.100f\n", x, y, x+y);
    printf("%x%x\n", a, b);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值