调试和发布版本之间 std::map 行为的差异(Xcode 8.3、C++ 11、Cocos2d-x)
Difference in std::map behavior between debug and release builds (Xcode 8.3, C++ 11, Cocos2d-x)
运行在 Cocos2d-x 框架上 Xcode 8.3.3、C++11 中构建的调试版本之间出现意外的行为差异。我正在使用 emplace() 方法来填充 std::map (_randomLetterChancesToAppear),它在调试中正确构建,但在发布时似乎没有填充。当两个值预期相等时,该断言被触发:
CC_ASSERT(outCellDetail._randomLetterChancesToAppear.size() == alphabetCount);
当断言在发布版本中失败时,_randomLetterChancesToAppear 似乎不包含任何数据。 (为了对此进行测试,以防相关,我为我的默认移动方案创建了一个重复的 Xcode 方案,并将 运行 构建配置更改为发布以允许我测试发布使用附加的 IDE 构建。)
这是有问题的方法:
void GameDataController::PopulateIndividualCellChancesToAppear(CellDetail &outCellDetail, const rapidjson::Value &cellDetailObject, const GameDetail& constGameDetail) const
{
cocostudio::DictionaryHelper* dicTool = cocostudio::DictionaryHelper::getInstance();
CC_ASSERT(dicTool->checkObjectExist_json(cellDetailObject, "LetterChancesToAppear"));
CCASSERT(constGameDetail._alphabet.length() > 0, "Zero-length alphabet!");
const rapidjson::Value& letterChancesObject = dicTool->getSubDictionary_json(cellDetailObject, "LetterChancesToAppear");
// iterate through each letter in this game detail's alphabet
const int alphabetCount = static_cast<int>(constGameDetail._alphabet.length());
std::vector<char> lettersNeedingChances;
for (int i = 0; i < alphabetCount; ++i)
{
// if a given alphabet letter has a chance to appear, save it.
const char alphabetChar = constGameDetail._alphabet.at(i);
char letterChanceKey[1];
sprintf(letterChanceKey, "%c", alphabetChar);
if (dicTool->checkObjectExist_json(letterChancesObject, letterChanceKey))
{
const float ChanceToAppear = dicTool->getFloatValue_json(letterChancesObject, letterChanceKey);
CC_ASSERT(!std::isnan(ChanceToAppear));
CC_ASSERT(ChanceToAppear >= 0.0f);
outCellDetail._randomLetterChancesToAppear.emplace(alphabetChar, !std::isnan(ChanceToAppear) ? ChanceToAppear : 0.0f);
}
else
{
// ... otherwise, store the letter in a list of letters without assigned chances.
lettersNeedingChances.push_back(alphabetChar);
}
}
// fill in missing chances to appear.
if (lettersNeedingChances.size() > 0)
{
float defaultRemainingChance = 1.0f / static_cast<float>(alphabetCount);
CC_ASSERT(!std::isnan(defaultRemainingChance));
CC_ASSERT(defaultRemainingChance > FLT_EPSILON);
for (const char remainingChar : lettersNeedingChances)
{
CCLOG("Character %c doesn't have a chance to appear specified. Using default %f", remainingChar, defaultRemainingChance);
outCellDetail._randomLetterChancesToAppear.emplace(remainingChar, defaultRemainingChance);
}
}
// vvvv THIS ASSERT IS FAILING IN RELEASE, BUT NOT IN DEBUG BUILD CONFIGURATIONS. vvvvv
CC_ASSERT(outCellDetail._randomLetterChancesToAppear.size() == alphabetCount);
// normalize chances to appear.
float chanceTotal = 0.0f;
for(auto iterator = outCellDetail._randomLetterChancesToAppear.begin();
iterator != outCellDetail._randomLetterChancesToAppear.end();
iterator++)
{
chanceTotal += iterator->second;
}
if (std::abs(chanceTotal - 1.0f) > FLT_EPSILON)
{
float adjustmentFactor = 1.0f / chanceTotal;
float adjustedChanceTotal = 0.0f;
for(auto iterator = outCellDetail._randomLetterChancesToAppear.begin();
iterator != outCellDetail._randomLetterChancesToAppear.end();
iterator++)
{
iterator->second = iterator->second * adjustmentFactor;
adjustedChanceTotal += iterator->second;
}
CCASSERT(std::abs(adjustedChanceTotal - 1.0f) <= FLT_EPSILON, "adjustedChanceTotal != 1.0f");
}
}
有没有想过为什么这个方法在两种构建配置之间的行为可能不同?
答案归功于 Igor Tandetnik。
char letterChanceKey[1];
sprintf(letterChanceKey, "%c", alphabetChar);
没有为终止的 NUL 留出足够的空间,导致缓冲区溢出。增加缓冲区大小解决了这个问题。
谢谢伊戈尔!
运行在 Cocos2d-x 框架上 Xcode 8.3.3、C++11 中构建的调试版本之间出现意外的行为差异。我正在使用 emplace() 方法来填充 std::map (_randomLetterChancesToAppear),它在调试中正确构建,但在发布时似乎没有填充。当两个值预期相等时,该断言被触发:
CC_ASSERT(outCellDetail._randomLetterChancesToAppear.size() == alphabetCount);
当断言在发布版本中失败时,_randomLetterChancesToAppear 似乎不包含任何数据。 (为了对此进行测试,以防相关,我为我的默认移动方案创建了一个重复的 Xcode 方案,并将 运行 构建配置更改为发布以允许我测试发布使用附加的 IDE 构建。)
这是有问题的方法:
void GameDataController::PopulateIndividualCellChancesToAppear(CellDetail &outCellDetail, const rapidjson::Value &cellDetailObject, const GameDetail& constGameDetail) const
{
cocostudio::DictionaryHelper* dicTool = cocostudio::DictionaryHelper::getInstance();
CC_ASSERT(dicTool->checkObjectExist_json(cellDetailObject, "LetterChancesToAppear"));
CCASSERT(constGameDetail._alphabet.length() > 0, "Zero-length alphabet!");
const rapidjson::Value& letterChancesObject = dicTool->getSubDictionary_json(cellDetailObject, "LetterChancesToAppear");
// iterate through each letter in this game detail's alphabet
const int alphabetCount = static_cast<int>(constGameDetail._alphabet.length());
std::vector<char> lettersNeedingChances;
for (int i = 0; i < alphabetCount; ++i)
{
// if a given alphabet letter has a chance to appear, save it.
const char alphabetChar = constGameDetail._alphabet.at(i);
char letterChanceKey[1];
sprintf(letterChanceKey, "%c", alphabetChar);
if (dicTool->checkObjectExist_json(letterChancesObject, letterChanceKey))
{
const float ChanceToAppear = dicTool->getFloatValue_json(letterChancesObject, letterChanceKey);
CC_ASSERT(!std::isnan(ChanceToAppear));
CC_ASSERT(ChanceToAppear >= 0.0f);
outCellDetail._randomLetterChancesToAppear.emplace(alphabetChar, !std::isnan(ChanceToAppear) ? ChanceToAppear : 0.0f);
}
else
{
// ... otherwise, store the letter in a list of letters without assigned chances.
lettersNeedingChances.push_back(alphabetChar);
}
}
// fill in missing chances to appear.
if (lettersNeedingChances.size() > 0)
{
float defaultRemainingChance = 1.0f / static_cast<float>(alphabetCount);
CC_ASSERT(!std::isnan(defaultRemainingChance));
CC_ASSERT(defaultRemainingChance > FLT_EPSILON);
for (const char remainingChar : lettersNeedingChances)
{
CCLOG("Character %c doesn't have a chance to appear specified. Using default %f", remainingChar, defaultRemainingChance);
outCellDetail._randomLetterChancesToAppear.emplace(remainingChar, defaultRemainingChance);
}
}
// vvvv THIS ASSERT IS FAILING IN RELEASE, BUT NOT IN DEBUG BUILD CONFIGURATIONS. vvvvv
CC_ASSERT(outCellDetail._randomLetterChancesToAppear.size() == alphabetCount);
// normalize chances to appear.
float chanceTotal = 0.0f;
for(auto iterator = outCellDetail._randomLetterChancesToAppear.begin();
iterator != outCellDetail._randomLetterChancesToAppear.end();
iterator++)
{
chanceTotal += iterator->second;
}
if (std::abs(chanceTotal - 1.0f) > FLT_EPSILON)
{
float adjustmentFactor = 1.0f / chanceTotal;
float adjustedChanceTotal = 0.0f;
for(auto iterator = outCellDetail._randomLetterChancesToAppear.begin();
iterator != outCellDetail._randomLetterChancesToAppear.end();
iterator++)
{
iterator->second = iterator->second * adjustmentFactor;
adjustedChanceTotal += iterator->second;
}
CCASSERT(std::abs(adjustedChanceTotal - 1.0f) <= FLT_EPSILON, "adjustedChanceTotal != 1.0f");
}
}
有没有想过为什么这个方法在两种构建配置之间的行为可能不同?
答案归功于 Igor Tandetnik。
char letterChanceKey[1];
sprintf(letterChanceKey, "%c", alphabetChar);
没有为终止的 NUL 留出足够的空间,导致缓冲区溢出。增加缓冲区大小解决了这个问题。
谢谢伊戈尔!