I am trying to read from a file and put each new line/entry in to an array. However, I am more primarily familiar with C# and C++ isn't my thing. The reason I need to do this is for a project and I am the only one that is willing to do this part.
I do not know how to properly convert character types or if it is possible. I have tried searching around the internet but have not found any answers regarding something like my issue here. Also, because I do not know what causes an illegal sizeof operand I do not know what is wrong here.
#include "..\STDInclude.h"
// TODO: Fill that list with names
char* Bots::NameList[] = {};
void Bots::GetNames()
{
using namespace std;
ifstream file("bot_names.txt");
if (file.is_open())
{
for (int i = 0; i < 48; i++)
{
file >> Bots::NameList[i];
}
}
}
void Bots::Initialize()
{
// Replace staff array with custom one
*(char***)Addresses::BotArray1 = Bots::NameList;
*(char***)Addresses::BotArray2 = Bots::NameList;
*(char***)Addresses::BotArray3 = Bots::NameList;
// Apply new array size
int size = (sizeof(Bots::NameList) / sizeof(Bots::NameList[0]));
*(BYTE*)Addresses::BotArraySize1 = size;
*(BYTE*)Addresses::BotArraySize2 = size;
*(BYTE*)Addresses::BotArraySize3 = size;
*(BYTE*)Addresses::BotArraySize4 = size;
}
Arrays in C++ are of fixed size. So when you write char* Bots::NameList[] = {} , you have an empty array of c-strings (aka char*).
Worse, when you later write file >> Bots::NameList[i]; you are writing null terminated c-strings to uninitialized pointers, which will cause memory corruption.
Unless fundamental rewrite, this code is doomed to fail. I strongly suggest that you replace use of char* with std::string and that you switch from fixed size arrays to vectors.
std::vector<std::string> Bots::NameList;
void Bots::GetNames()
{
...
for (int i = 0; i < 48; i++)
{
string s; // space separated strings ? sure ?
file >> s; // or getline(file, s) if you want lines
NameList.push_back(s);
}
}
}
Aditional remark:
I can't tell for sure, as I don't know the definition of Addresses members, but statements like the following are relatively suspicious:
*(char***)Addresses::BotArray1 = Bots::NameList;
It suggests that Addresses::BotArray1 is an array or a pointer. But the fact that you are casting with (char***) suggest that you tried to fix a type mismatch. And dereferencing the casted pointer will make sense only if BotArray points already to a valid char** pointer in which the address of NameList should be stored.
In C++ an array must be sized when created and then it is fixed, so char* Bots::NameList[] = {}; is a zero element array and is stuck that way. Worse, in file >> Bots::NameList[i] nothing ever allocated storage for Bots::NameList[i], so your program is writing into uninitialized memory. Probably a BOOM waiting to happen. I'm going to suggest something completely different.
in the Bots class definition:
std::vector<std::string> NameList;
Then later...
void Bots::GetNames()
{
std::ifstream file("bot_names.txt");
if (file.is_open())
{
for (int i = 0; i < 48; i++)
{
std::string temp;
file >> temp;
NameList.push_back(temp);
}
}
}
Addresses::BotArray1..N must also become std::vectors and Addresses::BotArraySize1..N are made redundant because vectors know their size.
Related
What I'm trying to do here is quite simple. But some way, some how I'm missing something. I'm trying to "pre-declare" an array with strictly string elements in such a way that I can update the array contents during a specific period (periodically). So here are the snippets:
string ShotBox[] = {}; //"Pre-Declare" array that could contain as many elements here
Then I have a loop within my Update container. Snippet's:
for (int i = 0; i < sizeof(ShotBox) - 1; i++){
std::string soa = sPath;
std::string so = soa + ShotBox[i];
char *cstr = new char[so.length() + 1];
strcpy(cstr, so.c_str());
scPath = cstr;
}
All is fine with everything except the fact that whichever way I try to "pre-declare", I get a memory access violation. In this very exact snippets, the exact error is: an empty array is invalid for an array with unspecified bound.
I tried using "vector" but can't seem to work around it. What's the way to solve this? Please I don't want libraries. I need direct short methods or something of such.
Use std::vector<std::string>.
The vector manages an internal array.
std::vector<std::string> ShotBox; // (empty)
ShotBox.push_back(a_string); // add a string to the internal array
std::cout << ShotBox[0] << '\n'; // print first element
The problem of memory access violation itself is caused by your misunderstanding of sizeof operator. Specifically, sizeof(ShotBox) is the size (in bytes) of your array, not the ShotBox[] array size.
for (int i = 0; i < sizeof(ShotBox)/sizeof(std::string); i++) {
...
}
Inside the for loop ShotBox[] elements aren't updated at all. The only thing that happens is concatenation of sPath with ShotBox[i] into a new C string 'cstr'. If your aim is to update ShotBox[i] element, just add the following assignment to the end of for loop:
for (int i = 0; i < N_SHOT_BOX; i++) {
...
ShotBox[i] = so;
}
It's much more convenient to use std::vector for working with collections of variable size:
#include <string>
#include <vector>
#include <memory.h>
int main() {
std::vector<std::string> ShotBox{"str1", "str2", "str3"};
for (int i = 0; i < ShotBox.size(); i++){
std::string soa = sPath;
std::string so = soa + ShotBox[i];
char *cstr = new char[so.length() + 1];
strcpy(cstr, so.c_str());
ShotBox[i] = cstr;
}
return 0;
}
Galik's answer suggesting std::vector is the Modern C++ way to do what you want to do.
The reason your code doesn't work is that the following line of code doesn't do what you think it does
string ShotBox[] = {}; //"Pre-Declare" array that could contain as many elements here
Try adding the following to your program ...
std::cout << sizeof(ShotBox) << std::endl;
... and you should find that you've declared an array that is zero bytes long. Indeed some compilers will treat an empty initializer as an error if the array bounds are not specified.
In the C++ language arrays are fixed length entities. One way to approximate a dynamic array is to use a pointer and to use memory management functions to allocate a larger array and then copy the old array contents into the new larger array.
But that is a really bad idea.
Doing it correctly with exception safety and efficiency is hard to do and if you do manage it, you'll have re-implemented std::vector which seems like wasted effort!
EDIT: Im quite new to c++ and programming as a whole.
I'm supposed to make a program where i use stucts and and an array of structs.
Security council < > Member of Security council
My task was to use the concept of "UML aggregation" to create a program where I use structs and struct arrays. (I hope you understand what I'm trying to say)
Since a Member of a Security council is a part of a Security council, and not the other way around, the struct of Security council must have an array of its members.(bear with me)
//example
struct Member_sc{
char * name;
int age;
};
struct Security_council{
Member_sc members[10];
};
Now, I've created this program and everything works perfectly (according to my teacher), but now she told me create an exact copy, but instead of the "members" array I must use an array of pointers to the Member_sc structs. Since I havent completely figured out how pointers work, I have come across some problems.
I can post the code to the original program if needed, but it contains 4 files(main, header, and some function files) and it would be a pain to try and post it here.
here is the prototype (all in one file, for now)
#include <iostream>
using namespace std;
struct member_sc{
string name;
};
struct security_council{
member_sc *point;
security_council **search; // ignore this for now
int n;
security_council():n(0){}
};
void in_mem( member_sc &x){
getline(cin,x.name);
}
void out_mem(member_sc &x){
cout<<x.name<<endl;
}
void in_SC(security_council &q, member_sc &x){
int num; //number of members
cin>>num;
for(int i=0; i<num; ++i){
in_mem(x);
q.point[q.n]=x;
q.n++;
}
}
void out_SC(security_council &q,member_sc &x){
for(int i=0; i<q.n; ++i){
out_mem(q.point[i]);
}
}
int main(){
member_sc y;
security_council x;
in_mem(y); // works
out_mem(y); // works
in_SC(x,y); // crashes after i input the number of members i want
out_SC(x,y); //
system("pause");
return 0;
}
The program crashes after you input the number of members you want in your Security council.
Is my way of thinking right? or should I use dynamic memory allocation?
in addition to that (my teacher gave me an additional task) create a search function using pointers. i thought that pointer to pointer may be good for that, but im not sure.
Any help or advice would be greatly appreciated.
( i think ill figure out the search thingy once i figure out how pointers to structs work)
The first part of your issue is this:
cin >> num;
this reads only the digits that have been typed and stops at the newline. Then, in in_mem the call to getline immediately reads a newline. You need to do:
cin >> num;
cin.ignore();
this will drain the input stream of any remaining input, or catch up so to speak.
However your core problem is that you don't allocate any memory for "point" to point to.
A pointer is just a variable holding a value that happens to be the address (offset from 0) of a thing in memory. If you are going to the airport and write "Gate 23" on a post-it note, the post it note is a pointer and "Gate 23" is the value.
In your code, that variable is uninitialized and will either be 0, if you are lucky, or some random address in memory if you aren't so lucky.
To the airport analogy: you arrive at the airport and find that your post-it note has "pizza" written on it. Not helpful.
Your teacher has actually specified an "array of pointers". Break that down: pointer to what? member_sc, that's member_sc*. And now make it an array
member_sc* pointers[10];
NOTE: This is not good, modern C++ - in modern C++ you would use something called a smart pointer (std::unique_ptr) probably.
std::unique_ptr<member_sc[]> pointers(new member_sc[10]);
Now you have 10 pointers instead of just one, and all of them will need some allocation to point to. The easiest way to do this is with the new keyword and the copy constructor:
for (int i = 0; i < num; i++) {
in_mem(x);
pointers[q.n] = new member_sc(x); // make a clone of x
q.n++;
}
or in modern C++
for (int i = 0; i < num; i++) {
in_mem(x); // x is temporary for reading in
pointers[q.n] = std::make_unique<member_sc>(x);
q.n++;
}
However there is a limitation with this approach: you can only have upto 10 security council members. How do you work around this? Well, the modern C++ answer would be to use a std::vector
std::vector<member_sc> members;
// ditch n - vector tracks it for you.
// ...
for (int i = 0; i < num; ++i) {
in_mem(x);
q.members.push_back(x);
// q.n is replaced with q.members.size()
// which is tracked automatically for you
}
but I'm guessing your teacher wants you to actually understand pointers before you get to forget about them with modern luxuries.
We need to re-use the pointer stuff we've just used above and change "pointers" to an array of pointers.
Which means we're going to want a pointer to a set of pointer-to-member_sc.
member_sc** pointers;
We'll need to assign some memory for this to point to:
cin >> num;
cin.ignore();
if (num == 0) {
// do something
return;
}
pointers = new member_sc[num];
luckily, using a pointer to an array is as easy as using an array, the only major difference being that you lose the size-of-array information -- all you have is the address, not the dimensions.
for (int i = 0; i < num; i++) {
in_mem(x);
q.pointers[i] = new member_sc(x);
q.n++;
}
I'm deliberately not providing you with a complete working example because this is obviously for a class.
You never initialize the memory that the point member refers to, yet then in statement q.point[q.n]=x; you attempt to use it.
Basically, after you read in the number of members, and before the for loop where you read in the individual members, you need to allocate an array of an appropriate number of member_sc objects and store it in q.point. Don't forget to free this memory when done using it.
Once you do that, you can also remove the member_sc &x argument from both in_SC and out_SC, as that will become unnecessary.
Finally, some validation of your input seems to be in place. Consider what will happen if the user enters a negative number, and you attempt to use that directly to determine the size of memory to allocate.
Here's a simple example showing how to use a dynamically allocated array of structures:
#include <iostream>
#include <string>
struct member_sc {
std::string name;
};
void test_array(int count)
{
if (count <= 0) {
return; // Error
}
// Allocate an array of appropriate size
member_sc* members = new member_sc[count];
if (members == nullptr) {
return; // Error
}
// ... fill in the individual array elements
for(int i(0); i < count; ++i) {
// ... read from input stream
// I'll just generate some names to keep it simple
members[i].name = "User A";
members[i].name[5] += i; // Change the last character, so we have different names
}
// Now let's try printing out the members...
for(int i(0); i < count; ++i) {
std::cout << i << ": " << members[i].name << "\n";
}
delete[] members;
}
int main(int argc, char** argv)
{
for(int count(1); count <= 10; ++count) {
std::cout << "Test count=" << count << "\n";
test_array(count);
std::cout << "\n";
}
return 0;
}
Example on Coliru
Of course, there are many other issues with this style of code, but I believe that's beside the point of this question. For example:
Instead of using bare pointers, it would be more appropriate to use some kind of a smart pointer.
Instead of a simple array, use some kind of collection, such as a vector.
Since you are asked to use an array of pointers, do so: replace
Member_sc members[10];
with
Member_sc* members[10];
Then fill out that array using dynamic memory allocation. As a matter of good form, at the end of the program remember to release the dynamic memory you have used.
I'm having trouble with wrapping my head around pointers, and using pointers in structs. To start, I don't know if I am using the pointer properly in the struct. Additionally, when I run my program, it seems to crash when it reaches readRecords, so there must be something wrong with it. Since I don't quite know how to use pointers very well, I am probably doing something wrong here... I just don't know what. Is there some way that I can edit this function so that I don't get crashes? Also, I have to keep these functions, as they are part of my project requirements.
struct testScores
{
string name;
string idNum;
int testNum;
int *tests; // This is supposed to be a dynamically allocated array
double average;
char grade;
};
[...]
void arrStruct(testScores*& sPtr)
{
sPtr = new testScores[];
}
void readRecords(ifstream& data, int record, testScores*& sPtr)
{
for (int count = 0; count < record; count++)
{
data >> sPtr[count].name;
data >> sPtr[count].idNum;
data >> sPtr[count].testNum;
sPtr[count].tests = new int[sPtr[count].testNum]; // tests is dynamically allocated (?)
for (int tCount = 0; tCount < sPtr[count].testNum; tCount++)
data >> sPtr[count].tests[tCount];
}
}
sPtr = new testScores[];
This appears to be illegal syntax - array new requires a subscript to know how much space to allocate.
Usually this is at least 1, in your case the compiler probably interprets this as new testScores[0] which does return a valid pointer, but without allocating any memory from the heap.
Of course any subsequent access to memory pointed to by sPtr is out-of-bounds and causes undefined behaviour (in your case a crash).
#include "average.c++"
#include "name.c++"
class Grade {
public:
Grade() {}
void searcharray(Name *array[]) {
int i;
for(i = 0; i <= 10; i++){
printf("%s", array->name);
}
}
};
int main() {
int i;
char line[64];
Name *names[10];
for(i = 0; i < 5; i++){
scanf("%s", &line);
names[i] = new Name(line);
}
Grade *test;
test = new Grade();
test->searcharray(names);
}
This code gives the error
"grade.c++ in member function 'void Grad::searcharray(Name*)':
grade.c++:11:25: error: request for member 'name' in ' array', which is of pointer type 'Name*' (maybe you meant to use '->' ?)"
I need help making this work. I am guessing it is something simple like extending the class like you would in Java but not sure how this works in c++.
I am assuming you can pass an array of objects to a class like you would in C with just an array.
The root to my question is to find a solution and to get a reason for this code being wrong.
Your code can be substantially improved by taking advantage of the Standard library. The problem with your initial code was that you were doing array->name where array was a C-style array (technically the pointer into which it decayed). An expression like that isn't possible unless you obtain the pointer at the index first:
array[i]->name;
Moreover, the for loop in which that line was written is traversing the array 1 too many times. The conditional statement i <= 10 should be i < 10 so you won't dereference an address past the end of the array.
Anyway, instead of showing your code with the corrections, I thought I might as well show you what it should look like if you use vectors, memory-management, and std::string. I hope this helps:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
class Grade
{
public:
Grade() { }
static void searcharray(const std::vector<std::unique_ptr<Name>>& array)
{
for (const auto& obj : array)
{
std::cout << obj->name;
}
}
};
int main()
{
std::string name;
std::vector<std::unique_ptr<Name>> names;
while (std::cin >> name)
names.push_back(std::unique_ptr<Name>(new Name(name)));
// names.push_back(std::make_unique<Name>(name))
Grade::searcharray(names);
}
Note that I also made searcharray static since it has nothing to do with a given instance of Grade.
As others have pointed out the problem is that you're using a parameter declared Name *array[] like array->name.
Remember that C++ built on top of C, which follows a rule 'declaration mimics use', which means that the way a variable is declared looks like the way it is used. So with the declaration:
Name *array[]
The way you get a name out of this is:
*array[i]
And name is a member of Name so you have to get a Name object first. Then you can tack on member access:
(*array[i]).name
And then you can use the -> shortcut where (*x).y is the same as x.y:
array[i]->name
Other issues:
Your code appears to be heavily influenced by the style of code required for the 1989 or 1990 version of C. You should try to avoid that as it makes writing C++ code much worse than it has to be.
You declare a Grade * and allocate it immediately. You can combine the declaration with initialization into:
Grade *test = new Grade();
But you don't need to use a pointer anyway: use Grade test; (and if you did need a pointer then you should use a smart pointer. Never use 'naked' new.)
Similarly you can avoid new when you create Names.
Name names[10]; // assuming that Name is default constructible
for(...) {
...
name[i] = Name(line);
}
You should avoid a fixed size array here. Instead you should default to using std::vector:
std::vector<Name> names;
for (...) {
...
names.push_back(Name(line)); // or in C++11 names.emplace_back(line);
}
You should declare the variable i as part of the for loop, not as a variable outside it:
for (int i=0; i<10; ++i)
When you read input you should avoid scanf and fixed sized buffers. Instead, if you're reading lines you should probably start off with std::getline and std::string.
std::string line;
while (std::getline(std::cin, line)) { // read as many lines as there are, not just 10 no matter what
names.emplace_back(line);
}
Lets say i store headers in some file, but some part of the header is dynamic length, something like this it would look:
struct HeaderTest {
int someparam;
int more;
int arrsize; // how big array, read arrsize elements into arr:
int arr[arrsize]; // not valid
};
Is there some elegant way for reading dynamic data into a struct?
Instead of having arr and arrsize variables in your struct, you can define your struct like this:
struct HeaderTest
{
int someparam;
int more;
std::vector<int> data;
}
No arr, no arrsize. Just use std::vector, and std::vector::size(). That is elegant!
And if you want to read binary data from a file, then you can write like this:
struct HeaderTest
{
int someparam;
int more;
int size;
char *data;
}
Otherwise, go with the first struct!
An Advice:
Reading your comments everywhere, I feel that I should suggest you to get a good book, and study it first. Here is list of really good books:
The Definitive C++ Book Guide and List
Well, if you don't want to use a container class (not sure why you wouldn't) you can declare arr as a pointer to int and leave it to the client to initialize the pointer to a valid memory location as well as correctly initialize arrsize.
That said, you should just use a vector. Why make things more difficult than they need to be?
This answer is more C than C++, but, you can easily make use of realloc() to resize a buffer to be as large as you need it. As demonstrated in this pseudo code.
struct HeaderTest {
int someparam;
int more;
int arrsize;
int arr[];
};
HeaderTest* pkt = (HeaderTest*)malloc(sizeof(HeaderTest));
read(&pkt,sizeof(pkt));
pkt = (HeaderTest*)realloc(pkt,sizeof(HeaderTest)+sizeof(pkt->arr[0])*pkt->arrsize);
read(pkt->arr,sizeof(int)*pkt->arrsize);
I don't think there is a very elegant way. You should probably make that dynamic member a pointer, then read all other members first, allocate memory for the last one, and then read the remainder of the data.
Since you're in C++, you can nicely encapsulate this in a class so that you don't have to worry about this detail in your code anymore. Also, as other have said, a std::vector would be a more C++-like approach than a simple pointer and manually allocated memory. It would also be more resistant to memory leaks.
No one was able to give you the solution you wanted, but I have devised it for you.
This function takes a C-string filename, opens the file and reads the contents for you. It returns an int*, which can be assigned to t.container. Enjoy.
int* read(char* filename)
{
// open file
ifstream f;
f.open(filename, ios::binary);
// get file size
f.seekg (0, ios::end);
int length = f.tellg();
f.seekg (0, ios::beg);
// allocate new int*
length = (length -(sizeof(int)*2)) / sizeof(int);
int* buf = new int[length];
for(int i = 0; i < length; ++i)
{
// create byte array to hold bytes
unsigned char* temp = new char[sizeof(int)];
stream.read((char*)temp, sizeof(int));
// convert byte array to int
for(int j = 0; j < sizeof(int); ++j)
{
buf[i] = buf[i] + (temp[j] << (j*8));
}
delete[] temp;
}
f.close();
return buf;
}