现在的位置: 首页 > 综合 > 正文

绝对能够测试你的C语言功力的几个问题!

2013年12月28日 ⁄ 综合 ⁄ 共 11082字 ⁄ 字号 评论关闭
另外,在实际的VC++教学中,发现很少有真正精通了C语言编程的学员,一般都有或多或少概念不是完全清楚的问题,特别是一些需要丰富的实战经验才能体会和明白的问题,如字符串,指针,类型转换,定义指向函数的指针类型,这也是导致学习VC++困难的一个原因。下面有几个简单测试将能发现你对C语言的掌握情况。
int x=35;
char str[10];
//问:strlen(str)和sizeof(str)的值分别是多少?
strcpy(str,"www.it315.org"/*共13个字母*/);
//问:此时x和strlen(str)的值分别是多少?
str="it315.org";//编译能通过吗?
char *pstr;
strcpy(pstr,"http://www.it315.org");
//上句编译能通过吗?运行时有问题吗?
const char *p1;
char * const p2;
//上面两句有什么区别吗?
p1=(const char *)str;
//如果是p1=str;编译能够通过吗?明白为什么要类型转换?类型转换的本质是什么?
strcpy(p1,"abc");//编译能够通过吗?
printf("%d",str);//有问题吗?
pstr=3000;//编译能过吗?如果不行,该如何修改以保证编译通过呢?
long y=(long)pstr;//可以这样做吗?
int *p=str;
*p=0x00313200;
printf("%s",str);//会是什么效果?提示0x31对应字符'1',0x32对应字符'2'。
p=3000;//p+1的结果会是多少?
char *pc=new char[100];//上述语句在内存中占据几个内存块,怎样的布局情况?
void test(char **p)
{
       *p=new char[100];
}//这个编译函数有问题吗?外面要调用这个函数,该怎样传递参数?
//能明白typedef int (*PFUN)(int x,int y)及其作用吗?
 C语言测试题的讲解分析
下面是网友Jackie214发布的答案,其认真态度令我深受感动.这些题是我2000年在做培训时为学员们设计的,但我已经近6年没有接触过vc++下的c语言编程了,有9年没接触unix下的c语言编程了,所以,除了一些核心思想令我终生难忘外,其他一些语法细节和当初出题的意图何在,我也差不多忘记了!前天把这个测试题发布出来,是因为前一阵才知道我多年前写的一些内容又要被别人加入到其书籍中出版(我只是对一些人惯于借鉴别人的东西而当作自己成就的性格很不赞赏,但这般行为在商业上来说是无可厚非,甚至是值得学习,只是我自己做不出这样的事情来,反而有点自己没胆量吃葡萄,就说吃葡萄的人酸),当时非常生气,就又翻腾出了这些快被自己遗忘的东西,前几天就顺便把这个测试题发表在自己的blog里了.我没有受过正规的计算机教育,除了自学过谭浩强的那本入门级的c语言,也没有看过c语言方面的经典名著,很多东西都是自己瞎摸索出来的,所以有自己的讲课思路,但不一定严谨,还请看客多多谅解!
另外,我有过一段开发实践和教学经历后,回过头来看谭老爷子的那本c语言书,觉得写得真的很不好,没抓主重点,讲解也不通俗,很多人都这么认为,但也没有人能下定决心,吃上两年的苦,写一被绝对好过老谭的书,我曾经有过这般想法,但一直未能实施!我期望书能通过试验来让读者轻松学懂,并且学懂后还不容易忘记,所以要把原理性的东西用通俗易懂的方式表现出来,还要高度总结出核心思想,如果谁有这方面的心愿,不妨与我合作!
下面是网友Jackie214发布的答案,随后是我的一点补充.
Jackie214 发表于2006-05-19 11:47 AM  IP: 61.154.9.*
int x=35;
char str[10];
//问:strlen(str)和sizeof(str)的值分别是多少?
// strlen(str) 值不确定,strlen根据'/0'确定字符串是否结束。
// sizeof(str)=10 sizeof一个数组为数组长度
strcpy(str,"www.it315.org"/*共13个字母*/);
//问:此时x和strlen(str)的值分别是多少?
// x 为35
// strcpy(char* dest, const char* src)
// 根据src来复制dest,依照src的'/0'决定复制的长度,而dest必须要提供足够的长度,这里会引起溢出,strlen返回13,但是数组外部的数据已经被破坏
//(作者注:我下面给出了更确切的答案 )
str="it315.org";//编译能通过吗?
// 数组不能赋值,只能初始化。char str[10] = "it315.org";
// 而且初始化时编译器会检查数组的长度与初始化串的长度是否匹配
char *pstr;
strcpy(pstr,"
http://www.it315.org");
//上句编译能通过吗?运行时有问题吗?
// 可以通过编译,但是pstr指向了常量区,运行时最好只做读操作,写操作不保险
//(作者注:我下面给出了更确切的答案 )
const char *p1;
char * const p2;
//上面两句有什么区别吗?
// const char* 和 char const* 一样,都是表示指向常量的字符指针。
// char * const 表示指向字符的常量指针
p1=(const char *)str;
//如果是p1=str;编译能够通过吗?明白为什么要类型转换?类型转换的本质是什么?
// 可以通过编译。关于常量与非常量指针的关系是这样的:
// const指针可以指向const或者非const区域,不会造成什么问题。
// 非const指针不能指向const区域,会引起错误。
strcpy(p1,"abc");//编译能够通过吗?
// 不能通过,strcpy( char*, const char*); char* 不能指向const char*
printf("%d",str);//有问题吗?
// 没有问题,输出的是str的地址信息。
pstr=3000;//编译能过吗?如果不行,该如何修改以保证编译通过呢?
// 不能通过,char* pstr表示pstr是个字符指针,不能指向3000的整形变量。
// 修改的话,可以这样:pstr = (char*)3000,把pstr指向3000这个地址;
long y=(long)pstr;//可以这样做吗?
// 可以,y的值为pstr所指的地址。不过如果是纯粹要地址的话,最好是用unsigned long。
int *p=str;
*p=0x00313200;
printf("%s",str);//会是什么效果?提示0x31对应字符'1',0x32对应字符'2'。
// 首先编译未必会过关,有些编译器可能不允许int * 直接指向char*。最好是改为int *p = (int*)str;
// 过关了效果就是什么东西都没有。int *p=str; p为str所指的地址,*p表示修改了str所指向的内存。
// 由于sizeof(int)在32位机上,int有4个字节(其实具体要看编译器的配置文件,好像是limit.h,一般是4个字节)所以修改了str[0]-str[3]
// 由于0x00313200头尾都是0,所以字符串为'/0'开头,什么都打印不出来。这里有个Big-endin和little-endin的问题。以0x31323334为例
// little-endin的机器上面,0x31323334在内存中排列顺序为34 33 32 31,输出为4321,如INTEL芯片的pc
// big-endin机器上面为31 32 33 34 ,输出为1234,如IBM POWERPC
p=3000;//p+1的结果会是多少?
// 3000+sizeof(int); 指针+1均为原来地址加上sizeof(指针所指的数据类型)
char *pc=new char[100];//上述语句在内存中占据几个内存块,怎样的布局情况?
// 本身pc会占用函数栈一个4字节的指针长度(具体是否为4个字节要看机器和编译器)。
// new会在堆上申请100个字节sizeof(char)的连续空间。
void test(char **p)
{
*p=new char[100];
}//这个编译函数有问题吗?外面要调用这个函数,该怎样传递参数?
// 该程序没有问题。需要在函数中对指针所指的地址进行变化是必须传人指针的地址。
// 原因是这样的:如果传入的为指针本身,在函数调用的时候,实参会被复制一个实例,这样就不是原来的指针了,对该指针本身进行的任何改变都不能传递回去了。
// 可以这样理解,如果传入的参数为int,那么对int本身的值的改变就传不回去啦,加个*也是一样的。
//能明白typedef int (*PFUN)(int x,int y)及其作用吗?
// 定义了一个函数指针类型的宏,这样PFUN就表示指向返回值为int,且同时带2个int参数的函数指针类型了。
// 可以用来定义这样的变量:
// 比如有个函数为int fun( int x, int y );
// PFUN p = fun;
//(作者注:我下面给出了更确切的答案)
--------------------------------------------------------------------------------------------------------------
下面是我的一点补充:
第二题:
int x=35;
char str[10];
strcpy(str,"
www.it315.org"/*共13个字母*/);
//问:此时x和strlen(str)的值分别是多少?
答:strlen的值为13,在VC++环境下,x的值是要改变的(其他编译器下没试,).虽然表面上看来,在程序中并没有修改x的值,但是实际运行的结果是上面的x的值发生了修改,这是因为strcpy以后,把多余的数据拷贝进了str的邻居(int类型的x)中,所以x的数据也就变了.这是一个曾让我刻骨铭心的问题,在我刚出道时遇到这个问题,虽然在朋友的帮助下解决了这个问题,但一直不明白x的值为何变了,只有最后走上培训教师的岗位,才开始梳理自己曾经的困惑,才开始总结以前的经验供学员们借鉴.我觉得这个题目的价值非常之大,它能引起学员对字符串拷贝越界问题的足够重视,并且通过这个问题更能明白字符串的处理是怎么回时,更能明白字符串与字符数组的关系:字符串就是一个字符数组,只是把这个字符数组用在处理串的函数中时,这些函数不考虑数组的长度,只是记住数组的首地址,从首地址开始处理,并在遇到0时结束处理,
第四题:
char *pstr;
strcpy(pstr,"
http://www.it315.org");
//上句编译能通过吗?运行时有问题吗?
答: 编译可以通过,但是pstr没有进行有效的初始化,它指向了一个不确定的内存区,运行时会出现内存不可写错误!
最后一题:
//能明白typedef int (*PFUN)(int x,int y)及其作用吗?
答:函数指针最大的用处在于它可以被一个模板方法调用,这是我在学java的设计模式时领悟到的.例如,有两个函数的流程结构完全一致,只是内部调用的具体函数不同,如下所示:
void func1()
{
         //一段流程代码和面向方面的代理,如安全检查,日志记录等
         int sum = add( x , y);
        //一段流程代码和面向方面的代理,如安全检查,日志记录等
}
void func2()
{
         //与func1完全相同的一段流程代码和面向方面的代理,如安全检查,日志记录等
         int difference = sub( x , y);
        //与func1完全相同的一段流程代码和面向方面的代理,如安全检查,日志记录等
}
那么,可以只定义一个函数,如下所示
void func(PFUNC p)
{
         //与func1完全相同的一段流程代码和面向方面的代理,如安全检查,日志记录等
         int difference = p( x , y);
        //与func1完全相同的一段流程代码和面向方面的代理,如安全检查,日志记录等
}
调用程序在调用时,让参数p分别指向add和sub函数就可以了.
对于其他题目的讲解,由于我目前写作和工作的重点已完全不在c语言方面了,也没有时间一一解答,就借用Jackie214发布的答案来回应大家.
[点击此处收藏本文]   发表于 2006年05月19日 15:13:00
 
 
David 发表于2006-05-20 1:39 AM  IP: 218.20.119.*
支持啊!
 
reader 发表于2006-05-20 2:30 AM  IP: 131.107.0.*
char *pstr;
strcpy(pstr,"
http://www.it315.org");
//上句编译能通过吗?运行时有问题吗?
// 可以通过编译,但是pstr指向了常量区,运行时最好只做读操作,写操作不保险
答案有问题
 
clever101 发表于2006-05-20 11:01 AM  IP: 221.235.57.*
*****************************************
char *pstr;
strcpy(pstr,"
http://www.it315.org");
//上句编译能通过吗?运行时有问题吗?
// 可以通过编译,但是pstr指向了常量区,运行时最好只做读操作,写操作不保险
答案有问题
******************************************
你自己写过测试程序就知道没有问题。
 
reader 发表于2006-05-20 1:34 PM  IP: 131.107.0.*
---你自己写过测试程序就知道没有问题。
char *strcpy(
char *strDestination,
const char *strSource
);
The strcpy function copies strSource, including the terminating null character, to the location specified by strDestination. The behavior of strcpy is undefined if the source and destination strings overlap.
看看定义, "pstr指向了常量区.."? 这句根本不对
 
秦始皇 发表于2006-05-21 1:28 AM  IP: 211.161.61.*
这种水平还能当培训教师?
感觉真是。。。。。。。。。。
第二题:
int x=35;
char str[10];
strcpy(str,"
www.it315.org"/*共13个字母*/);
//问:此时x和strlen(str)的值分别是多少?
答:strlen的值为13,在VC++环境下,x的值是要改变的(其他编译器下没试,).虽然表面上看来,在程序中并没有修改x的值,但是实际运行的结果是上面的x的值发生了修改,这是因为strcpy以后,把多余的数据拷贝进了str的邻居(int类型的x)中,所以x的数据也就变了.
上面这个题你知道原因吗?好像没有说,建议你看看内存分配的基本原理吧,看看变量是如何产生的。你说的这个好像怎么都不是原理吧。根据你说的现象我可以断定你的x和str[10]是局部变量,如果你把他们都变成全局的试试看情况如何,嘿嘿。
如果看了书还是想不明白的话就发邮件吧。
fkingq@yahoo.com
 
liyuan02 发表于2006-05-21 11:28 AM  IP: 219.133.128.*
//sizeof(str)=10 sizeof一个数组为数组长度
应该是分配的字节数
char str[10]; //sizeof(str)=10
int str[10]; //sizeof(str)=40
vc6.0
 
Tony Huang 发表于2006-05-21 2:33 PM  IP: 218.82.49.*
第二题:
int x=35;
char str[10];
strcpy(str,"
www.it315.org"/*共13个字母*/);
//问:此时x和strlen(str)的值分别是多少?
答:strlen的值为13,在VC++环境下,x的值是要改变的(其他编译器下没试,).虽然表面上看来,在程序中并没有修改x的值,但是实际运行的结果是上面的x的值发生了修改,这是因为strcpy以后,把多余的数据拷贝进了str的邻居(int类型的x)中,所以x的数据也就变了.
这个问题很显然的啊!因为栈是向前增长的,所以先分配了x的空间,再分配str的空间以后就会造成str之后紧接x,而strcpy是不检查缓冲区溢出错误的,所以数据自然就溢出到了x里面。
在现在的规范里面基本上都要求使用strncpy来代替strcpy以防止这种愚蠢的缓冲区溢出错误发生了
 
corer 发表于2006-05-21 4:08 PM  IP: 211.137.211.*
char *pstr;
strcpy(pstr,"
http://www.it315.org");
//上句编译能通过吗?运行时有问题吗?
// 可以通过编译,但是pstr指向了常量区,运行时最好只做读操作,写操作不保险
这个有很大问题啊
1 strcpy及strncpy第一个入口参数应该是数组,指针不对
或如 str[10]; pstr=str; 使用strcpy或strncpy 第一个参数必须的指向一段内存区域,是字符串拷贝,是重新分配一段内存区域,把第二个参数拷过去。
2 第二个参数长度如果超过第一参数数组的长度会产生溢出
这种水平还能当培训教师?
感觉真是。。。。。。。。。。
呵呵 大学老师都这样,根本不知道细节问题
 
rhrh8744 发表于2006-05-21 5:19 PM  IP: 222.212.172.*
跟我一样,半灌水,响叮当!还冒充全威!最好马上不要当老师了,别误了别人
 
Ken 发表于2006-05-21 8:41 PM  IP: 219.136.176.*
第一题vc里不是随机分配,而是4个字节4个字节地分配
从str[9]到str[12]到时分配12个字节
而第13个是x,所以'/0在x的第一个字节之后
如果str[13]就strlen(str)=17了
 
PX 发表于2006-05-21 8:43 PM  IP: 222.70.94.*
不错,不错,辛苦张老师了,不过希望张老师能下次能深入一点,不要总是在c语言本身上费口舌了,讲讲你的经验什么的会更好!
 
bw 发表于2006-05-21 10:25 PM  IP: 218.28.73.*
局部变量是在堆栈上,从高到低排序,所以int x 在char str[]后面,str溢出影响x。
如果是全局变量,在数据段里,从低到高排序,int x 在char str[]前面,str溢出不影响x。
题目中没有写清楚是局部变量还是全局变量,一般认为是全局变量,所以看了很纳闷。
 
china 发表于2006-05-21 10:36 PM  IP: 59.44.194.*
N久以前听过他的课
现在回忆起来,只能用两个字
垃圾
 
学会感恩 发表于2006-05-22 2:35 AM  IP: 222.79.135.*
我们国人大都喜欢对人不对事啊!!
悲哀~~
感觉有些人素质低的不能再低了!!
好多人根本不是在和张老师讨论技术,而是千方百计的挖苦,讽刺张老师,这样的人,注重一辈子都是小丑一个~
今天第一次看张老师的blog,看来妒忌,眼红张老师的人实在太多了!!
张老师,成大事,不能和小人计较,更不能和无知,无耻,卑鄙,下流的小丑计较!
 
reader 发表于2006-05-22 3:39 AM  IP: 24.16.129.*
同意 学会感恩
而且张老师在他的补充已经很婉转地纠正了Jackie214错的答案
 
feizhuangxuan 发表于2006-05-22 8:43 AM  IP: 222.185.254.*
提醒楼上的:
C语言是学好其他语言最好最使用的工具,所以,张SIR用C来给我们讲的
 
Jackie214 发表于2006-05-22 8:59 AM  IP: 61.154.9.*
啊,第四题我做错了。张老师说的是对的。
我说的那种情况是这样的:
char* p = "
http://www.it315.org";
还有sizeof数组的时候,给出的是数组的字节长度。liyuan02是对的,我说的不准确。
 
BEN 发表于2006-05-22 9:22 AM  IP: 211.100.21.*
TrackBack来自《【转】C语言测试题的讲解分析》:
还是围绕指针和数组来做的讲解,指针和数组真是把双刃剑,编程效率和运行效率永远是一对负熵。
 
可爱的男生 发表于2006-05-22 9:25 AM  IP: 58.49.196.*
I think you are right.
 
对酒当歌 发表于2006-05-22 11:31 AM  IP: 61.167.60.*
很多细节问题没必要那么死抠,用到了自然就熟悉了,我觉得大家把精力放在这上面没什么必要。国内的程序员就是太关注这些东西了,但是这些问题只是考察了一个人掌握一种工具的程度,反映不出一个程序员的真正素质。比如说可能很多人没注意过C和汇编代码是如何映射的,但是你一旦开始做一些控制领域的编程,自然而然的就需要看那些反汇编的代码了。
 
青白 发表于2006-05-22 12:05 PM  IP: 222.90.74.*
坚决同意对酒当歌的观点!!!
 
老笨鸟 发表于2006-05-22 12:40 PM  IP: 221.219.36.*
//能明白typedef int (*PFUN)(int x,int y)及其作用吗?
// 定义了一个函数指针类型的宏,这样PFUN就表示指向返回值为int,且同时带2个int参数的函数指针类型了。
// 可以用来定义这样的变量:
// 比如有个函数为int fun( int x, int y );
// PFUN p = fun;
typedef 不是定义了一个“宏”,而是定义了一个“类型”,宏和类型是完全不同的概念。
 
flash_he 发表于2006-05-22 1:47 PM  IP: 222.66.83.*
有些人文章都没看完就乱评论。垃圾
 
bigfatsea 发表于2006-05-22 2:11 PM  IP: 60.241.113.*
第二题:
int x=35;
char str[10];
strcpy(str,"
www.it315.org"/*共13个字母*/);
//问:此时x和strlen(str)的值分别是多少?
答:strlen的值为13,在VC++环境下,x的值是要改变的(其他编译器下没试,).虽然表面上看来,在程序中并没有修改x的值,但是实际运行的结果是上面的x的值发生了修改,这是因为strcpy以后,把多余的数据拷贝进了str的邻居(int类型的x)中,所以x的数据也就变了.这是一个曾让我刻骨铭心的问题,在我刚出道时遇到这个问题,虽然在朋友的帮助下解决了这个问题,但一直不明白x的值为何变了,只有最后走上培训教师的岗位,才开始梳理自己曾经的困惑,才开始总结以前的经验供学员们借鉴.我觉得这个题目的价值非常之大,它能引起学员对字符串拷贝越界问题的足够重视,并且通过这个问题更能明白字符串的处理是怎么回时,更能明白字符串与字符数组的关系:字符串就是一个字符数组,只是把这个字符数组用在处理串的函数中时,这些函数不考虑数组的长度,只是记住数组的首地址,从首地址开始处理,并在遇到0时结束处理.
-------------------------------------------------------
这段没搞明白,张老师没讲出原理,后来“秦始皇”得讽刺式评论中提到内存分配方式得问题,并断定张老师是用局部变量,这一点经过偶验证证实乐,不过秦兄语焉不详,还望高人继续指点:)此外,在GCC下测试结果和VC下不同。
以下是偶写得测试代码以及在GCC和VC7.1下的测试结果:
-------------------------------------------------------
#include <stdio.h>
int x=35;
char str[10];
void func(){
int x=53;
char str[10];
strcpy(str,"
www.it123.org");/*共13个字母*/
printf("Local Var: %d, %s/n", x, str);
}
int main(int argc, char *argv[])
{
strcpy(str,"
www.it315.org");/*共13个字母*/
printf("Global Var: %d, %s/n", x, str);
func();
return 0;
}
-------------------------------------------------------
GCC:
Global Var: 35,
www.it315.org
Local Var: 53,
www.it123.org
VC:
Global Var: 35,
www.it315.org
Local Var: 103,
www.it123.org
-------------------------------------------------------
 
tjuzhangrui 发表于2006-05-22 2:13 PM  IP: 61.189.35.*
我已经近6年没有接触过vc++下的c语言编程了,有9年没接触unix下的c语言编程了,所以,除了一些核心思想令我终生难忘外……
-------------------------------------------------------
张老师给我们讲讲啥核心思想让您终生难忘吧。
 
guanzhong 发表于2006-05-22 4:49 PM  IP: 210.72.232.*
那些"编译能否通过吗?"这样的问题没有什么好处,编译是否通过可以依赖编译器进行检查,但是有些代码根本就不能那样写.甚至strcpy这样的函数都是很不安全的,应尽量使用能够提供长度的如strncpy,lstrcpyn这样的函数.
 
absurd 发表于2006-05-22 9:06 PM  IP: 211.161.63.*
这都是最基本的知识,这些知识都不掌握,谁敢用你的程序?当然也不要限于语言本身,理解了计算机原理,这些东西都是一目了然的。去年我出的C语言面试题目就包含了其中一些内容,先在公司内部摸底考了一下,发现只要有经验的人都考得不错。
 
qtext 发表于2006-05-23 12:07 AM  IP: 220.163.59.*
int x=35;
char str[10];
strcpy(str,"
www.it315.org"/*共13个字母*/
前面有人说strcpy会把x的值破坏,这是不可能的,x分配在低地址
str分配在高地址(vc编译生的目标码是这样的),只会把str后面的变量破坏,绝不会破坏str前面的变量,push 指令才会使stack向上长(低地址),分配局部变量是按顺序,不要生搬硬套。

抱歉!评论已关闭.