一点声明:
本试题整理供本人查漏补缺,题目来自网络,如有侵权,请联系作者删除;若有错误,请不吝指出,万分感谢。
目录
找错题
试题1:
void test1()
{
char string[10];
char* str1 = "0123456789";
strcpy( string, str1 );
}
答案:
字符串str1需要11个字节才能存放下(包括末尾的’\0’),而string只有10个字节的空间,strcpy会导致数组越界。
试题2:
void test2()
{
char string[10], str1[10];
int i;
for(i=0; i<10; i++)
{
str1[i] = 'a';
}
strcpy( string, str1 );
}
答案:
str1在数组内没有结束的标志’\0’, 当调用strcpy时,并不确定从复制了多少字节的数据到string开始所在内存上面,可能会破坏string数组后面内存中的数据。
注:
/*strcpy的实现代码*/
char* strcpy(char* des, const char* source)
{
char* r = des;
assert((des != NULL) && (source != NULL));
while((*r++ = *source++) != ‘\0’);
return des;
}
试题3:
void test3(char* str1)
{
char string[10];
if( strlen( str1 ) <= 10 )
{
strcpy( string, str1 );
}
}
答案:
if(strlen(str1) <= 10) 应改为 if(strlen(str1) < 10),
因为strlen调用的结果并未统计’\0’所占用的1个字节。
注:
//strlen C语言函数实现
int strlen(const char* str)
{
assert(str != NULL);
int len = 0;
while((*str++) != '\0' )
len++;
return len;
}
试题4:
void GetMemory( char *p )
{
p = (char *) malloc( 100 );
}
void Test( void )
{
char *str = NULL;
GetMemory( str );
strcpy( str, "hello world" );
printf( str );
}
答案:
GetMemory函数的形参是字符串指针,在函数里面改变形参的值并不能真正改变传入形参的值。
执行完char *str =NULL; GetMemory(str); 之后,str仍然指向NULL。
试题5:
char *GetMemory( void )
{
char p[] = "hello world";
return p;
}
void Test( void )
{
char *str = NULL;
str = GetMemory();
printf( str );
}
答案:
根据变量的生存期可以知道,在函数GetMemory中,数组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 );
}
答案:
malloc申请内存之后并没有判断是否分配成功。
应该加上:
if ( *p == NULL )
{
...//进行申请内存失败处理
}
使用完堆内存之后,应该用free释放内存,并让指针指向NULL。
free(str);
str = NULL;
另外,个人觉得,malloc申请的内存里面存储的值不一定是空的,可能是无序的数据,这时应该用memset函数清零,再使用分配的内存,比较稳妥。特别是申请的内存作为缓存区使用时,需要注意这个问题。
试题7:
void Test( void )
{
char *str = (char *) malloc( 100 );
strcpy( str, "hello" );
free( str );
... //省略的其它语句
}
答案:
同试题6,malloc分配内存之后没有判断是否分配成功,释放内存之后应该让str变量指向空,避免造成野指针的出现。
试题8:
void swap( int* p1, int* p2 )
{
int *p;
*p = *p1;
*p1 = *p2;
*p2 = *p;
}
答案:
p是一个“野”指针,并不知道指向哪个内存地址,对其赋值将导致程序运行崩溃。gcc编译时将产生"段错误(核心已转储)"的错误。
应改为如下:
void swap(int* p1, int* p2)
{
int p = 0;
p = *p1;
*p1 = *p2;
*p2 = p;
}
简答题
试题1:分别给出BOOL, int, float,指针变量 与“零值”比较的 if 语句(假设变量名为var)。
答案:
//BOOL型变量:
if(!var)
//int型变量:
if(var == 0)
//float型变量:
#define EPS 0.000001
if ((var >= -EPS) && (var <= EPS))
//指针变量:
if(var == NULL)
试题2:以下为Windows NT下的32位C++程序,请计算sizeof的值。
void Func ( char str[100] )
{
sizeof( str ) = ?
}
void *p = malloc( 100 );
sizeof ( p ) = ?
答案:
sizeof(str) = 4;
sizeof(p) = 4;
注:
数组名本质总结:
(1)
char str[10];
cout << sizeof(str) << endl;
输出为10,此时str指代数据结构char[10];
(2)
char str[10];
str++; //编译出错,提示str不是左值
在这里,str作为一个指针常量,但不能做自增,自减操作,不能被修改。
(3)数组名作为函数形参时,沦为普通指针,可进行自增,自减。
试题3:写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。
另外,当你写下面的代码时会发生什么事?
least = MIN(*p++, b);
答案:
//标准宏MIN
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
当写这句代码时,least = MIN(*p++, b);宏展开为MIN(*p++,b) ((*p++) <= (b) ? (*p++) : (b)),多次的++将产生副作用。
*p++ 意思是,p先与++结合,先p++,然后再取*p。* 与++优先级相同,都是自右向左结合。
试题4:为什么标准头文件都有类似以下的结构?
#ifndef __INCvxWorksh
#define __INCvxWorksh
#ifdef __cplusplus
extern "C" {
#endif
/*.......*/
#ifdef __cplusplus
}
#endif
#endif /* __INCvxWorksh */
答案:
其中
#ifndef __INCvxWorksh
#define __INCvxWorksh
#endif /* __INCvxWorksh */
是为了防止头文件被重复引用。
-----------------------------------------------
#ifdef __cplusplus
extern "C" {
#endif
/*...*/
#ifdef __cplusplus
}
#endif
放在extern "C"里面的函数声明在编译的时候将按照C的编译方式,不会按照C++的编译方式编译函数。
作为一种面向对象的语言, C++支持函数重载,而过程式语言C则不支持。
函数被C++编译后在symbol库中的名字与C语言的不同。
例如,假设某个函数的原型为:
void foo(int x, int y);
该函数被C编译器编译后在symbol库中的名字为_foo,
而C++编译器则会产生像_foo_int_int之类的名字。
foo_int_int这样的名字包含了函数名和函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。
为了实现C和C++的混合编程, C++提供了C连接交换指定符号extern "C"来解决名字匹配问题,函数声明前加上
extern "C"后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了。
试题5:编写一个函数,作用是把一个char组成的字符串循环右移n个。
比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefg”
函数头是这样的:
//pStr是指向以'\0'结尾的字符串的指针
//steps是要求移动的n
void LoopMove ( char * pStr, int steps )
{
//请填充...
}
答案:
#define MAX_LEN 1024
//解法一
//个人认为最优解
void LoopMove(char* pStr, int steps)
{
int n = strlen(pStr) - steps;
char tmp[MAX_LEN];
memcpy(tmp, pStr + n, steps);
memcpy(pStr + steps, pStr, n);
memcpy(pStr, tmp, steps);
}
//解法二:
void LoopMove(char* pStr, int steps)
{
char tmp[MAX_LEN];
int len = strlen(pStr);
int n = len - steps;
strcpy(tmp, pStr + n);
strcpy(tmp + steps, pStr);
tmp[len] = '\0';
strcpy(pStr, tmp);
}
试题6:已知WAV文件格式如下表,打开一个WAV文件,以适当的数据结构组织WAV文件头并解析WAV格式的各项信息。
WAVE文件格式说明表:
偏移地址 | 字节 数 | 数据类型 | 内 容 |
00H | 4 | Char | "RIFF"标志 |
04H | 4 | int32 | 文件长度 |
08H | 4 | Char | "WAVE"标志 |
0CH | 4 | Char | "fmt"标志 |
10H | 4 | 过渡字节(不定) | |
14H | 2 | int16 | 格式类别 |
16H | 2 | int16 | 通道数 |
18H | 2 | int16 | 采样率(每秒样本 数),表示每个通道 的播放速度 |
1CH | 4 | int32 | 波形音频数据传送速 率 |
20H | 2 | int16 | 数据块的调整数(按 字节算的) |
22H | 2 | 每样本的数据位数 | |
24H | 4 | Char | 数据标记符"data " |
28H | 4 | int32 | 语音数据的长度 |
答案:
/*
已知WAV文件格式如下表,打开一个WAV文件,
以适当的数据结构组织WAV文件头并解析WAV格式的各项信
息。*/
#include <stdio.h>
#include <string.h>
#define WAVE_FORMAT_LEN 44
typedef short int16;
typedef int int32;
typedef struct WAVE_format_struct{
char RIFF_flag[4]; //"RIFF"标志
int32 file_len; //文件长度
char wave_flag[4]; //"WAVE"标志
char fmt_flag[4]; //"fmt"标志
int32 reserved; //过渡字节(不定)
int16 format; //格式类别
int16 channels; //通道数
int16 sample_rate;//采样率
int32 trans_speed; //波形音频数据传送速率
int16 block_adjust; //数据块的调整数(按字节算的)
int16 data_bit; //每样本的数据位数
char data_flag[4]; //数据标记符“data”
int32 data_len; //语音数据的长度
} WAVE_format;
//打印标志
void printf_flag(const char* head, const char* flag, int size)
{
int i;
printf("%s = ", head);
for(i = 0; i < size; i++)
{
printf("%c", flag[i]);
}
printf("\n");
}
int main(void)
{
WAVE_format wave_format;
char buffer[WAVE_FORMAT_LEN];
FILE* WAVE_file = fopen("周杰伦-稻香.wav", "r");
if (WAVE_file == NULL)
{
printf("open file error!!\n");
return -1;
}
int buffer_len = -1;
buffer_len = fread(buffer, 1, WAVE_FORMAT_LEN, WAVE_file);
if (!buffer_len)
{
printf("read file error!!\n");
return -1;
}
memset(&wave_format, 0, WAVE_FORMAT_LEN);
memcpy(&wave_format, buffer, WAVE_FORMAT_LEN);
printf_flag("wave_format.RIFF_flag", wave_format.RIFF_flag, 4);
printf("wave_format.file_len = %d\n", wave_format.file_len);
printf_flag("wave_format.wave_flag", wave_format.wave_flag, 4);
printf_flag("wave_format.fmt_flag", wave_format.fmt_flag, 4);
printf("wave_format.reserved = %d\n", wave_format.reserved);
printf("wave_format.format = %hd\n", wave_format.format);
printf("wave_format.channels = %hd\n", wave_format.channels);
printf("wave_format.sample_rate = %hd\n", wave_format.sample_rate);
printf("wave_format.trans_speed = %d\n", wave_format.trans_speed);
printf("wave_format.block_adjust = %hd\n", wave_format.block_adjust);
printf("wave_format.data_bit = %hd\n", wave_format.data_bit);
printf_flag("wave_format.data_flag", wave_format.data_flag, 4);
printf("wave_format.data_len = %d\n", wave_format.data_len);
fclose(WAVE_file);
return 0;
}
注:memset,memcpy可直接用于结构体的操作。
试题7:请说出static和const关键字尽可能多的作用。
答案:
static关键字:
(1).函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
(2).在模块内的static全局变量可以被模块内的所有函数访问,但不能被模块外的其他函数访问;
(3).在模块内的static函数只可以被这一模块内的其他函数调用,这个函数的使用范围被限制在声明它的模块内;
(4).在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
(5).在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。
const关键字:
(1).想阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2).对指针来说,可以指定指针本身为const,也可以指定指针所指向的数据为const,或者二者同时指定为const;
(3).在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4).对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5).对于类的成员函数,有时候必须指定其返回值为const类型,以使其返回值不为“左值”。例如:
const classA operator*(const classA& a1,const classA& a2);
operator*的返回结果必须是一个const对象。
如果不是,下面这样的异常代码也不会编译出错:
classA a, b, c;
(a * b) = c; // 对a*b的结果赋值
操作(a * b) = c显然不符合编程者的初衷,也没有任何意义。
试题8:编写类String的构造函数、析构函数和赋值函数,
已知类String的原型为:
class String
{
public:
String(const char *str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
~ String(void); // 析构函数
String & operate =(const String &other); // 赋值函数
private:
char *m_data; // 用于保存字符串
};
答案:
//普通构造函数
String::String(const char *str)
{
if(str==NULL)
{
m_data = new char[1]; // 得分点:对空字符串自动申请存放结束标志'\0'的空
//加分点:对m_data加NULL 判断
*m_data = '\0';
}
else
{
int length = strlen(str);
m_data = new char[length+1]; // 若能
加 NULL 判断则更好
strcpy(m_data, str);
}
}
// String的析构函数
String::~String(void)
{
delete [] m_data; // 或delete m_data;
}
//拷贝构造函数
String::String(const String &other)
// 得分点:输入参数为const型
{
int length = strlen(other.m_data);
m_data = new char[length+1];
//加分点:对m_data加NULL 判断
strcpy(m_data, other.m_data);
}
//赋值函数
String & String::operate =(const String &other) // 得分点:输入参数为const型
{
if(this == &other) //得分点:检查自赋值
return *this;
delete [] m_data; //得分点:释放原有的内存资源
int length = strlen( other.m_data );
m_data = new char[length+1]; //加分点:对m_data加NULL 判断
strcpy( m_data, other.m_data );
return *this; //得分点:返回本对象的引用
}