嵌入式软件开发试题——供自己查漏补缺(不断更新)

一点声明:

本试题整理供本人查漏补缺,题目来自网络,如有侵权,请联系作者删除;若有错误,请不吝指出,万分感谢。

目录

找错题

简答题


 

找错题


试题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文件格式说明表:

偏移地址字节
数据类型内 容
00H4Char"RIFF"标志
04H4int32文件长度
08H4Char"WAVE"标志
0CH4Char"fmt"标志
10H4 过渡字节(不定)
14H2int16格式类别
16H2int16通道数
18H2int16采样率(每秒样本
数),表示每个通道
的播放速度
1CH4int32波形音频数据传送速
20H2int16数据块的调整数(按
字节算的)
22H2 每样本的数据位数
24H4Char数据标记符"data
28H4int32语音数据的长度

答案:

/*
已知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; //得分点:返回本对象的引用
}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值