Hello, I need help with a bug.
I've added three new weapons to the game. They all blow up (cause a segmentation fault in Unix lingo) when the following line of code is run:
weapontype[getweapontype(itemtypename())]->get_name(subtype)
The part that is crashing is the getweapontype(itemtypename()) part. getweapontype(const string) has the following code:
/* transforms a weapon type name into the index of that weapon type in the global vector */
int getweapontype(const string &idname)
{
for(int i=0;i<(int)weapontype.size();i++)
{
if(weapontype[i]->get_idname()==idname)
{
return i;
}
}
return -1;
}
The segmentation fault happens upon the return of the function. I have put test statements (consisting of getch() and fprintf statements to stderr) all through this, so I know that getweapontype() works properly up until the moment of returning i, at which point it crashes. I also tried creating a local variable in Weapon::get_name() and setting getweapontype() equal to that before looking up the weapontype[] vector. getweapontype() crashes on returning i, irrespective of whether it is used as an element of weapon type[] or if it is simply returning an int to a local value, and it crashes for all three of my new weapon types - this despite that getweapontype() can see the new weapon types within itself.
Can anybody please help tell me what is going on and how to fix it?
Thanks!
I looked at that code and it does not have any bugs in it (the array index stays within bounds, using const references to std::strings is a very safe programming practice that makes bugs and crashes unlikely, you are simply returning an integer, I don't see anything in that code that could cause a bug, except for one thing... that function calls the get_idname() function of weapontype
[i
], so perhaps the bug might be there). The bug is probably somewhere else but it does not cause a segfault until that code happens.
Anyway, the problem you encountered, a segmentation fault, is typically caused by using pointers in C/C++. A segmentation fault occurs when software attempts to read or write a location in memory that it does not have permission from the operating system to read or write to. This can be caused by an array/vector/etc. index being out of bounds, dereferencing an uninitialized or NULL pointer, attempting to access an object that has been deleted, or a buffer or stack overflow. In C/C++, arrays are actually implemented as pointers to an area of memory, and the indexing operator [] adds a number to the pointer equal to the size of the objects in the array times the value you are indexing. And vectors are implemented as arrays, and thus ultimately pointers. What is safest is references, as they always have to be initialized and they are not allowed to be NULL.
Anyway, it appears as if, either explicitly or implicitly through how the compiler implemented the code, one of those things happened. So here are the 4 likely culprits:
1) Uninitialized or null pointer/object getting dereferenced/accessed
int *i; // not initialized or allocated
(*i)++; // this could point to anything... SIGSEGV... Segmentation Fault
2) Array/vector index out of bounds
char c[13]="Conservative"; // we need 13 characters to store this as null-terminated
addch(c[13]); // if an array is of size 13, its highest index will be 12 so this is out of bounds... SIGSEGV... Segmentation Fault
3) Attempting to access an object that has been deleted/freed (this can even happen with references, if you create a reference to an object pointed to by a pointer, then delete the object, then try to access the reference... normally references are safer than pointers but even they don't protect you from everything)
delete x; // x is deleted, gone, outta here
gamelog.log(x); // you can't use x, we just deleted it... SIGSEGV... Segmentation Fault
4) Attempting to modify read-only memory (for example trying to the value of something that is const)
char *s="liberal"; // "liberal" is actually implicitly a const expression that the char* pointer s is pointing to, you can't modify it
*s='L'; // I said, you can't modify it! It's const, even though it doesn't say so explicitly... SIGSEGV... Segmentation Fault
The fact that it occurs when you are returning from this function is interesting... that means it likely has something to do with the code that happens when you enter and leave the function, and variables that change scope... old local variables that are no longer local variables, and new local variables that were local prior to the function call, weren't local inside the function that got called, but are now local again. This could also be the result of strange compiler optimizations or a buggy compiler.
However the 4 likely culprits I listed above, those are probably what caused it, try and look for those. Sometimes, a program can do one of those things and get away with it temporarily, without crashing immediately, and then the crash will happen a bit later on, maybe in a different function... like a ticking time bomb. The memory access violation, the segmentation fault, was probably caused by something that happened earlier, but that the program got away with without being caught, temporarily. And then when trying to return from this function, it suddenly detonates the segmentation fault without any warning, when actually the bug probably happened earlier, before this function was ever called, or maybe inside the function it calls, get_idname() from the WeaponType class.
It appears the WeaponType class populates values from XML, from an XML file. So if you leave out certain values in the XML file, that might trigger a crash because a value would never be populated to a certain variable and it would remain uninitialized.
Still, I don't have enough to go on here, I'll need to see how exactly you added the 3 weapons, and the XML and/or C++ code for that. This type of bug can be a little hard to track down. That is why some people prefer languages such as Java which are designed in such a way as to make this type of bug almost impossible to happen, whereas C/C++ is designed so it's hard to avoid this type of bug happening unless you are perfect and never make mistakes. The benefit of C/C++ over Java, though, is that being able to use pointers, despite their dangerousness, allows you to make programs that run much faster and have less overhead. I am grouping C and C++ together here because when it comes to this type of thing, a segmentation fault, and many other things, the 2 languages are pretty much the same, and the vast majority of C code is also valid C++ code without any alterations (with the C++11 standard adding in the bits from C that were missing from the earlier C++98 standard such as specific-size integer types with specific numbers of bits). C++ does add plenty of stuff to try and make errors like segmentation faults happen less often, for instance you can replace arrays with std::vectors and C-strings with std::strings and pointers with references and that all makes crashes less likely.
I have a pretty strong suspicion this is one of those time-bomb bugs where something bad happens earlier but the compiler didn't catch it when compiling, the operating system didn't catch it when it executed, and some earlier code left you a time-bomb segmentation fault bug that got triggered later on. Ah, I think I have it... when you leave a function, it gets rid of the local variables and other function call overhead that is stored in memory. Well, what must have happened is, the earlier time-bomb made certain memory locations, shall we say, unsuitable for use, and then this function got called and used them, and when the function tried to return and free up that memory, it couldn't, because of some unsavory activity that happened at those memory locations previously... namely an uninitialized pointer/object being dereferenced/accessed, an array/vector index out of bounds, or attempting to access an object that had been deleted. That happened in a certain memory location and made it "dirty", and then this function used memory in the "dirty" location and triggered the error.