145 lines
4.4 KiB
Markdown
145 lines
4.4 KiB
Markdown
# 段错误修复说明
|
||
|
||
## 问题定位
|
||
|
||
段错误发生在 `ECG_12LEAD_Data_Mapper` 函数的这一行:
|
||
```cpp
|
||
output_channels[9][sample] = -0.5f * (lead_I + lead_II); // aVR
|
||
```
|
||
|
||
## 根本原因
|
||
|
||
### 1. **数组未初始化**
|
||
```cpp
|
||
// 问题代码:只创建了向量,但没有设置大小
|
||
std::vector<std::vector<float>> output_channels(12);
|
||
// 此时 output_channels[9] 是一个空的向量,访问 [sample] 会导致段错误
|
||
```
|
||
|
||
### 2. **缺少边界检查**
|
||
```cpp
|
||
// 问题代码:直接访问数组元素,没有验证索引和大小
|
||
output_channels[9][sample] = value; // 可能越界
|
||
```
|
||
|
||
## 修复方案
|
||
|
||
### 1. **正确初始化所有通道**
|
||
```cpp
|
||
// 修复后:为所有12个通道设置正确的大小
|
||
std::vector<std::vector<float>> output_channels(12);
|
||
for (int ch = 0; ch < 12; ++ch) {
|
||
output_channels[ch].resize(num_samples, 0.0f); // 初始化为0
|
||
}
|
||
```
|
||
|
||
### 2. **添加边界检查**
|
||
```cpp
|
||
// 修复后:在访问数组前验证索引和大小
|
||
if (9 < output_channels.size() && 10 < output_channels.size() && 11 < output_channels.size()) {
|
||
if (sample < output_channels[9].size() && sample < output_channels[10].size() && sample < output_channels[11].size()) {
|
||
output_channels[9][sample] = -0.5f * (lead_I + lead_II); // aVR
|
||
output_channels[10][sample] = lead_I - 0.5f * lead_II; // aVL
|
||
output_channels[11][sample] = lead_II - 0.5f * lead_I; // aVF
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. **验证输入数据一致性**
|
||
```cpp
|
||
// 修复后:确保所有前8个通道都有相同的采样点数
|
||
for (size_t i = 1; i < 8; ++i) {
|
||
if (input_channels[i].size() != num_samples) {
|
||
throw std::runtime_error("Channel " + std::to_string(i) + " has different sample count: " +
|
||
std::to_string(input_channels[i].size()) + " vs " + std::to_string(num_samples));
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. **验证输出数据完整性**
|
||
```cpp
|
||
// 修复后:确保所有输出通道都有正确的大小
|
||
for (size_t ch = 0; ch < output_channels.size(); ++ch) {
|
||
if (output_channels[ch].size() != num_samples) {
|
||
throw std::runtime_error("Output channel " + std::to_string(ch) + " has incorrect size: " +
|
||
std::to_string(output_channels[ch].size()) + " vs expected " + std::to_string(num_samples));
|
||
}
|
||
}
|
||
```
|
||
|
||
## 修复前后的对比
|
||
|
||
### **修复前(有段错误风险)**
|
||
```cpp
|
||
// 创建12导联输出结构
|
||
std::vector<std::vector<float>> output_channels(12);
|
||
|
||
// 直接访问未初始化的通道
|
||
output_channels[9][sample] = value; // ❌ 段错误!
|
||
```
|
||
|
||
### **修复后(安全)**
|
||
```cpp
|
||
// 创建12导联输出结构,并初始化所有通道的大小
|
||
std::vector<std::vector<float>> output_channels(12);
|
||
for (int ch = 0; ch < 12; ++ch) {
|
||
output_channels[ch].resize(num_samples, 0.0f); // 初始化为0
|
||
}
|
||
|
||
// 安全的数组访问
|
||
if (9 < output_channels.size() && sample < output_channels[9].size()) {
|
||
output_channels[9][sample] = value; // ✅ 安全
|
||
}
|
||
```
|
||
|
||
## 技术要点
|
||
|
||
### 1. **向量初始化的重要性**
|
||
- `std::vector<std::vector<float>>(12)` 只创建了12个空的向量
|
||
- 每个内部向量需要单独调用 `resize()` 设置大小
|
||
- 使用 `resize(size, default_value)` 可以同时设置大小和默认值
|
||
|
||
### 2. **边界检查的必要性**
|
||
- 即使理论上索引应该有效,也要进行边界检查
|
||
- 使用 `if` 语句验证索引和大小
|
||
- 提供清晰的错误信息,便于调试
|
||
|
||
### 3. **数据一致性验证**
|
||
- 确保所有输入通道有相同的采样点数
|
||
- 验证输出通道的完整性
|
||
- 在关键步骤添加断言或异常
|
||
|
||
## 预防措施
|
||
|
||
### 1. **始终初始化容器**
|
||
```cpp
|
||
// 好的做法
|
||
std::vector<std::vector<float>> channels(12);
|
||
for (auto& ch : channels) {
|
||
ch.resize(expected_size, default_value);
|
||
}
|
||
```
|
||
|
||
### 2. **使用范围检查**
|
||
```cpp
|
||
// 好的做法
|
||
if (index < container.size() && sample < container[index].size()) {
|
||
container[index][sample] = value;
|
||
}
|
||
```
|
||
|
||
### 3. **添加调试信息**
|
||
```cpp
|
||
// 好的做法
|
||
std::cout << "Creating " << num_channels << " channels with " << num_samples << " samples each" << std::endl;
|
||
```
|
||
|
||
## 测试建议
|
||
|
||
1. **运行测试模式1**: 验证解析器工作正常
|
||
2. **运行测试模式2**: 测试完整的映射流程
|
||
3. **观察调试输出**: 确认所有通道都被正确初始化
|
||
4. **检查输出文件**: 验证12导联数据完整性
|
||
|
||
现在段错误应该已经被修复,程序可以安全地处理12导联心电数据了!
|