I have written a code to read a file, store it in a structure and just display it. But somehow it is giving me a segmentation fault and I dont know why. Can someone please help me?
Output:
file: /home/neel/map2.txt
file opened
Start Intersection
a->road: 4
a->roadId[0]: 1
a->lane[0][0]: 2
a->lane[0][1]: 2
a->roadId[1]: 2
a->lane[1][0]: 2
a->lane[1][1]: 2
a->roadId[2]: 3
Segmentation fault
Code:
#include <iostream>
#include <fstream>
#include <stdio.h>
using namespace std;
struct Intersection
{
unsigned short road;
long long int *roadId;
short *lane[2];
};
int main(int argc, char** argv)
{
std::ifstream file;
cout<<"file: "<<argv[1]<<endl;
file.open(argv[1], std::ios::in);
cout<<"file opened"<<endl;
while (!file.eof())
{
cout<<"Start Intersection"<<endl;
Intersection *a = new Intersection;
file>>a->road;
a->roadId = new long long int[a->road];
a->lane[0] = new short[a->road];
a->lane[1] = new short[a->road];
cout<<"a->road: "<<a->road<<endl;
for (int i=0; i<a->road; i++)
{
file>>a->roadId[i];
cout<<endl<<"a->roadId["<<i<<"]: "<<a->roadId[i]<<endl;
file>>a->lane[i][0];
cout<<"a->lane["<<i<<"][0]: "<<a->lane[i][0]<<endl;
file>>a->lane[i][1];
cout<<"a->lane["<<i<<"][1]: "<<a->lane[i][1]<<endl;
}
cout<<"Intersection inserted"<<endl;
delete a;
}
}
Text file:
4
1
2
2
2
2
2
3
2
2
4
2
2
Your lane is an array of 2 elements, however when i reaches 2 in your inner loop you are trying to print a->lane[2][0], which doesn't exist.
file>>a->lane[i][0]; //wrong
file>>a->lane[i][1]; //wrong
The indices should be reverse:
file>>(a->lane[0][i]); //correct
file>>(a->lane[1][i]); //correct
I added brackets just for clarity.
Besides, there is memory leak in your program. There should be as many delete as there are new statements, to ensure that there is no memory leak. So write these:
delete [] a->roadId;
delete [] a->lane[0];
delete [] a->lane[1];
delete a; //you've written only this!
Note delete a should be the last statement when deallocating the memory!
I don't mean to be nasty, but this code has enough problems it's almost difficult to decide which ones to start with.
using namespace std;
Here's the first red flag. About all I can say is that using namespace std; is a poor idea. With other namespaces it can be acceptable, but with std, it should always be avoided (IMO).
struct Intersection
{
unsigned short road;
long long int *roadId;
short *lane[2];
};
This strikes me as a pretty poorly designed structure. std::vector is a good thing. Use it. Rather than just a structure of dumb data, you might want to consider defining operator>> for your structure type, so you can read one directly. Even if you don't do that, from the way you're using it, what you really want is something more like:
struct road {
long long Id;
short lane[2];
};
struct Intersection {
int road_count;
road *roads;
};
Then, rather than a couple of parallel arrays all the same size that have to be walked in parallel, you get a a number of roads, each with its own data. std::vector is still better though.
int main(int argc, char** argv)
{
std::ifstream file;
cout<<"file: "<<argv[1]<<endl;
file.open(argv[1], std::ios::in);
Rather than defining an ifstream object and then opening it separately, you should normally plan on passing the name to the ctor so it's defined and opened in a single operation, something like:
std::ifstream file(argv[1]);
But, you also normally want to add a bit of error checking so you only attempt to use the command line argument as a file name if one has been passed, something like this:
if (argc < 2) {
std::cerr << "Usage: your_command <filename>\n";
return EXIT_FAILURE;
}
Then you'd have the code to define the ifstream.
while (!file.eof())
Here's another major problem. A loop of this form is essentially always wrong (including this case, from the looks of things).
cout<<"Start Intersection"<<endl;
Intersection *a = new Intersection;
There seems to be no reason to allocate this dynamically. Are you, perhaps, a recovering (or perhaps not recovering) Java or C# programmer? Java requires that all objects of user defined classes be allocated dynamically, but C++ does not.
file>>a->road;
a->roadId = new long long int[a->road];
a->lane[0] = new short[a->road];
a->lane[1] = new short[a->road];
cout<<"a->road: "<<a->road<<endl;
for (int i=0; i<a->road; i++)
{
file>>a->roadId[i];
cout<<endl<<"a->roadId["<<i<<"]: "<<a->roadId[i]<<endl;
file>>a->lane[i][0];
cout<<"a->lane["<<i<<"][0]: "<<a->lane[i][0]<<endl;
file>>a->lane[i][1];
cout<<"a->lane["<<i<<"][1]: "<<a->lane[i][1]<<endl;
}
I'd prefer to separate the code for reading data from the code for displaying data. Except for things like homework (or debugging) you rarely want to display lots of raw data as you're reading it. In any case, the reading code should normally reside in the operator>> for that class, and the display code in operator<< for the class.
cout<<"Intersection inserted"<<endl;
This seems to be an outright falsehood. You haven't actually inserted an Intersection into anything.
delete a;
When you quit allocating the Intersection dynamically, you'll be able to eliminate this as well. If you insist on handling all the dynamic allocation by hand, you need to delete the components before this to avoid having memory leaks (another reason to prefer to std::vector).
I know that may sound pretty negative, and that leaves me a bit torn. On one hand, I'd really like to suggest better ways to do things. At the same time, this looks enough like homework that I'm extremely hesitant to just post better code either. I've tried to include a few hints about better ways, but realize they're probably not quite as specific as you'd like -- I apologize for that, but given that it's probably homework I don't think I can be a lot more specific.
Related
I'm working on a fairly straightforward priority scheduling program which takes a text file with lines in the format:
[N/S/n/s] number number
And I'm attempting to convert the numbers into a double format. I'm trying to do this with a stringstream (this is a class project which must run on a version of Linux without stod), using the example here as a reference: https://www.geeksforgeeks.org/converting-strings-numbers-cc/
The problem is, when I try and implement what I'd think should be a fairly simple few lines of code to do this, I'm met with "Segmentation Fault (Core Dumped)" which seems directly related to my attempt to actually send the stringstream to the double variable I've created. I've included my code so far (still obviously quite far from done) and have also indicated the last line I'm able to execute by outputting "made it to here." I'm very confused about this issue, and would appreciate any help available. Note that although I've posted my entire code for completion's sake, only a small part near the bottom is relevant to the question, which I've clearly indicated.
Code:
#include <iostream>
#include <stdio.h>
#include<string.h>
#include <stdlib.h>
#include<cstring>
#include<sstream>
#include<cstdlib>
#include <unistd.h>
#include<pthread.h>
#include<ctype.h>
#include <vector>
#include <sys/wait.h>
#include <fstream>
#include<ctype.h>
using namespace std;
struct Train{
public:
char trainDirection;
double trainPriority;
double trainTimeToLoad;
double trainTimeToCross;
};
void *trainFunction (void* t){cout << "placeholder function for "<< t <<endl;}
vector<string> split(string str, char c = ' '){
vector<string> result;
int start = 0;
int end = 3;
int loadCounter = 1;
int crossCounter = 1;
result.push_back(str.substr(start, 1));
start = 2;
while (str.at(end) != ' '){
end++;
loadCounter++;
}
result.push_back(str.substr(start, loadCounter));
start = end + 1;
end = start +1;
while(end < str.size()){
end++;
crossCounter++;
}
result.push_back(str.substr(start, crossCounter));
for(int i = 0; i < result.size(); i++){
cout << result[i] <<"|";
}
cout<<endl;
return result;
}
int main(int argc, char **argv){
//READ THE FILE
const char* file = argv[1];
cout << file <<endl;
ifstream fileInput (file);
string line;
char* tokenPointer;
int threadCount = 0;
int indexOfThread = 0;
while(getline(fileInput, line)){
threadCount++;
}
fileInput.clear();
fileInput.seekg(0, ios::beg);
//CREATE THREADS
pthread_t thread[threadCount];
while(getline(fileInput, line)){
vector<string> splitLine = split(line);
//create thread
struct Train *trainInstance;
stringstream directionStringStream(splitLine[0]);
char directionChar = 'x';
directionStringStream >> directionChar;
trainInstance->trainDirection = directionChar;
if(splitLine[0] == "N" || splitLine[0] == "S"){
trainInstance->trainPriority = 1;
}
else{
trainInstance->trainPriority = 0;
}
stringstream loadingTimeStringStream(splitLine[1]);
double doubleLT = 0;
cout << "made it to here" <<endl;
loadingTimeStringStream >> doubleLT; //THIS IS THE PROBLEM LINE
trainInstance->trainTimeToLoad = doubleLT;
stringstream crossingTimeStringStream(splitLine[2]);
double doubleCT = 0;
crossingTimeStringStream >> doubleCT;
trainInstance->trainTimeToCross = doubleCT;
pthread_create(&thread[indexOfThread], NULL, trainFunction,(void *) trainInstance);
indexOfThread++;
}
}
There are some mistakes in your code that lead to undefined behavior and that's the cause of your segmentation fault. Namely:
You don't check the number of arguments before using them
You do not return a value in trainFunction
You do not create a valid object for trainInstance to point to
The first two are somewhat obvious to solve, so I'll talk about the last one. Memory management in C++ is nuanced and the proper solution depends on your use case. Because Train objects are small it would be optimal to have them allocated as local variables. The tricky part here is to ensure they are not destroyed prematurely.
Simply changing the declaration to struct Train trainInstance; won't work because this struct will be destroyed at the end of the current loop iteration while the thread will still be potentially alive and try to access the struct.
To ensure that the Train objects are destroyed after the threads are done we must declare them before the thread array and make sure to join the threads before they go out of scope.
Train trainInstances[threadCount];
pthread_t thread[threadCount];
while(...) {
...
pthread_create(&thread[indexOfThread], nullptr, trainFunction,static_cast<void *>(&trainInstances[indexOfThread]));
}
// Join threads eventually
// Use trainInstances safely after all threads have joined
// trainInstances will be destroyed at the end of this scope
This is clean and works, but it isn't optimal because you might want the threads to outlive the trainInstances for some reason. Keeping them alive until the threads are destroyed is a waste of memory in that case. Depending on the number of objects it might not even be worth it to waste time trying to optimize the time of their destruction but you can do something like the following.
pthread_t thread[threadCount];
{
Train trainInstances[threadCount];
while(...) {
...
pthread_create(&thread[indexOfThread], nullptr, trainFunction,static_cast<void *>(&trainInstances[indexOfThread]));
}
// Have threads use some signalling mechanism to signify they are done
// and will never attempt to use their Train instance again
// Use trainInstances
} // trainInstances destroyed
// threads still alive
It is best to avoid pointers when dealing with threads that don't provide a C++ interface because it is a pain to deal with dynamic memory management when you can't simply pass smart pointers around by value. If you use a new statement, execution must always reach exactly one corresponding delete statement on the returned pointer. While this may sound trivial in some cases it is complicated because of potential exceptions and early return statements.
Finally, note the change of the pthread_create call to the following.
pthread_create(&thread[indexOfThread], nullptr, trainFunction,static_cast<void *>(&trainInstances[indexOfThread]));
There are two significant changes to the safety of this line.
Use of nullptr : NULL has an integral type and can silently be passed to non-pointer argument. Without named arguments, this is a problem because it is hard to spot the error without looking up the function signature and verifying the arguments one-by-one. nullptr is type-safe and will cause a compiler error whenever it is assigned to a non-pointer type without an explicit cast.
Use of static_cast : C-style casts are dangerous business. They will try a bunch of different casts and go with the first one that works, which might not be what you intended. Take a look at the following code.
// Has the generic interface required by pthreads
void* pthreadFunc(void*);
int main() {
int i;
pthreadFunc((void*)i);
}
Oops! It should have been (void*)(&i) to cast the address of i to void*. But the compiler won't throw an error because it can implicitly convert an integral value to a void* so it will simply cast the value of i to void* and pass that to the function with potentially disastrous effects. Using a static_cast will catch that error. static_cast<void*>(i) simply fails to compile, so we notice our mistake and change it to static_cast<void*>(&i)
You dereferenced trainInstance via -> operator without assigning valid buffer, so the system will try to write to weird place and cause segmentation fault.
You can allocate buffer like this:
struct Train *trainInstance = new struct Train;
struct is not needed here, but I used one because it is used in the original code.
Also don't forget to check the number of arguments before using argv[1].
this is the code which i am trying to run.The compiler shows some warning that the variable 'p' may be possible to be used uninitialized.
And ..on running it has a problem ..i will show..below.
please correct me and explain .
Thank you.
execution of code
*
code
that error is that..the first two elements of the character array being output are somewhat messed up!!!*
#include<bits/stdc++.h>
using namespace std;
class file{
public:int a;
char* name;
public:
file(int x,char* b):a(x){name=b;}
void printfile(){cout<<a<<" "<<name<<endl;}
};
int main(){
char *p;
int x=10;
cout<<"enter a name"<<endl;
cin>>p;
file k(x,p);
ofstream f("file",ios::out|ios::binary);
f.write((char*)&k,sizeof(class file));
f.close();
ifstream of("file",ios::in|ios::binary);
file o(0,'\0');
of.read((char*)&o,sizeof(class file));
o.printfile();
of.close();
return 1;
}
You have p as some pointer. Who is going to allocate the memory that pointer points to?
In C it is almost always the responsibility of the caller to allocate any buffers before the call.
If you don't want to, then use a std::string instead.
First of all I would advise you to read some basic manual about the c++ pointers and memory handling so you will better understand the source of the problem.
There are two major problems in your code.
The first is that you are creating a pointer which is not connected to any allocated memory. In simple words you are asking to access a memory address without asking the system to reserve it for you.
Additionally, in that memory location there can already be stored any bit configuration. The initialization of a variable is the task of giving a chunk of memory some data which have a meaningful interpretation. I'm not even sure what the in-stream operator of char* is supposed to do in this particular case. He is probably appending your characters after the last one which is not a 0 or an end of line.
We are trying to return an array of structures to print the contents in the main. When debugging the code, we get to the line right before the return statement and it shows that it is holding the right contents which is an int and then a string (playerID and name). As soon as the return statement executes the array is returned to the main but only the playerID is held in the array. All of the values for name have been lost. Can someone explain why this would happen and a possible solution? If further clarification is needed please let me know.
#include<iostream>
#include<fstream>
#include<cstring>
#include<string>
#include<math.h>
using namespace std;
struct Player
{
int playerID;
string name;
};
Player *fillPlayers();
int main()
{
Player *myPlayerPointer;
myPlayerPointer = fillPlayers();
return 0;
}
Player * fillPlayers() {
ifstream file1;
Player * fullPlayerPointer = new Player[244];
Player ourPlayers[244];
file1.open("Players.txt");
if (file1.fail()) {
cerr << "file1 did not open";
}
if (file1.is_open()){
while (!file1.eof()){
string size;
getline(file1, size);
for(int i = 0; i < 244; i++){
file1 >> ourPlayers[i].playerID;
file1 >> ourPlayers[i].name;
}
}
file1.close();
}
fullPlayerPointer = &ourPlayers[0];
return fullPlayerPointer;
}
This code looks a lot like C code. In C++ we have fancy RAII containers like std::vector and std::array that will do exactly what you want.
As for the issue, you are not returning an array, instead you are returning a pointer to an int. You should check out What is array decaying?.
http://en.cppreference.com/w/cpp/container/vector
http://en.cppreference.com/w/cpp/container/array (C++ >= 11)
The generally correct answer is use a std::vector as it assists in managing ownership and the lifetime of storage as well as tracking the capacity and resizing itself as needed. But let's take a walk through fillPlayers to explain what is going wrong.
Player * fillPlayers()
{
ifstream file1;
Player * fullPlayerPointer = new Player[244];
Dynamically allocated 244 Players. Dynamic storage has manually controlled lifetime and will remain valid until such time as it is freed. Unfortunately this allocated storage is never used and not put away correctly later with delete[] This allocation will be "leaked". As dire as this sounds, this is the "right" way to allocate the storage, but as we will see, the function uses it incorrectly.
Player ourPlayers[244];
Statically allocated 244 Players as a temporary variable. This variable will only exist inside the innermost set of surrounding curly braces {}. Afterward it will be rendered invalid. This means that references to ourPlayers should not be returned from this function as ourPlayers will be rendered invalid at the return from the function and before the caller can make use of it.
file1.open("Players.txt");
if (file1.fail())
{
cerr << "file1 did not open";
}
Always test before using, so the above is almost right. It is rendered redundant by the next line performing a nearly identical test. This could be a good place to put an else, but the code is more easily read with if (file1.is_open()) followed by an else to print the error message if it is not open.
Why do I say this? Because the programmer's intent with is_open is much easier to discern than with the broader term fail.
if (file1.is_open())
{
while (!file1.eof())
Read Why is iostream::eof inside a loop condition considered wrong? for details on why this is almost always the wrong solution to looping through a file.
{
string size;
getline(file1, size);
Always test a read to ensure it succeeded. In addition, the file size was read and then not converted into an integer to use in the loop that follows.
for (int i = 0; i < 244; i++)
Regardless of how many entries there are in this file 244 will always be read. If the file does not have at least 244 entries, the read will fail and as the reads are not being checked for success, garbage will be stored in the array.
Also note that there is a loop iterating through 244 entries in the file that is surrounded by a loop that will attempt to read again until the EOF flag is set. Most likely you only want one such loop.
{
file1 >> ourPlayers[i].playerID;
file1 >> ourPlayers[i].name;
}
}
file1.close();
}
fullPlayerPointer = &ourPlayers[0];
The pointer to the dynamic allocation made earlier is overwritten by a pointer to the temporary allocation ourPlayers. A reference to long-term storage has been replaced by a reference to storage that is about to go out of scope and become invalid.
It is possible that OP intended this to copy the data in the short term storage to the long term storage, but unfortunately that's not what the compiler was told to do, and it's not really worth doing. It would be much more useful to directly read the file into the long term storage.
return fullPlayerPointer;
Returns from the function and gives an invalid array to the caller.
}
Lot to fix in there.
Here's a very simple approach that fixes all of the above problems but exposes a few more:
Player * fillPlayers()
{
ifstream file1("Players.txt");
Player * players = nullptr;
if (file1.is_open())
{
int size;
if (file1 >> size)
{
players = new Player[size];
int index = 0;
while (file1 >> players[index].playerID >> players[index].name
&& index < size)
{
}
// extra brains needed here to handle premature read failure.
}
file1.close();
}
else
{
cerr << "file1 did not open";
}
return players; // only a pointer to the array was returned. How big the
// array is and how many items are actually in it is lost
}
This is where std::vector really becomes awesome. It knows how big it is and how full it is. An array doesn't.
Now, assuming std::vector is not allowed, and Paul McKenzie has already covered what to do if it isn't, the smart thing to do is make a very simple wrapper around the array to get some modicum of the safety and ease of use vector provides.
class stupidvector
{
Player *players;
size_t capacity; // a typical vector implementation uses a pointer for
// this to make iteration easier
size_t size; // vector uses pointer here, too.
public:
stupidvector();
stupidvector(size_t size);
// correctly copy a stupid vector Rule of Three. In this case, don't
// allow it to be copied.
stupidvector(const stupidvector& src)=delete;
// correctly move a stupid vector Rule of Five
stupidvector(stupidvector && src);
// release storage
~stupidvector();
// add an item to the end
void push(const Player & player);
// how big is it?
size_t getcapacity();
// how full is it?
size_t getsize();
// make it act like an array
Player & operator[](size_t index);
// correctly assign. Rule of Three. again we don't want to copy these,
// but if we did, look at Copy and Swap Idiom for a neat solution
stupidvector& operator=(const stupidvector& src) = delete;
// correctly move
stupidvector& operator=(stupidvector && src);
};
Pay special attention to the Rules of Three and Five
My code that I have is quite large and complicated so I won't waste your time reading it, but you're going to have to make certain assumtions about variables in it as a result. I will tell you the values of the variables which I have confirmed in the debugger so you know with certainty. Know that I have omitted a lot of unrelated code in here so what you see isn't everything but I have included everything that is relevant.
// This is defined in a class:
char**** m_DataKeys;
// This is in a member function of the same class:
m_DataKeys = new char*** [m_iNumOfHeroes]; // m_iNumOfHeroes = 2
while ( pkvHero )
{
// iHeroNum = 0 and then 1 #define NUM_OF_ABILITIES 4
m_DataKeys[iHeroNum] = new char** [NUM_OF_ABILITIES];
for (int ability = 0; ability < NUM_OF_ABILITIES; ability++)
{
if (pkvExtraData) // only is true when iHeroNum == 1 and ability == 0
{
// iNumOfExtraData == 2
m_DataKeys[iHeroNum][ability] = new char* [iNumOfExtraData];
while ( pkvSubKey )
{
// iCurExtraDataNum increments from 0 to 2
m_DataKeys[iHeroNum][ability][iCurExtraDataNum] = new char [50];
I put a break point on the line
m_DataKeys[iHeroNum] = new char** [NUM_OF_ABILITIES];
Before the line is called and when iHeroNum == 0 the m_DataKeys array looks like:
m_DataKeys | 0x02072a60
pointer | 0xffeeffee
Error : expression cannot be evaluated
Which is expected. After the line gets called it looks like:
m_DataKeys | 0x02072a60
pointer | 0x02496b00
pointer | 0xffeeffee
Error : expression cannot be evaluated
Which seems to look correct. However, since I set a breakpoint there, I hit play and had it hit it on the next loop around, where iHeroNum == 1 now and ran the line and m_DataKeys then looked like this:
m_DataKeys | 0x02072a60
pointer | 0x02496b00
pointer | 0xffeeffee
Error : expression cannot be evaluated
Which is the exact same as before! The line didn't change the array.... At all!
For clarification, m_DataKeys is a 3 dimensional array of character pointers to character arrays of size 50.
I can't figure out why this is happening, it looks like my code is correct. Is it possible that the garbage collector is screwing me over here? Or maybe the new allocator?
Edit: A Symptom of a Larger Problem
Let me elaborate a little more on the structure of my code, because really, this is just a cheap solution to a bigger problem.
I already have structs as one of you wisely suggested:
struct HeroData
{
// Lots o data here
// ...
// .
//
AbilityData* Abilities[NUM_OF_ABILITIES];
}
struct AbilityData
{
// More data here
// ...
// .
CUtlMap<char*,int> ExtraData [MAX_ABILITY_LEVELS];
}
Now when it got complicated and I had to do this DataKeys arrays of pointers to arrays of pointers crap is only when the need arose to be loading in some data to a dynamic structure, where both the keys, the values, and the numbers of data are completely dynamic. So I thought to use a map of char arrays to ints, but the only problem is that I can't store the actual char array in my map, I have to use a char *. I tried defining the map as:
CUtlMap<char[50],int> ExtraData [MAX_ABILITY_LEVELS];
But that really didn't work and it seems sort of strange to me anyway. So, I had to find some place to stick all these ExtraDataKeys and for some reason I thought it cool to do it like this. How can I store char arrays in objects like arrays or maps?
Since you are using pointers as class members, my best guess is that you are violating The Rule Of Three. That is, you did not provide a copy constructor and a copy assignment operator for your class. That usually leads to strange data loss when passing objects of your class around.
Note that no sane C++ programmer would use char****. Here is my best attempt to fix your problem using vectors and strings, but there is probably a much better design for your specific problem:
#include <string>
#include <vector>
class Foo
{
int m_iNumOfHeroes;
std::vector<std::vector<std::vector<std::string> > > m_DataKeys;
enum { NUM_OF_ABILITIES = 4, iNumOfExtraData = 2 };
public:
explicit Foo(int iNumOfHeroes)
: m_iNumOfHeroes(iNumOfHeroes)
, m_DataKeys(m_iNumOfHeroes, std::vector<std::vector<std::string> >
(NUM_OF_ABILITIES, std::vector<std::string>(iNumOfExtraData)))
{
}
};
int main()
{
Foo x(2);
}
In case you have never seen that colon syntax in the constructor before, that is a member initializer list.
I really wish C++ had array bounds checking
std::vector and std::string do have bounds checking if you use the foo.at(i) syntax instead of foo[i]. In Debug mode, even foo[i] has bounds checking enabled in Visual C++, IIRC.
Though the code might be correct, I personally find that working with something like a char **** can get pretty confusing pretty fast.
This is just my personal preference, but I always try to organize things in the most clear and unambiguous way I can, so what I would do in your situation would be something like
struct Ability
{
char extraData[NUM_OF_EXTRA_DATA][50];
};
struct HeroData
{
Ability abilities[NUM_OF_ABILITIES];
};
class Foo
{
// here you can choose a
HeroData *heroArray;
// and then you would alloc it with "heroArray = new HeroData[m_iNumOfHeroes];"
// or you can more simply go with a
std::vector<HeroData> heroVector;
};
I think this makes things more clear, making it easier for you and other programmers working on that code to keep track of what is what inside your arrays.
I think you expect the wrong thing to happen (that the visual display in the debugger would change), even though your code seems correct.
Your debugger displays m_DataKeys, *m_DataKeys and **m_DataKeys, which is the same as m_DataKeys, m_DataKeys[0] and m_DataKeys[0][0]. When you change m_DataKeys[1], you are not going to notice it in your debugger output.
The following might help you: in my debugger (MS Visual Studio 2005), if you enter e.g. m_DataKeys,5 as your watch expression, you will see the first 5 elements of the array, that is, m_DataKeys[0], m_DataKeys[1], ..., m_DataKeys[4] - arranged in a neat table. If this syntax (with the ,5) doesn't work for you, just add m_DataKeys[1] into the debugger's watch window.
Not sure why this didn't occur to me last night, but I was pretty tired. Heres what I decided to do:
struct AbilityData
{
// Stuff
CUtlMap<char*,int> ExtraData [MAX_ABILITY_LEVELS];
char **DataKeys;
}
Thats what my abilityData struct now looks like, and it now works, but now I want to reorganize it to be like:
struct AbilityData
{
// Stuff
CUtlMap<char*,int[MAX_ABILITY_LEVELS]> ExtraData;
char **DataKeys;
}
Because it makes more sense that way, but then I run into the same problem that I had before with the char array. It almost seems like to me it might just be best to ditch the whole map idea and make it like:
struct AbilityData
{
// Stuff
int *ExtraData;
char **DataKeys;
}
Where ExtraData is now also a dynamically allocated array.
The only problem with that is that I now have to get my data via a function which will loop through all the DataKeys, find a matching key for my input string, then return the ExtraData associated with it.
This question was migrated 12 years ago.
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
int main (){
int a,b;
b = 0;
cout<<" this is a family profiling program to test my knowledge of structural arrays. ";
cin>> a;
struct p {
char name[20];
int age;
char hobby[40];
char favcolor[15];
};
p family[2];
**cin.getline(family.name[0]);**
cout<<"enter the name of family member"<<b+1;
I am trying to use this code to create a family profiling program, i know how to add value to an array or structural array during compiler time but not to use cin or cin.getline to add a value to to a specific value in a specific structure of an array.
please respond with a simple answer; Im still new to programming.(my attempt is bolded
If you insist on using an array, the easiest way to do this (add elements to a fixed-size array) would be to copy everything to a NEW array, including the new item.
A much better way would be to use a dynamic structure, such as a linked list. The standard template library and the boost library have many ways to help you here, but you could also easily implement a simple linked list on your own (or find code for it).
As Mike Chess said, this is probably best asked on http://www.stackoverflow.com
(also, to get your code formatted nicely, edit your post, select the code section, and click the button with the ones and zeros icon just above the text area)
Firstly, you'll find it much easier to write reliable code if you use std::string instead of character arrays. You were nearly on the right track though: instead of family.name[0] try family[0].name, then getline will work with std::string as below...
struct p
{
std::string name;
// other stuff...
};
if (getline(std::cin, family[0].name))
{
// it worked!
...
}
The "high performance" and old school option is to use realloc like this.
p * family = malloc(sizeof(p)*2);
family = realloc(family, sizeof(p)*13);
Of course this doesn't invoke constructors, and is not really acceptable in C++ generally. So your best option is.
#include <list>
using namespace std;
...
list<p> family;
p blah;
family.push_back(blah);
That's a linked list so it's perfect for datasets of unknown length. Same code can be used for an STL vector, which if you predict the size of your input in advance well enough will give you a performance boost.