为啥类中的向量自动变为NULL
Posted
技术标签:
【中文标题】为啥类中的向量自动变为NULL【英文标题】:why vector in class automatically become NULL为什么类中的向量自动变为NULL 【发布时间】:2019-06-08 20:03:14 【问题描述】:我正在创建简单的缓存模型来模拟某些应用程序。
但是,下面的代码在lru
中发生了问题。 (我没有复制无关的代码)
main.cpp
int main(void)
Cache* L1Cache = new Cache(64, 64, 8);
Cache* L2Cache = new Cache(256, 64, 4);
Cache* L3Cache = new Cache(2048, 64, 16); // This object causes problem
Cache* L4Cache = new Cache(2048, 64, 8);
L1Cache->initCache();
L2Cache->initCache();
L3Cache->initCache();
L4Cache->initCache();
return 0;
Cache.h
typedef struct CacheLine
std::vector<uint64_t> data;
CacheLine;
typedef struct CacheSet
std::vector<bool> valid;
std::vector<uint64_t> tag;
std::vector<CacheLine> directory;
CacheSet;
typedef struct LRU
std::vector<std::vector<bool>> lruMatrix;
LRU;
class Cache
public:
Cache(uint32_t cacheSizeInKB, uint32_t lineSizeInByte, uint32_t numOfDirs)
// set cache size
this->cacheSizeInKB = cacheSizeInKB;
this->lineSizeInByte = lineSizeInByte;
this->numOfDirs = numOfDirs;
this->numOfSets = (cacheSizeInKB * 1024) / (lineSizeInByte * numOfDirs);
// set memory address offset
this->blockOffsetFrom = log2(lineSizeInByte) - 1;
this->blockOffsetTo = 0;
this->indexOffsetFrom = this->blockOffsetFrom + ceil(log2(this->numOfSets));
this->indexOffsetTo = this->blockOffsetFrom + 1;
this->tagOffsetFrom = 63;
this->tagOffsetTo = this->indexOffsetFrom + 1;
// reserve vectors before using
cache.reserve(this->numOfSets);
for (int x = 0; x < this->numOfSets; ++x)
cache[x].valid.reserve(numOfDirs);
cache[x].tag.reserve(numOfDirs);
cache[x].directory.reserve(numOfDirs);
for (int y = 0; y < this->numOfDirs; ++ y)
cache[x].directory[y].data.reserve(lineSizeInByte / 8);
lru.reserve(this->numOfSets);
for (int i = 0; i < this->numOfSets; ++i)
lru[i].lruMatrix.reserve(numOfDirs);
for (int j = 0; j < this->numOfDirs; ++j)
lru[i].lruMatrix[j].reserve(numOfDirs);
std::cout << "1: " << &lru[0].lruMatrix[0] << std::endl; // this shows correct memory address space
void initCache();
void accessData(uint64_t addr);
void printLRUMatrix(uint64_t index);
private:
const uint32_t HIT = 1;
const uint32_t MISS = 0;
// cache size list
uint32_t cacheSizeInKB;
uint32_t lineSizeInByte;
uint32_t numOfDirs;
uint32_t numOfSets;
// offset list
uint64_t blockOffsetFrom;
uint64_t blockOffsetTo;
uint64_t indexOffsetFrom;
uint64_t indexOffsetTo;
uint64_t tagOffsetFrom;
uint64_t tagOffsetTo;
std::vector<CacheSet> cache;
std::vector<LRU> lru;
;
Cache.cpp
void Cache::initCache()
for (int x = 0; x < numOfSets; ++x)
for (int i = 0; i < numOfDirs; ++i)
cache[x].valid[i] = false;
cache[x].tag[i] = 0;
for (int j = 0; j < numOfDirs; ++j)
cache[x].directory[i].data[j] = 0;
std::cout <<"2: " << &lru[0].lruMatrix[0] << std::endl; // This prints out 0 address in case of L3Cache
/*
for (int i = 0; i < numOfSets; ++i)
std::cout << "i: " << i << std::endl;
for (int j = 0; j < numOfDirs; ++j)
std::cout << "j: " << j << std::endl;
for (int k = 0; k < numOfDirs; ++k)
std::cout << "k: " << k << std::endl;
this->lru[i].lruMatrix[j][k] = false;
*/
输出
1: 0x9464d0
1: 0x9f5190
1: 0xded230
1: 0x140d2d0
2: 0x9464d0
2: 0x9f5190
2: 0
2: 0x140d2d0
我在上面的代码中遇到了奇怪的情况。
在L3Cache的情况下,lru[0].lruMatrix[0]的地址在Cacheconstructor
(0xded230)和成员函数initCache()
(0)之间是不同的。
但是,L1Cache、L2Cache、L4Cache 等其他情况会在constructor
和initCache()
之间打印正确(相同)的地址。
唯一不同的是,L3Cache 使用的 numOfDir 16 比其他的都大。
我无法弄清楚为什么会发生这种情况。看来我的代码没有错误。
有什么问题吗?
【问题讨论】:
cache.reserve(...)
更改向量的容量,而不是大小。它的大小仍然为零。 cache[x]
然后通过访问超出范围的索引,对于 x
的任何值表现出未定义的行为。
@IgorTandetnik 我不明白为什么这很重要。只有 L3Cache 对象会导致问题
“未定义的行为”并不意味着“会发生不好的事情”。这意味着“可能会发生一些不好的事情”。它可能不会。
【参考方案1】:
您正在阅读lru
超出范围。
假设在Cache::initCache()
运行之前没有其他东西触及Cache::lru
,这个std::vector
将默认初始化为空状态。由于该函数中的任何内容都不会增加 lru
的大小,因此当您点击此行时它仍然是空的:
std::cout <<"2: " << &lru[0].lruMatrix[0] << std::endl;
在其中,您有lru[0]
。这是取消引用 lru
的第一个元素,它不存在。这是未定义的行为。 任何事情都可能发生。不要这样做。在取消引用之前,您需要确保 0
位置存在某些内容。
lru[0].lruMatrix
处的向量地址看起来为零的原因可能是因为vector
最初将其动态分配的指向数组的指针设置为空指针。因此,取消引用第一个元素就是取消引用空指针。这是您的特定标准库供应商的实现细节;不要依赖这种行为。
我还在您的代码中看到以下模式:
lru.reserve(this->numOfSets);
for (int i = 0; i < this->numOfSets; ++i)
lru[i].doSomething();
...
出于同样的原因,这是未定义的行为。 std::vector::reserve
不会改变向量的大小。它只分配存储。你可能想要std::vector::resize
【讨论】:
以上是关于为啥类中的向量自动变为NULL的主要内容,如果未能解决你的问题,请参考以下文章
为啥在 XSD 中定义为属性时,自动生成的类中的字段会序列化为元素?
为啥 Spark 不能自动检测 Parquet 文件中的新字段?