Access violation when inserting element into globa

2020-04-16 18:38发布

问题:

I've been trying to debug this for hours now with no luck. I know you guys will solve the problem within minutes, so here's the situation:

I've got ~400 .cpp/.h files called ProblemX.cpp/ProblemX.h (where X is a number from 1 to 400). Each file contains the solution for a math related problem. I would like to have the problems register themselves at compile time to a global map with a unique key (just an int will work) and have the value be a pointer to the function that kicks off the math problem solution.

The global map is created and handled in files called Problem.h/Problem.cpp. However, I'm getting an "Access violation reading location 0x00000004" when the first Problem attempts to self-register in the map. The code is as follows:

In the ProblemX.h files (problem1 kicks off the solution for this problem):

#ifndef PROBLEM1_H
#define PROBLEM1_H

#include "Problems.h"
#include <string>

std::string problem1();
static int rc1 = registerProblem(1, problem1);

#endif

In the Problems.h file (problemFinder is the function that uses the global map to call the appropriate function pointer):

#ifndef PROBLEMS_H
#define PROBLEMS_H

#include <string>

int registerProblem(int problemNum, std::string (*problemFunc)(void));
std::string problemFinder(int problemNum);

#endif

In Problems.cpp:

#include "Problems.h"
#include <iostream>
#include <map>

using namespace std;

map<int,std::string (*)(void)> problems_map;

int registerProblem(int problemNum, string (*problemFunc)(void)) {
    int rc = 0;
    problems_map[problemNum] = problemFunc;
    return rc;
}


string problemFinder(int problemNum) {
    string retStr = "";
    retStr = problems_map[problemNum]();
    return retStr;
}

The access violation occurs where "problems_map[problemNum] = problemFunc;".

Thanks!

回答1:

As the enigmatically named user93353 answered, the problems_map global is not guaranteed to be constructed before globals in other files.

To avoid the static initialization order fiasco, make problems_map a local static, that will be init'd on its first use:

map<int,std::string (*)(void)>&
get_problems_map()
{
  static map<int,std::string (*)(void)> problems_map;
  return problems_map;
}

Then use it like so:

get_problems_map()[problemNum] = problemFunc;

This ensures the map gets created as soon as it's needed, not just when the global constructors from Problems.cpp get run, which might be (and in your case definitely is) after the global rc1 variable gets initialised.



回答2:

Beware using statics like this. The order in which they are defined can make a difference. The variable might be there but it is not necessarily constructed. This actually bit me on the bum yesterday, so it's fresh in my memory.

In Problems.cpp, the order things are defined is:

static int rc1 = registerProblem(1, problem1);
map<int,std::string (*)(void)> problems_map;

That means rc1 is initialised before problems_map.

Your issue therefore comes about because registerProblem is called to initialise rc1 and it uses problems_map which has not yet been constructed.

I always thought the compiler would work this out. But when you think about it, the general case is too difficult to consider (especially if you end up with mutual dependencies). So I imagine the only sane thing to do is expect the programmer to put their static definitions in the correct order, just as they ought to do with any other code statements.



回答3:

Check the C++ FAQ on Constructors. In particular check questions 10.14 to 10.18 - "static initialization order fiasco".

What's the "static initialization order fiasco"?

A subtle way to crash your program.

The static initialization order fiasco is a very subtle and commonly misunderstood aspect of C++. Unfortunately it's very hard to detect — the errors often occur before main() begins.

In short, suppose you have two static objects x and y which exist in separate source files, say x.cpp and y.cpp. Suppose further that the initialization for the y object (typically the y object's constructor) calls some method on the x object.

That's it. It's that simple.



标签: c++ map