抓虫子2020-7-19篇
共用体数据类型错误
问题描述
在编写Union共用体类型的时候,写了如下代码,在第5行出现错误:
#include <iostream>
#include <string>
using namespace std;
int main() {
union { string nu, na; int ag; } StA;
}
错误原因分析
union定义的数据结构中,所有数据共享一段内存,而当这些数据包含类的时候,就会有构造函数、析构函数,那么同一类的union类的成员在初始化时,就有可能会执行不同的构造函数,这是无法预料的,故原则上不倡导共用体内包含具有一些复杂特性的类型。所以,我们在定义Union类时要尽量避免成员变量是对象(含有自己的构造函数)。
写入访问权限冲突(_Left 是 nullptr)
问题描述
//输入n组学生数据【姓名,学号,分数】,依照分数输出最好成绩和最差成绩学生信息
#include <iostream>
#include <string>
using namespace std;
int main() {
int n; cin >> n;
struct student {
student() :name(""), num(""), grade(0) {}
string name;
string num;
int grade;
};
student* a = new student[n];
if (n > 0) {
for (int i = 0; i < n; i++) {
cin >> a[i].name >> a[i].num >> a[i].grade;
}
/*这个for循环输入如果换成下面的while写法就会出现访问错误
while (n--)cin >> a[n - 1].name >> a[n - 1].num >> a[n - 1].grade;
*/
/*写成这个样子仍发成异常_Left 是 0xCDCDCDCD
int len = n;
while (len--)cin >> a[len - 1].name >> a[len - 1].num >> a[len - 1].grade;
*/
int lo = 0, hi = 0;
for (int i = 0; i < n; i++) {
if (a[i].grade > a[hi].grade)hi = i;
else if (a[i].grade < a[lo].grade)lo = i;
}
cout << a[hi].name << " " << a[hi].num << endl;
cout << a[lo].name << " " << a[lo].num << endl;
}
delete[]a;
}
错误原因分析
指针a进行下标运算实际上是将地址值加一个整数并存储在指针变量中,当指针指向数组中的最后一个元素时,再相加一个整数,就会出现越界情况,这和数组名取下标的情况是不同的。使用数组名的方式取数组中的元素,可以在其下标域中从大往小进行,即先取最后一个元素,再依次向前取。而使用指向数组的指针进行取下标是,却不能在最后一个元素偏移量的基础上使用[]符号向前取元素,因为[]符号进行的下标运算只会将指针进行自增,而不能进行自减。 不是自增,下标运算只是计算地址偏移量,不会改变原指针指向。
真正的错误原因在于while(len--)
,以及a[len-1]
,应该改为:
int len = n;
while (--len >= 0) { //此处也可以改为while(len-->0),将len当作必然大于左边界一个位置的哨兵,再减一个位置滑到合法的区间段成为可直接索引取值的偏移量
cin >> a[len].name >> a[len].num >> a[len].grade;
}
由于len--后,len本身就是a容器的最后一个元素的边界,即这时有len+1个元素,最末尾元素的偏移量为a+len,写成下标取值形式即为a[len];所以原示例中写的while循环是错误的,不仅最后一个元素没有存入值,也会导致当len=2,while循环直接成立,然后len自减1,变为1,进入循环体,通过a[len-1],存入0号元素的值;当len=1时,while对1进行判断,为真,进入循环体并将len做自减成为0,再通过a[len-1],就出现了越界访问。
int len = n; //假设n=2
while (len--) { //当len=1时,先判断len值是否为0,不为0即循环,再将len自减1,即len=0
cin >> a[len - 1].name >> a[len - 1].num >> a[len - 1].grade; //此时访问的是a[0-1]
}
类模板分文件编写(错误:LNK1120、LNK2019)
C++分文件编写的机制
各个文件独立编译,如果在某文件中出现了函数调用,但是在该文件中并没有对应函数的实现,此时就会在函数调用处生成特定符号,在链接过程中再对号入座完成函数调用。
C++模板的编译机制
由于模板是泛型编程,需要由函数的调用来确定使用的数据类型,故编译器会对模板进行两次编译,在声明的地方对模板代码本身进行编译,相关实现中所有涉及符号类型的部分都不会被编译,等待调用时再编译;而对主调文件中的相关代码编译时,缺乏被调成员的实现部分,故原先的调用语句就会生出无法解析的部分,继而编译器报错。
为了解决这种情况,可以有以下解决办法:
- 直接将模板定义实现的那个文件用#include命令包含到调用文件中
- 在头文件中直接把实现部分写出来,不再将声明与定义分开,将后缀改为.hpp
类的构造函数声明称内联函数(错误LNK2019、LNK1120)
问题描述
将构造函数声明称内联函数,在同一个文件中实现,编译没有问题,如下:
#include <iostream>
using namespace std;
class Base {
public:
Base(int a, int b);
void show();
int a, b;
};
inline Base::Base(int a, int b) :a(a), b(b) {}
void Base::show() { cout << "a:" << a << ",b:" << b << endl; }
int main() {
Base* p = new Base(1, 2);
p->show();
system("pause");
}
但如果将类体声明和成员函数实现分文件编写,继续使用内联形式的话,将会出现错误LNK2019、LNK1120,如下三个文件:
//文件Base.h
#pragma once
class Base {
public:
Base(int a, int b); //此处即便声明称inline Base(int a,int b),仍是一样的结果
void show();
int a, b;
};
//文件Base.cpp
#include "Base.h"
#include <iostream>
using namespace std;
inline Base::Base(int a, int b) :a(a), b(b) {}
void Base::show() { cout << "a:" << a << ",b:" << b << endl; }
//文件main.cpp
#include <iostream>
#include "Base.h"
using namespace std;
int main() {
Base* p = new Base(1, 2);
p->show();
system("pause");
}
错误分析
- C++规定在类体内部定义的函数默认成为内联函数(无论是否加inline关键字)
- 内联函数的声明与定义必须在同一个文件中
指针的指针
问题描述
下列代码会直接导致内存访问错误:
#include <iostream>
#include <string>
using namespace std;
#define N 3
int main() {
int** p = new int* [N];
for (int i = 0; i < N; i++) {
cin >> *p[i];
}
int s = N;
while (s-- > 0) {
cout << p[s] << "\t" << *p[s] << endl;
}
}
错误分析
-
int**
类型为指向指针的指针,即p保存的是一个指针的地址 -
new int* [N]
分配了N个int*
类型的空间块,也就是元素为int*类型,长度为N的数组 -
…………………………如何匹配的?p这个指针指向了一个数组空间的地址,而这个数组中存放的是N个指针
#include <iostream> using namespace std; int main() { int arr[5] = { 0 }; int* p = arr; //让p指向数组首地址,它们指向的地址都是a[0]元素的地址,但arr是个常量,不可变,而p可改变 //int* f = &arr; //错误,int(*)[5]类型的值不能用于初始化int*类型的实体 cout << "arr:" << arr << "\t" << "p:" << p << "\t" << "&arr:" << arr << endl; cout << "arr+1:" << arr + 1 << "\t" << "p+1:" << p + 1 << "\t" << "&arr+1:" << &arr + 1 << endl; //数组地址的增量是以整个数组空间大小为基础,而数组首元素的增量以及指向首地址的指针增量都是以数组首元素的类型所占空间大小为基础 for (int n = 0; n < 3; n++) { cout << endl << "n = " << n << endl; cout << "p+n:" << p + n << "\t" << "&arr+n:" << &arr + n << endl; } //cout << ++(&arr) << endl; //数组名:代表首元素地址,以及数组地址:用&运算符取地址的量,都是常量不可自增自减 }
-
当直接分配一个指针数组堆空间时,如
new int*[N]
其实也就是分配了N×sizeof(int*)的字节空间来存放这些指针变量,每个指针变量占4个字节(32位),所使用的堆空间位4N个字节,但其中的数据都未经有效初始化,全部为垃圾数据,这时每一个指针数组中的每个元素所指向的内存地址都是无法预料的,也就是说这步操作让你获得了存放N个野指针的数组而已,那么后面不经任何处理直接访问野指针指向的对象就会造成各种内存异常。
奇怪的分号错误(C2760)
描述
示例代码中第8行for循环一直提示「意外的标识符,应为 ' ; ' 」
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<typename T>
void printVec(vector<T>& v) {
for (vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
cout << *it << " ";
}
cout << endl;
}
void test01() {
vector<int> v;
for (int i = 0; i < 10; i++) {
v.push_back(i);
}
printVec(v);
}
int main() {
test01();
}
错误分析
C++的模板类型似乎不被编译器支持模板嵌套,vector本身作为一个类模板,使用的时候需要实际的参数,而若以vector<T>
方式使用,仍会被当作已给出类型,但这个类型编译器不会找到的,故当遇到for语句vector::iterator时,它就认为应该结束了,但确没有获得结束的分号,以致报错。
Cannot deference out of range deque iterator
问题描述
该代码在运行时抛出异常Cannot deference out of range deque iterator.
#include <iostream>
#include <string>
#include <vector>
#include <deque>
#include <algorithm>
#include <ctime>
using namespace std;
class Person {
public:
Person(string name, unsigned short age, unsigned short score = 0) :name(name), age(age), score(score) {}
string name; unsigned short score; unsigned short age;
};
void setPlayer(vector<Person>& v) {
//让用户录入选手名称
string name; unsigned short age;
cout << "请输入参赛选手的姓名,年龄,全部输入完毕后,输入\"exit\"退出:" << endl;
while (true) {
cin >> name; if ("exit" == name)break; cin >> age;
Person pe(name, age); v.push_back(pe);
}
}
void setScore(vector<Person>& v) {
//Person* it = NULL;
for (int i = 0; i < v.size(); ++i) {
//it = &v[i];
deque<unsigned int> d;
srand((unsigned)time(NULL));
int sum = 0, sco = 0;
for (int i = 0; i < 10; ++i) {
sco = rand() % 41 + 60;
d.push_back(sco);
sum += sco;
cout << sco << " ";
}
cout << endl;
sort(d.begin(), d.end());
//d.pop_back(); d.pop_front();
v[i].score = (sum - *d.begin() - *d.end()) / (v.size() - 2);
}
}
int main() {
//生成选手信息清单
vector<Person> player;
setPlayer(player);
//给选手评分
setScore(player);
//显示每个评委的打分
//显示最终平均分
system("pause");
}
错误分析
问题出在第41行,对d.end()解引用,由于迭代器所指向的区间是个前闭后开的 [begin , end),所以直接对end()指向的元素解引用是错误的!
将改行修改为下列代码即可:
v[i].score = (sum - d.front() - d.back()) / (v.size() - 2);
具有类型xxx的表达式会丢失一些const-volatile限定符以调用xxx (C3848)
问题代码:
#include <iostream>
#include <string>
#include <map>
#include "源1.h"
using namespace std;
class student {
public:
string name, num; unsigned age;
student(string na, string no, unsigned age) :name(na), num(no), age(age) {}
friend ostream& operator<<(ostream& os, const student& s) {
cout << s.name << "\t" << s.age << "\t" << s.num << "\t";
return os;
}
};
class score {
public:
score(unsigned math, unsigned science, unsigned chinese, unsigned english, unsigned politics)
:math(math), science(science), chinese(chinese), english(english), politics(politics) {}
friend ostream& operator<<(ostream& os, const score& s) {
cout << s.math << "\t" << s.science << "\t" << s.chinese << "\t" << s.english << "\t" << s.politics << "\t";
return os;
}
unsigned math, science, chinese, english, politics;
};
struct Mycmp {
bool operator()(student& va, student& vb) {
return va.num < vb.num;
}
};
void func() {
map<student, score, Mycmp> m;
m[student("小明", "2020 101 800", 23)] = score(100, 99, 85, 80, 89);
m[student("丽丽", "2020 101 802", 19)] = score(100, 99, 85, 80, 89);
m[student("王聪", "2020 101 080", 17)] = score(100, 99, 85, 80, 89);
m[student("小菲", "2020 101 500", 22)] = score(100, 99, 85, 80, 89);
void print(map < student, score, Mycmp> & m);
}
void print(map < student, score, Mycmp>& m) {
map < student, score, Mycmp>::iterator it = m.begin();
while (it++ != m.end()) cout << it->first << it->second << endl;
}
int main() {
func();
}
错误分析:
VC编译器的要求?同条错误,听说,有的代码放到codeblocks上能编译通过!但大致意思时调用的仿函数具有const属性,但代码中给出的实例不具备const修饰。用struct生成的仿函数,不能用const加以限定,所以应该将其声明为class类型的,并将成员函数用const关键字修饰。
修改后:
#include <iostream>
#include <string>
#include <map>
using namespace std;
class student {
public:
string name, num; unsigned age;
student(string na, string no, unsigned age) :name(na), num(no), age(age) {}
friend ostream& operator<<(ostream& os, const student& s) {
string sno = s.num; int pos = 0;
while ((pos = sno.find(' ', pos)) < sno.size()) {
sno.erase(pos, 1);
}
cout << s.name << "\t" << s.age << "\t" << sno << "\t";
return os;
}
};
class score {
public:
score(unsigned math = 0, unsigned science = 0, unsigned chinese = 0, unsigned english = 0, unsigned politics = 0)
:math(math), science(science), chinese(chinese), english(english), politics(politics) {}
friend ostream& operator<<(ostream& os, const score& s) {
cout << s.math << "\t" << s.science << "\t" << s.chinese << "\t" << s.english << "\t" << s.politics << "\t";
return os;
}
unsigned math, science, chinese, english, politics;
};
class Mycmp {
public:
bool operator()(const student& va, const student& vb) const {
return va.num < vb.num;
}
};
void print(map < student, score, Mycmp>& m);
void func() {
map<student, score, Mycmp> m;
m[student("小明", "2020 101 800", 23)] = score(100, 99, 81, 87, 89);
m[student("丽丽", "2020 101 802", 19)] = score(99, 96, 89, 90, 90);
m[student("王聪", "2020 101 080", 17)] = score(87, 88, 85, 100, 96);
m[student("小菲", "2020 101 500", 22)] = score(100, 79, 80, 99, 89);
print(m);
}
void print(map < student, score, Mycmp>& m) {
map < student, score, Mycmp>::iterator it = m.begin();
while (it != m.end()) {
cout << it->first << it->second << endl;
it++;
}
}
int main() {
func();
}