复习内容
- 函数重载与继承
- 指针
- 排序与查询算法
- C语言数组
- 指针
- Linux文件操作
选择题(每题三分)
1、一个C程序的执行是从 A 。
(A)本程序的main函数开始到main函数结束
(B)本程序文件的第一个函数开始到本程序文件的最后一个函数结束
(C)本程序的main函数开始到本程序文件的最后一个函数结束
(D)本程序文件的第一个函数开始到本程序main函数结束
2、若有定义:int a[3][4];不能表示数组元素a[1][1]的是 D 。
a[3][4]在底层存储使用连续存储,存储方式为:
a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3](A)*(a[1]+1) (B)*(&a[1][1]) (C)(*(a+1))[1] (D)*(a+5)
(a+5)并不是表示 a 的地址加上 5,指向a[5]的指针。因为数组 a 只有 3 行,所以a[5]是越界访问,正确的当时*(a[0]+5)
3、以下哪一关键字可用于重载函数的区分 C 。
(A)extern // 全局
(B)static // 静态变量
(C)const //可用于区分
(D)virtual //设置虚函数
4、下列有关继承和派生的叙述中,正确的是 C 。
(A)派生类不能访问通过私有继承的基类的保护成员(不能对外传承)
(B)多继承的虚基类不能够实例化 (X) 可以被实例化,就是实例化引入
(C)如果基类没有默认构造函数,派生类构造函数必须显式地调用基类的带参构造函数
(D)基类的析构函数和虚函数都不能够被继承,需要在派生类中重新实现(构造函数)
派生类需要
确保基类的成员被正确的初始化。如果基类没有默认构造函数,即没有无参构造函数,那么派生类无法自动调用基类的构造函数来初始化基类的成员。
5、下面程序的输出结果是 A 。
#include <iostream.h> //不带.h
using namespace std;
void swap1(int &v1, int &v2) // 取地址实现交换
{
int tmp = v2; v2 = v1; v1 = tmp;
}
void swap1(int *v1, int *v2) // 地址交换
{
int tmp = *v2; *v2 = *v1; *v1 = tmp;
}
void main()
{
int i = 10, j = 20;
swap1(i, j);
swap1(&i, &j);
cout << i << ”,” << j << endl;
}(A)10,20
(B)20,10
(C)10,10
(D)20,20
6、用某种排序方法对关键字序列{35,84,21,47,15,27,68,25,20}进行排序时,序列的变化情况如下:
20,15,21,25,47,27,68,35,84
15,20,21,25,35,27,47,68,84
15,20,21,25,27,35,47,68,84
则采用的方法是 。
(A)直接选择排序
(B)希尔排序
(C)堆排序
(D)快速排序
7、队列通常采用两种存储结构是 A 。
(A)顺序存储结构和链表存储结构
(B)散列方式和索引方式
(C)链表存储结构和数组
(D)线性存储结构和非线性存储结构
8、C++ 基类中的 private 成员通过 A 类型的继承,可以被派生类访问。
(A)public
(B)protected
(C)private
(D)任何类型的继承都不能使得派生类可以访问基类的private成员
9、如果基类 A 和 A 的派生类 B 中都有成员函数 func();要在派生类的 func()中调用同名的基类的 func()成员函数,下列 B 操作是正确的。
(A)func();
(B)A::func();
(C)B::func();
(D)A.func();
10、下面对静态数据成员的描述中,正确的是 A 。 static
(A)静态数据成员是类的所有对象共享的数据
(B)类的每个对象都有自己的静态数据成员
(C)类的不同对象有不同的静态数据成员值
(D)静态数据成员不能通过类的对象调用 (静态数据成员可以通过类的对象或者类名加作用域运算符来访问和调用。)
11、下列代码的输出内容是 C 。
#include<stdio.h>
main()
{
int a, b, c, d;
a = 10;
b = a++;
c = ++a;
d = 10*a++;
printf(“%d,%d,%d”,b,c,d);
return 0;
}
(A)13,12,120 (B)10,11,120 (C)10,12,120 (D)10,12,130
12、c++中以下关于函数调用说法正确的是 D 。
(A)传地址后实参和形参指向不同的对象 (X)
(B)传引用后实参和形参是不同的对象 X
(C)传值后对形参的修改会改变实参的值 X
(D)其他三项都不对
填空题(每题5分)
1、下列中a的值是 400 。
#define AAA 200
#define BBB AAA+100 // define仅是代码替换
int a = BBB*2 //得出结构为AAA+100*2 仅是代码替换,得出的结果是4002、使用 函数重载 的方法可以实现编译时多态,使用 虚函数 的可以实现运行时多态。
3、程序的局部变量存在于 栈 中,全局变量存在于 静态区 中,动态申请数据存在于 堆 中。
简答题(每题5分)
1、用变量a给出下面的定义
(A)一个指向整型数的指针 int*a
(B)一个有10个指针的数组,该指针是指向一个整型数 int *a[10]
(C)一个指向有10个整型数组的指针 int (*a)[10]
(D)一个指向函数的指针,该函数有一个整型参数并返回一个整型数 typedef int(*a)(int)
(E)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数int (*a[10])(int)
3、一块N个字节的内存,它的首地址(头指针)为pStart,那么末地址为
pStart+N-1 首地址占用一个空间
4、计算下面结构体的大小,标注每个结构体成员的大小
typedef struct _a 14
{
char c1; 1
long i; 4 64位8个字节
char c2; 1
double f; 8
}a;
typedef struct _b
{
char c1; 1
char c2; 1
long i; 4
double f; 8
}b; 问答题(每题5分)
第一题
查找单向链表倒数第k个位置上的结点。
在不改变链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第k(k > 0)个位置的节点。
struct Node {
int val;
Node * next;
Node(int x) : val(x), next(NULL) {}
};答:
建立
两个单链表子链,一个链表向末尾next,当到第search个节点时,另一个节点也开始从头部next,当一个链表到达末尾,另一个链表正好位于倒数第seach个节点。时间复杂度O(n),空间复杂度O(n);
#include <iostream>
using namespace std;
struct Node {
int val;
Node * next;
Node(int x) : val(x), next(NULL) {}
};
Node* search(struct Node *node,int search) //返回倒数第search个节点
{
//定义两个节点一个向前走,一个维持k的位置
Node* p = node;
Node* search_p = node;
int number = 0;
while (p!=nullptr) //当判运行到结尾
{
p = p->next;
number++;
if (number > search) //seatch_p从search位置开始往后移动
{
search_p = search_p->next;
}
}
if (search <= number)
{
//搜索到了
return search_p;
}
else
{
//node节点没有倒数第seach个
return nullptr;
}
}
int main(void)
{
// 初始化
Node* nodes = new Node(NULL);
Node* node = nodes;
for (int i = 0; i < 10000; i++)
{
Node* add = new Node(i);
node->next = add;
node = node->next;
}
struct Node* p = search(nodes, 7);
if (p != nullptr)
{
cout << "倒数第7个数值为:" << p->val << endl;
}
else
{
cout << "Nodes节点总数不满足seach要求" << endl;
}
delete nodes;
return 0;
}第二题
以下程序,会存在什么问题?如果有问题,请更正.
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(char *s)
{
char *str = NULL;
GetMemory(str);
strcpy(str, s);
printf(str);
}答
str字符串空间分配问题,GetMemory`并没有分内存给`str`,需要调用str的`二重指针,传递给参数,来进行空间赋值- 空间大小问题,申请空间可能会因为申请空间过小而发生溢出。
- 使用过后,需
空间释放,避免出现野指针
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
void GetMemory(char** p)
{
*p = (char*)malloc(100); //*p获取str // 空间大小问题若输入过大会溢出
}
void Test(char* s)
{
char* str = NULL;
//P的申请空间,并没有赋值给str
GetMemory(&str);//传递 &str 来获取 str 的地址。
//s空间大于str申请的空间出现移除
strcpy(str, s);
printf(str);
free(str);
}
int main(void)
{
char s[100] = "TLove YOU,heihahiehahieha";
Test(s);
return 0;
}第三题
代码
存在什么缺陷,请进行说明并将修改的贴上来
unsigned long long fun(unsigned long long a, unsigned long long b)
{
return (a + b) / 2;
}答
- long long 解决
a+b可能的溢出。但是吧!(其它暂时没有发现)
unsigned long long fun(unsigned long long a, unsigned long long b)
{
//如果a+b超过了最大,可能出现移除问题
return (a + b) >> 1; // 使用位运算进行除以2操作
}第四题
代码存在什么缺陷,请尝试修改,将修改的贴上来
bool cmp(double a, double b, double c)
{
return (a + b) == c;
}答
浮点数和双精度末尾数具有不确定性,一般用减法判断
- 浮点精度存在限制
(a + b)与c的差的绝对值是否小于某个小的阈值来进行判断- 传入值的判断
双精度浮点数
#include <iostream>
using namespace std;
bool cmp(double a, double b, double c)
{
if (a == NULL || b == NULL || c == NULL)
{
cout << "exit parameter null" << endl;
return false;
}
//return (a + b) == c;
return a + b - c < 0.000000000001;
}
int main(void)
{
double a = 3.14526;
double b = 3.96515;
double c = 7.11041;
if (cmp(a, b, NULL)) //在单计算上时相等的,但是double后面的位数具有随机性要用减法
{
cout << "得出相等" << endl;
}
else
{
cout << "不相等哟或异常" << endl;
}
}第五题
以下代码存在什么问题,请指出,并将修改后的代码贴出。 文件一定考
void read_file(FILE* file, size_t filesize)
{
char* buffer = (char*)malloc(filesize);
if (NULL != buffer)
{
memset(buffer, 0, filesize);
fread(buffer, filesize, 1, file);
printf("%s\n", buffer);
free(buffer);
buffer = NULL;
}
}答
不健壮,修改一些bug和可能得错误文件是否
打开,filesize是否正确,是否完全将数据读完
void read_file(FILE* file, size_t filesize)
{
if (file == nullptr || filesize <= 0)
{
printf("read_file init error");
return;
}
// 申请内存空间
char* buffer = (char*)malloc(filesize);
if (buffer == nullptr)
{
printf("Memory allocation failed.\n");
return;
}
//对申请的空间buffer的filesize大小空间设置为0
memset(buffer, 0, filesize);
//读取file的内容 filesize要读元素快的大小,1为元素个数,存档到buffer里面,成功返回filesize大小
size_t count = fread(buffer, filesize, 1, file);
if (count != filesize)
{
printf("Error reading the file\n");
free(buffer);
buffer = NULL;
return;
}
printf("%s\n", buffer);
free(buffer);
buffer = NULL;
}第六题
以下代码存在什么问题,请指出,并将修改后的代码贴出。
vector<int> vecTest = { 1,2,3,4 };
vector<int>::iterator iter1;
for (iter1 = vecTest.begin(); iter1 != vecTest.end(); iter1++){
printf("%d ", *iter1);
if (*iter1 == 3) {
vecTest.erase(iter1);
}
}答:
在删除
iter1之后,迭代器iter1失效,无法再继续进行遍历或递增操作,产生未定义
erase删除之后,将会返回vector删除位置的写一个迭代器
vector<int> vecTest = { 1,2,3,4 };
vector<int>::iterator iter1; //好奇怪,
for (iter1 = vecTest.begin(); iter1 != vecTest.end();)
{
printf("%d ", *iter1);
if (*iter1 == 3)
{
iter1 = vecTest.erase(iter1);
//指向被删除元素的下一个元素的位置
}
else
{
iter1++;
}
}第七题
以下代码存在什么问题,请指出。
queue<int*> testQueue; //队列 先进先出
for (int i=0; i< 10000000; i++)
{
int* p = new int(i);
testQueue.push(p);
}
for (int i=0; i < 10000000; i++)
{
int* p = testQueue.front();
testQueue.pop();
if (p)
{
delete p;
p = NULL;
}
}答:
除了最后释放内存的可能存在的
内存泄漏,和健壮问题,暂时没发现其它问题
#include <iostream>
#include <queue> //队列
using namespace std;
int main(void)
{
queue<int*> testQueue; //队列 先进先出
for (int i = 0; i < 10000000; i++)
{
int* p = new int(i);
if (p == nullptr)
{
cout << "new p error:" << i << endl;
}
testQueue.push(p);
}
while(!testQueue.empty())
{
int* p = testQueue.front();
testQueue.pop();
if (p)
{
delete p;
p = nullptr;
}
}
}