为啥类中的向量自动变为NULL

Posted

技术标签:

【中文标题】为啥类中的向量自动变为NULL【英文标题】:why vector in class automatically become NULL为什么类中的向量自动变为NULL 【发布时间】:2019-06-08 20:03:14 【问题描述】:

我正在创建简单的缓存模型来模拟某些应用程序。

但是,下面的代码在lru 中发生了问题。 (我没有复制无关的代码)

ma​​in.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 等其他情况会在constructorinitCache() 之间打印正确(相同)的地址。

唯一不同的是,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 文件中的新字段?

为啥 Type.GetFields() 不返回基类中的支持字段?

自动映射列表变为0

mybatis 空字符串 为啥变为null

为啥有些值插入到mysql后会变为NULL-CSDN论坛