有日子没有学习C语言了, 前些天在看windows程序设计时, 按照win的体系结构,在VC 6里面找到
下面一段代码,发现自己的C语言功底实在是差之又差。代码为我看到的C代码,至目前为止,还只能理解个大概;
估计要慢慢体会了。
DECLARE_HANDLE(HDC);
#define DECLARE_HANDLE(name)
struct name##__
{
int unused;
};
typedef struct name##__ *name
上面的代码等我理解后再和大家分享吧, 下面说说这一次的C语言面试题。
8、找错题
找出下面几个函数的错误:
试题1:
void test1()
{
char string[10];
char* str1 = "0123456789";
strcpy( string, str1 );
}
/*
这个题目咋一看,没有任何错误, 给strcpy()函数传递的两个实参参数类型均能满足要求。
但是细心一看我们会发现这个函数存在越界问题,"0123456789"这个字符串
的长度为 strlen("0123456789") + 1 = 11 , 而很显然string[10],不可能存储这么大的空间
通常在使用strcpy函数时一定要考虑源串、和目的串的大小问题。
*/
函数改成下面的形式可能会健壮一些:
int StrCpy(const char *source; char dest[])
{
if( NULL==source || NULL == dest || ( strlen(dest) < strlen(source) ) )
return 1; // 返回值=1 表示复制失败
else
strcpy(dest,source);
return 0; //返回值=0 表示复制成功
}
试题2::
void test2()
{
char string[10], str1[10];
int i;
for(i=0; i<10; i++)
{
str1 = 'a';
}
strcpy( string, str1 );
}
/*
这个题目考查了两个问题:
1、 数组的首地址是常量,不可以作为左值, 即str1是一个常量,
它代表整个数组的首地址。
2、 第二数组的引用需要用下标,除了初始化时可以int iArray[10]={1,2}
这样赋值外,在其他地方不可以批量给数组元素赋值。
3、 同时strcpy复制函数是针对具有'\0'的字符类型变量,因此这个函数赋
值同样存在赋值越界的情况。
*/
改成下面的方式估计会健壮一些:
void test2( )
{
char string[10],
str1[10] ;
for(int i=0; i<10; i++)
str1[i] = 'a';
str[9]='\0';
strcpy(string , str1);
}
试题3:
void test3(char* str1)
{
char string[10];
if( strlen( str1 ) <= 10 )
{
strcpy( string, str1 );
}
}
//试题3同样存在越界的可能性。如果strlen(str1)=10, 则实际上str1占用的空间是11个。
//strlen函数返回的长度没有计算末尾'\0'字符。 因此需要注意。
改为下面的方式可能会更健壮:
void test3(char* str1)
{
char string[10];
if( strlen( str1 ) < 10 && NULL != str1 )
strcpy( string, str1 );
}
试题4:
void GetMemory( char *p )
{
p = (char *) malloc( 100 );
}
void Test( void )
{
char *str = NULL;
GetMemory( str );
strcpy( str, "hello world" );
printf( str );
}
/*
首先这个题目存在内存泄露的问题和指针指向空地址问题
说说这个题目的存在的几个问题:
1、 在GetMemory函数里面, 没有对malloc函数返回值进行测试
if(NULL==p)
2、 在函数里面没有对指针p进行释放
free(p);
3、这里会有一个问题,在C语言中默认时按值传递的, 不是按照地址传递的。
在程序里面不能改变str的指向。
GetMemory(str);不能改变str的指向。
函数原型为:
void GetMemory(char *p); 定义的就是一个指针类型的参数。
4、在函数内部不能改变传值参数的值
*/
/*****************
malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时,
它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求
的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果
有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多
的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,
malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大
的内存块。如果无法获得符合要求的内存块,malloc函数会返回NULL指针,因此在调用malloc动态申请内存块时,一定要
进行返回值的判断。
**************/
改成下面形式可能更健壮一些:
Void GetMemory(char **p)
{
char *temp;
if(NULL != (temp=(char *)malloc(1000)))
*p=temp;
free(temp);
}
试题5:
char *GetMemory( void )
{
char p[] = "hello world";
return p;
}
void Test( void )
{
char *str = NULL;
str = GetMemory();
printf( str );
}
/* 其实怎么说呢这个题目的理解比上面一个题目来对简单, 但是通过这个题目和上面
的题目需要知道一个事实:
那就是函数内部声明的局部变量(static类型的除外,当然还有register的除外),这里指的
是auto类型的变量, 其内存空间是在系统为应用程序开辟的栈里面申请。
而malloc函数申请的空间则是从系统为应用程序开辟的堆里面申请。堆里面申请的不会自动释放,
而栈里面申请的会随着函数声明周期的结束而自动释放。
这个题目的错误之处在于没有理解局部变量的生命周期。
*/
改成下面的形式可能会更健壮:
char *getmemory(void)
{
char *p=NULL;
if(NULL !=(p=(char *)malloc(strlen("hello word")+1))
return p;
}
/*
但是这样返回的的指针是游离的, 没有任何意义
*/
试题6:
void GetMemory( char **p, int num )
{
*p = (char *) malloc( num );
}
void Test( void )
{
char *str = NULL;
GetMemory( &str, 100 );
strcpy( str, "hello" );
printf( str );
}
/*
这个题目在第四个题目已经实现和论述,不再论述
指的一提的是:
传递&str值,并不能改变str的指向。
*/
试题7:
void Test( void )
{
char *str = (char *) malloc( 100 );
strcpy( str, "hello" );
free( str );
... //省略的其它语句
}
/*
这个题目比上面的更加简单, 它的问题就是没有对malloc函数的返回情况进行
检测,
如果 NULL=(char *)malloc(NUM) 那么strcpy函数将不会成功执行,
*/