筆者定義了一個結構體變量,用于作為函數的形參,定義如下:
struct CardParameters {float* Average = nullptr;int averageSize = 0;
};
需求描述:結構體變量作為函數的形參,在函數體中給指針變量分配內存空間并賦值,在函數調用結束后,在外部可以訪問這些數據,用于其他用途。
情形1:結構體作為函數形參時,值傳遞
函數的聲明:
void collectData(CardParameters card); // 值傳遞,不會修改原對象
函數的調用:
CardParameters CardSet;
CardSet.averageSize = 10;collectData(CardSet);
函數體示例:
void collectData(CardParameters card) {card.Average = new float[10]; card.averageSize = 10;for (int i = 0; i < card.averageSize; ++i) {card.Average[i] = static_cast<float>(i);}
}
值傳遞時,函數內部操作的是結構體的一個拷貝(copy),你在函數里對 card.Average
的賦值只影響這個拷貝,不會影響到外面的原始結構體 CardSet
。函數結束后,拷貝被銷毀,里面的 Average
指針也隨之失效,而外部的 CardSet.Average
還是原來的值。
在函數調用語句結束后,筆者試圖訪問結構體中的成員變量,發現CardSet.Average = nullptr,CardSet.averageSize = 0,也就是說結構體中變量的值還是初值,并沒有任何改變。
情形2:結構體作為函數形參時,引用傳遞
函數的聲明:
void collectData(CardParameters &card); // 引用傳遞,會修改原對象
函數體示例:
void collectData(CardParameters &card) { // 注意這里的 &card.averageSize = 10;card.Average = new float[card.averageSize];for (int i = 0; i < card.averageSize; ++i) {card.Average[i] = static_cast<float>(i);}// 打印驗證是否成功for (int i = 0; i < card.averageSize; ++i) {qDebug() << "Data[" << i << "] = " << card.Average[i];}
}
函數的調用:
CardParameters CardSet;
CardSet.averageSize = 10;collectData(CardSet); // 傳入引用,函數內的修改會影響外部變量// 現在可以安全訪問 CardSet.Average
if (CardSet.Average != nullptr) {for (int i = 0; i < CardSet.averageSize; ++i) {qDebug() << "外部訪問: " << CardSet.Average[i];}
}
在函數調用結束后,外部訪問結構體中的變量,其值和函數體中賦值的結果一致。
兩種情形在使用完指針變量后都需要手動釋放內存,否則會出現內存泄漏。
delete[] CardSet.Average;
CardSet.Average = nullptr;
CardSet.averageSize = 0;
總結:
1. 值傳遞
定義:將結構體對象復制一份傳入函數,函數內部操作的是副本。
特點:
特性 | 描述 |
---|---|
操作對象 | 函數內部操作的是原始結構體的一個拷貝 |
修改影響 | 不會影響外部原始結構體 |
性能開銷 | 如果結構體較大,會有性能損耗(需要復制) |
生命周期 | 副本生命周期僅限于函數體內,函數結束后自動銷毀 |
省流:在函數體中修改結構體中的任何變量,在函數調用結束后自動失效?
如果結構體中含有指針,并且沒有深拷貝邏輯,容易造成淺拷貝問題(兩個指針指向同一塊內存)。
2. 引用傳遞
定義:將結構體變量的引用傳入函數,函數內部操作的是原始對象本身。
特點:
特性 | 描述 |
---|---|
操作對象 | 函數內部直接操作原始結構體 |
修改影響 | 會直接影響外部原始結構體 |
性能開銷 | 高效,不需要復制整個結構體 |
生命周期 | 不涉及副本,不影響原始對象生命周期 |
在函數體中修改結構體中的任何變量,在函數調用結束后值仍然保留,適合用于數據采集、配置設置等需要“輸出”的場景