采坑记录

栈内的变量是默认无初始值的

把如图所示的数组

放入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是行,对应了直角坐标里面的yj是列,对应了直角坐标里面的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的构造函数是两个过程

上面执行的先后顺序无法保证,有可能会出现 :

  1. 给A分配了内存
  2. 给B分配了内存
  3. 调用A的构造函数失败,此时抛出异常,系统回收A的内存
  4. 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;
}

 


 

不要在析构函数中抛出异常

不要在析构函数中抛出异常

 


 

多线程编程中,共享资源的安全性问题

 

 

 

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值