栈内的变量是默认无初始值的
把如图所示的数组
放入main函数内,就会致错:
原因是:
栈内的变量是默认无初始值的,用memset赋个初始值
memset函数是在头文件#include <memory.h>中定义的。代码改成:
数组作为参数,自动退化为指针
传参的时候,数组自动退化成指针了,又采坑了、上代码
#include <iostream>
using namespace std;
void calculate(int container[], int num) {
if (container == nullptr) return;
int len = sizeof(container) / sizeof(int);
//动态申请一个和container一样大的数组
//int *answer;
//answer = (int *)malloc(sizeof(int)*len);
int less = -1, more = len;
for (int i = 0; i < len; i++) {
if (container[i] < num) {
less++;
container[i]=container[less] + container[i];
container[less] = container[i] - container[less];
container[i] = container[i] - container[less];
}
else if (container[i] > num) {
more--;
container[i] = container[more] + container[i];
container[more] = container[i] - container[more];
container[i] = container[i] - container[more];
}
}
int a = 0;
}
int main()
{
int container[] = { 3,4,5,0,2 };
int num = 3;
int len = sizeof(container) / sizeof(int);
calculate(container, num);
return 0;
}
container函数中获得的len的值为1,而不是预期中的5.
交换变量
机灵不能乱抖。最近新学了一种交换两个变量的值的方式,可以不用申请中间变量就交换两个变量的值,如下所示:
//交换a和b的值
a = a + b;
b = a - b;
a = a - b;
于是乎,老夫意气风发的,遇到交换变量的值就用这种方式。恩,bug也就随之而来了
if (arr[l] < arr[r]) {
//交换arr[l]和arr[less]的值
arr[l] = arr[l] + arr[less];
arr[less] = arr[l] - arr[less];
arr[l] = arr[l] - arr[less];
}
肉眼看没什么问题,可是,如果 less 的值和 l 相等,最后arr[l]和arr[less]的值就都变成0了。。。
布尔值不应该参与运算
bool b = true;
bool b2 = -b; //b2是true
对大多数运算符来说,布尔类型的运算江北提升为int类型。上面布尔变量b的值为真,参与运算时将被提升成整数 1 ,对他求负的结果是 -1 。将 -1 在转换会布尔值并将其作为b2的初始值,显然这个厨师值不等于0 。转换成布尔值后应该为 1 。所以,b2 的值为真。
解引用运算符的优先级低于点运算符
(*p).fun();
if语句里面的条件如果是负数,也会执行
看到群里有意有意思的
bug在这:
是get_data()返回了一个vector数组,然后std::begin取到迭代器之后,这个verctor数组就析构了,*b就有问题了
max不支持一次取三个数中的最大值
要写成这样
res=max(res, max(abs(leftneed),abs(rightneed)));
做二维矩阵的题的时候,二维数组a[i][j]里面,i是行,对应了直角坐标里面的y,j是列,对应了直角坐标里面的x。
会因为版本的标准不同而产生的bug
e.g变长数组,早期的版本是不支持这样定义数组长度的
#include<iostream>
using namespace std;
int main() {
int a = 10;
int aa[a];
return 0;
}
C中用%s输出char类型的单个字符,程序崩溃,为什么?
printf在根据%s输出时,将给定的值作为字符串的首地址,然后逐个字节输出直至碰到’\0'。
如果给定的值是不是一个字符串的地址,而是字符、数字的话,就会将字符或数字的值作为将打印字符串的首地址进行打印。
显然,这些随意的值作为地址的话,打印其中的值就会引起莫名其妙的错误了。
上面对应%s的 it->first 就是char类型的字符,故会出错。
无符号数和有符号数进行比较大小的坑
有符号数会转换成无符号的数,然后按照无符号的数的规则进行比较。
-1转成无符号数是无符号数中的最大值,因此 有符号数 -1 会比无符号数 10要大。
未定义行为
下面代码调用顺序不确定,程序报错也是合法的。
同理,下面的代码会出现内存泄漏。
假设下面代码一定会执行delete a和delete b(当然,有可能会调用不到,这里是假设一定会执行)
new A假设执行成功(new失败的时候会抛出异常,对于抛出异常的类,资源会被释放掉)
对于new来说,资源的构造和初始化是两个过程,即分配内存和调用A的构造函数是两个过程。
上面执行的先后顺序无法保证,有可能会出现 :
- 给A分配了内存
- 给B分配了内存
- 调用A的构造函数失败,此时抛出异常,系统回收A的内存
- new B分配的内存就泄漏了
namespace 调用函数的坑
namespace A {
struct X{};
struct Y{};
void f(int){}
void g(X){}
}
namespace B {
void f(int i) {
//调用的是namespace B中的f
f(i);
}
void g(A::X x) {
//调用的是namespace A下的g(X)函数
//根据参数找调用的函数,参数X在namespace A中进行定义,namespace A中存在g函数,因此
g(x);
}
void h(A::Y y) {
//调用的是namespace B下的h函数,因为查找namespace A中没有h函数
//即,先查找了namespace A中,没找到再查找本namespace中是否存在调用的函数
h(y);
}
}
namespace C {
void g(A::X x) {
//调用的也是namespace A中的函数g
g(x);
}
}
由于编译器优化导致的内存泄漏
下面的代码不会产生内存泄漏
class ObjectA {
public:
explicit ObjectA(int a):m_value(new int(a)){
cout << "ObjectA的构造函数被调用" << endl;
}
~ObjectA() {
cout << "ObjectA的析构函数被调用" << endl;
delete m_value;
}
private:
int *m_value;
};
class Evil {
public:
Evil() { throw 10; }
~Evil(){}
};
class Normal :public ObjectA {
public:
//分别调用ObjectA的构造函数,ObjectA的构造函数,Evil的构造函数
//在调用Evil的构造函数时抛出异常,导致Normal的构造函数调用失败,因此不会调用Normal的析构函数
//调用完Evil的构造函数之后,调用ObjectA的析构干啥
explicit Normal(int a):ObjectA(a), m_a(a+1),m_evil(), m_resource(new int(a+2)){}
~Normal() { delete m_resource; }
private:
ObjectA m_a;
Evil m_evil;
int *m_resource;
};
static void hasMemoryLeak() {
try {
Normal n(1);
}
catch (...) {
}
}
int main() {
hasMemoryLeak();
return 0;
}
但是对上面的代码进行修改,就会产生内存泄漏
Normal类中的数据成员顺序调换一下,即——将m_resource和m_evil的顺序进行调换
class Normal :public ObjectA {
public:
explicit Normal(int a):ObjectA(a), m_a(a+1),m_evil(), m_resource(new int(a+2)){}
~Normal() { delete m_resource; }
private:
//将m_resource和m_evil的顺序进行调换
//则此时构造函数初始列的调用顺序为
//explicit Normal(int a):ObjectA(a), m_a(a+1), m_resource(new int(a+2)),m_evil(){}
//即使m_evil()写在了m_resource(new int(a+2))的前面,编译器会对调用顺序进行优化
ObjectA m_a;
int *m_resource;
Evil m_evil;
};
则此时Normal类的构造函数初始列的调用顺序为
explicit Normal(int a):ObjectA(a), m_a(a+1), m_resource(new int(a+2)),m_evil(){}
即使m_evil()写在了m_resource(new int(a+2))的前面,编译器会对调用顺序进行优化
从而导致内存泄漏——构造函数在构造过程中抛出异常,因此析构函数不会被调用,m_resource(new int(a+2))申请的内存发生了泄漏。
class ObjectA {
public:
explicit ObjectA(int a):m_value(new int(a)){
cout << "ObjectA的构造函数被调用" << endl;
}
~ObjectA() {
cout << "ObjectA的析构函数被调用" << endl;
delete m_value;
}
private:
int *m_value;
};
class Evil {
public:
Evil() { throw 10; }
~Evil(){}
};
class Normal :public ObjectA {
public:
//分别调用ObjectA的构造函数,ObjectA的构造函数,Evil的构造函数
//在调用Evil的构造函数时抛出异常,导致Normal的构造函数调用失败,因此不会调用Normal的析构函数
//调用完Evil的构造函数之后,调用ObjectA的析构干啥
explicit Normal(int a):ObjectA(a), m_a(a+1),m_evil(), m_resource(new int(a+2)){}
~Normal() { delete m_resource; }
private:
//将m_resource和m_evil的顺序进行调换
//则此时构造函数初始列的调用顺序为
//explicit Normal(int a):ObjectA(a), m_a(a+1), m_resource(new int(a+2)),m_evil(){}
//即使m_evil()写在了m_resource(new int(a+2))的前面,编译器会对调用顺序进行优化
ObjectA m_a;
int *m_resource;
Evil m_evil;
};
static void hasMemoryLeak() {
try {
Normal n(1);
}
catch (...) {
}
}
int main() {
hasMemoryLeak();
return 0;
}
关于placement new
placement new在标准库中用的比较多
class Object {
public:
Object():m_value(new int(4)){}
~Object() { delete m_value; }
private:
int m_data[100];
int *m_value;
};
char info[10000];
void placementNew() {
//placement new,利用某一个地方的内存来进行new,而不是系统内存
Object* s = new(info)Object();
//下面两个,哪一个正确?答案是第一句是正确的
s->~Object();
//只有placement new,没有placement delete
//delete会释放内存还给系统,而new出来的资源并不是来自于系统,因此执行 delete s 会报错
delete s;
}
不要在析构函数中抛出异常
多线程编程中,共享资源的安全性问题