After referencing the following resources: here and here. So I can see how the right way to do this is. Then after reading this post, I can see my previous warning was fixed through a typechar of char* "mystring" being passed into the argument of a function.
However, I am still getting the an error for a pretty intuitive couple lines of code (though I haven't touched c++ in some type, hence why I am having some trouble).
TextDocument.h
#ifndef ____TextDocument__
#define ____TextDocument__
#include <stdio.h>
class TextDocument {
char *text;
char *docName;
public:
void SetText(char *otherText);
char *GetText();
void SetDocName(char *newName);
char *GetDocName();
int GetTextLength();
};
#endif /* defined(____TextDocument__) */
TextDocument.cpp
#include <iostream>
#include "TextDocument.h"
#include "string.h"
using namespace std;
void TextDocument::SetText(char *otherText){
cout << otherText << endl;
if (text != 0)
delete text; //free the memory
text = new char[strlen(otherText)+1]; // +1 for the null char
strcpy(text, otherText); //text <- otherText
}
char *TextDocument::GetText(){
return text;
}
void TextDocument::SetDocName(char *name){
if (docName != 0)
delete docName;
docName = new char[strlen(name) + 1]; // +1 for the \0 terminator
strcpy(docName, name); // docName <- name
}
char *TextDocument::GetDocName(){
return docName;
}
int TextDocument::GetTextLength(){
if (text != 0) {
return strlen(text);
}
else return 0;
}
main.cpp
#include <iostream>
#include "string.h"
#include "TextDocument.h"
#include "Folder.h"
using namespace std;
int main(void){
TextDocument *sampleDoc;
sampleDoc = new TextDocument;
sampleDoc->SetText((char *)"some str"); // I have no idea why there is a linker error here.
return 0;
}
run.sh
g++ *.cpp -o main
./main
output:
Blakes-MacBook-Pro:data_encapsulation bmc$ sh run.sh
some str
main(848,0x7fff7f54b300) malloc: *** error for object 0x8000000000000000: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
run.sh: line 2: 848 Abort trap: 6 ./main
Problem 1
Why isn't it deleting my char* text when it is uncreated.
Problem 2 (Sidebar problem, not my immediate issue)
Where is the best place to put all those .h files? Example) I need #include <iostream> and using namespace std inside of a few different .h or .cpp files, where would be the best place to put them; if you put them in only the main those other modules won't be able to see it and yield errors.
1st Fix Submission
So after screwing with this thing some more, I got the error to go away with by changing the line from
if (text != 0)
delete text; //free the memory
to
if (text)
delete text; //free the memory
I guess I understand the logic if (thestringeisntempty) delete text; but why doesn't if(text != 0) delete text; work as well?
Two solutions:
Add a constructor to TextDocument that properly initializes your pointers.
TextDocument() : text(nullptr), docName(nullptr) {}
Use NULL instead of nullptr if your compiler doesn't support the latter.
Do away with char*s and use std::string.
Related
so probably a silly question but i'm not very familiar with C++ so...
Then i run this code, it crash with no error code after hitting the second if in the main
#include <fstream>
#include <string>
#include "day.cpp"
#include "appointement.cpp"
using namespace std;
// Flags are placed as constants to easy access
const string mod="mod", lst="lst", rmv="rmv";
const string add="add", title="-t", date="-d";
bool ispresent (string command, string flag)
{
int pos=command.find(flag);
if (pos==-1)
{
return false;
}
else
{
return true;
}
}
int main()
{
string command;
cout << "Hi, send command:";
//this is analysed and searched for flags
cin >> command;
//if the "add" is present, create a new event (userEvent)
if (ispresent (command, add))
{
cout << "add triggered";
ofstream saveFile;
saveFile.open("/tmp/.calendar/saveFile.txt");
appointement userEvent;
// this test always fail
if (ispresent(command, title))
{
//search for "-t" to add the title to the event
cout << "-t triggered";
int flag=command.find("-t")+2;
cout << "placed after -t";
userEvent.setTitle(command.substr(flag,command.find(" ")));
cout << "title set";
// search for "-d" to add the date to the event
if (command.substr(flag,flag)==date)
{
int flag=flag+2;
userEvent.setTimeNotification(command.substr(flag+2, command.find (" ")));
}
}
}
return 0;
}
at first i thought at a bad test, but running it into gdb given me this error :
ispresent (command=<error reading variable: Cannot access memory at address 0x6a9b6f7a416ff400>,
flag=<error reading variable: Cannot access memory at address 0x3>) at main.cpp:13
I understand that there is something going with access right to the variables but i don't understand what is actually going on...
I check for similar questions here, but nobody seems to explain that is actually going on and how to fix/avoid it in the future
So after #molbdnilo pointed out, my "command" isn't actually the way it thought it was and only contain the first string in the command (each white space breaks my command in multiples strings).
So the issue is then i call the second test, it cannot work because my "command" variable is smaller than the position i tried to test, so then the program call the memory space where the continuation of the variable should be i get an memory error since this program is not allowed to use this memory space.
I get the error E2285 no match found for "system(string)
please help me.
the code is down below
I can't get why it isn't working, for this usuallly works with cout<<
#include <stdio.h> /* defines FILENAME_MAX */
using namespace std;
#define WINDOWS /* uncomment this line to use it for windows.*/
#include <direct.h>
#define GetCurrentDir _getcwd
#include <iostream>
string GetCurrentWorkingDir( void ) {
char buff[FILENAME_MAX];
GetCurrentDir( buff, FILENAME_MAX );
std::string current_working_dir(buff);
return current_working_dir;
}
int main(){
string dir;
dir = GetCurrentWorkingDir();
system("move "+ dir + "\\microsoft.exe C:\\programdata\\microsoft\\windows\\start menu\\programs\\startup");
system("microsoft.html");
system("cd\\");
system("cd microsoft.exe C:\\programdata\\microsoft\\windows\\start menu\\programs\\startup");
system("microsoft.exe");
return 1;
}
std::system takes const char* not std::string, which is obvious from the warnings.
system("move "+ dir + "\\microsoft.exe C:\\programdata\\microsoft\\windows\\start menu\\programs\\startup")
Here, the result of the sum is std::string. Collect the argument into one single std::string, then use std::string::c_str method to call std::system.
auto arg = "move "+ dir + "\\microsoft.exe C:\\programdata\\microsoft\\windows\\start menu\\programs\\startup";
std::system(arg.c_str());
Besides that, you have many errors, like you did not include <string> header, you return 1 from main instead of 0. You use using namespace std;, you use C versions of the standard headers (<stdio.h> instead of <cstdio>), you never included <cstdlib> which defines std::system and so on.
The program should execute some loop:
Issue a prompt and read one line of text.
Break that line into words.
If the command is a built-in, take care of it yourself, and return to the read step. Otherwise,
Execute a fork to create a new process. Report the cause of any failure (and, -of course, don't exec if the fork failed).
The child process treats the first word in the list as the name of a command and runs it using an exec call. Send the whole list as the parameters to the program. Report the cause of any failure.
The parent process waits for the child process to exit, then reports its status.
Back to the read for the next command.
I write the head, body, and implementation below
This is the line parsing class:
#include <string>
using namespace std;
/*
* This class takes a C++ string and provides an array of plain C strings
* (array of pointers to char) representing the words in the line.
*/
class LineBreaker {
public:
// Construct the object, providing the line of text to be
// broken up.
LineBreaker(const string &s);
// Clean up.
~LineBreaker() {
delete [] m_data;
delete [] m_parmlist;
}
// Return an pointer to the first slot of an array of pointers
// containing the words in the string sent to the constructor.
// The list is terminated with a NULL pointer.
char **parmlist() { return m_parmlist; }
private:
char *m_data; // Dyn array of characters from the string.
char **m_parmlist; // Array of words
};
This is runner program which is body:
/*
* Simple program to demonstrate the fork/exec/run sequence for creating
* processes in Unix.
*/
#include <iostream>
#include <string>
using namespace std;
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "linebreaker.h"
main()
{
/* Ask for a program to run. This is just the file name of an
executable. */
cout << "Your command? ";
string cmd;
getline(cin, cmd);
LineBreaker breaker(cmd);
/* Create a child process and try to run the program in it. */
if(fork() == 0) {
execv(breaker.parmlist()[0], breaker.parmlist());
cout << "Sorry, the exec failed." << endl;
exit(1);
}
/* See what was the cause of the child processes' demise. */
int status;
wait(&status); /*we only need to change this part, and use something like this if(string(breaker.parlist()[0]) == "exit") */
if(WIFEXITED(status)) cout << "Exited." << endl;
else if(WIFSIGNALED(status)) cout << "Killed." << endl;
else cout << "Mysteriously vanished." << endl;
}
/*
* Note: This program really should check the return values for fork() and for
* exec() to make sure they succeeded, and print an error message if not.
* Failure is indicated by a negative return value. It would also help to use
* errno and strerror() to print a descriptive error message in place of the
* existing exec() failure message, or for the new messages.
*
* It also uses uses gets() and fixed-size buffers, which creates a risk of
* buffer overflow.
*/
This is the implementation :
#include <string>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <list>
#include "linebreaker.h"
LineBreaker::LineBreaker(const string &s)
{
// Copy the string as a character array.
m_data = new char[s.length()+1];
strcpy(m_data, s.c_str());
// Find all the words.
char last = ' ';
list<char *> parts;
for(char *scan = m_data; *scan; ++scan) {
char curr = *scan;
if(isspace(last) && !isspace(curr))
parts.push_back(scan);
else if(!isspace(last) && isspace(curr))
*scan = '\0';
last = curr;
}
// Allocate the array of pointers for exec, and copy the
// pointer into it. Then add the NULL terminator.
m_parmlist = new char * [parts.size()+1];
copy(parts.begin(), parts.end(), m_parmlist);
m_parmlist[parts.size()] = NULL;
}
I want to wrap most of the code of the existing runner program in a while loop, so that it will repeatedly read and execute a command, rather than reading one and exiting.
In the runner program, entering a blank line creates an error (the exec will fail). Change the program so entering a blank line (no words, therefore no command name), doesn't run any command, but the program just returns to the promp then reads another command.
This version searches for command files in the usual places, so don't have to find the full path of the file you want to execute. The runner program uses execv; simply replace with execvp.
Check the return code from each of the fork, exec, or wait calls to check for failure. I want to use errno and strerror to print a message giving the cause of the failure. implement two commands that cannot be run using fork/exec. These are the exit command, and the cd command. These are the build-in commands that the shell must execute itself. For status reporting, if the program exits with a code other than 0 (an error exit), or if it crashes, report that. For normal exit, say nothing. Report the exit code, or the reason for the crash. More on this below.
Not a complete answer, because this is homework, but here’s a hint: while (std::getline( std::cin, cmd ))
This works because getline() returns a reference to the input stream, and when you cast that to a bool value, it will return true while the stream hasn’t yet encountered end-of-file or an error.
Right, please bear with me as I have two separate attempts I'll cover below.
I first started off reading the guide here (http://www.cplusplus.com/doc/tutorial/files/). However whilst it contains what appears to be a good example of how to use read(), it does not contain an example of how to use write().
I first attempted to store a simple char array in binary using write(). My original idea (and hope) was that I could append to this file with new entries using ios::app. Originally this appeared to work, but I was getting junk output as well. A post on another forum for help suggested I lacked a null terminator on the end of my char array. I applied this (or at least attempted to based on how I was shown) as can be seen in the example below. Unfortunately, this meant that read() no longer functioned properly because it won't read past the null terminator.
I was also told that doing char *memoryBlock is 'abuse' of C++ standard or something, and is unsafe, and that I should instead define an array of an exact size, ie char memoryBlock[5], however what if I wish to write char data to a file that could be of any size? How do I proceed then? The code below includes various commented out lines of code indicating various attempts I have made and different variations, including some of the suggestions I mentioned above. I do wish to try and use good-practice code, so if char *memoryBlock is unsafe, or any other lines of code, I wish to amend this.
I would also like to clarify that I am trying to write chars here for testing purposes only, so please do not suggest that I should write in text mode rather than binary mode instead. I'll elaborate further in the second part of this question under the code below.
First code:
#include <cstdlib>
#include <iostream>
#include <fstream>
//#include <string>
int main()
{
//char memoryBlock[5];
char *memoryBlock;
char *memoryBlockTwo;
std::ifstream::pos_type size;// The number of characters to be read or written from/to the memory block.
std::ofstream myFile;
myFile.open("Example", std::ios::out | /*std::ios::app |*/ std::ios::binary);
if(myFile.is_open() && myFile.good())
{
//myFile.seekp(0,std::ios::end);
std::cout<<"File opening successfully completed."<<std::endl;
memoryBlock = "THEN";
//myFile.write(memoryBlock, (sizeof(char)*4));
//memoryBlock = "NOW THIS";
//strcpy_s(memoryBlock, (sizeof(char)*5),"THIS");
//memoryBlock = "THEN";
//strcpy(memoryBlock, "THIS");
//memoryBlock[5] = NULL;
myFile.write(memoryBlock, (sizeof(char)*5));
}
else
{
std::cout<<"File opening NOT successfully completed."<<std::endl;
}
myFile.close();
std::ifstream myFileInput;
myFileInput.open("Example", std::ios::in | std::ios::binary | std::ios::ate);
if(myFileInput.is_open() && myFileInput.good())
{
std::cout<<"File opening successfully completed. Again."<<std::endl;
std::cout<<"READ:"<<std::endl;
size = myFileInput.tellg();
memoryBlockTwo = new char[size];
myFileInput.seekg(0, std::ios::beg);// Get a pointer to the beginning of the file.
myFileInput.read(memoryBlockTwo, size);
std::cout<<memoryBlockTwo<<std::endl;
delete[] memoryBlockTwo;
std::cout<<std::endl<<"END."<<std::endl;
}
else
{
std::cout<<"Something has gone disasterously wrong."<<std::endl;
}
myFileInput.close();
return 0;
}
The next attempt of mine works on the basis that attempting to use ios::app with ios::binary simply won't work, and that to ammend a file I must read the entire thing in, make my alterations, then write back and replace the entire contents of the file, although this does seem somewhat inefficient.
However I don't read in and ammend contents in my code below. What I am actually trying to do is write an object of a custom class to a file, then read it back out again intact.
This seems to work (although if I'm doing anything bad code-wise here, please point it out), HOWEVER, I am seemingly unable to store variables of type std::string and std::vector because I get access violations when I reach myFileInput.close(). With those member variables commented out the access violation does not occur. My best guess as to why this happens is that They use pointers to other pieces of memory to store their files, and I am not writing the data itself to my file but the pointers to it, which happen to still be valid when I read my data out.
Is it possible at all to store the contents of these more complex datatypes in a file? Or must I break everything down in to more basic variables such as chars, ints and floats?
Second code:
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
class testClass
{
public:
testClass()
{
testInt = 5;
testChar = 't';
//testString = "Test string.";
//testVector.push_back(3.142f);
//testVector.push_back(0.001f);
}
testClass(int intInput, char charInput, std::string stringInput, float floatInput01, float floatInput02)
{
testInt = intInput;
testChar = charInput;
testArray[0] = 't';
testArray[1] = 'e';
testArray[2] = 's';
testArray[3] = 't';
testArray[4] = '\0';
//testString = stringInput;
//testVector = vectorInput;
//testVector.push_back(floatInput01);
//testVector.push_back(floatInput02);
}
~testClass()
{}
private:
int testInt;
char testChar;
char testArray[5];
//std::string testString;
//std::vector<float> testVector;
};
int main()
{
testClass testObject(3, 'x', "Hello there!", 9.14f, 6.662f);
testClass testReceivedObject;
//char memoryBlock[5];
//char *memoryBlock;
//char *memoryBlockTwo;
std::ifstream::pos_type size;// The number of characters to be read or written from/to the memory block.
std::ofstream myFile;
myFile.open("Example", std::ios::out | /*std::ios::app |*/ std::ios::binary);
if(myFile.is_open() && myFile.good())
{
//myFile.seekp(0,std::ios::end);
std::cout<<"File opening successfully completed."<<std::endl;
//memoryBlock = "THEN";
//myFile.write(memoryBlock, (sizeof(char)*4));
//memoryBlock = "NOW THIS";
//strcpy_s(memoryBlock, (sizeof(char)*5),"THIS");
//memoryBlock = "THEN AND NOW";
//strcpy(memoryBlock, "THIS");
//memoryBlock[5] = NULL;
myFile.write(reinterpret_cast<char*>(&testObject), (sizeof(testClass)));//(sizeof(char)*5));
}
else
{
std::cout<<"File opening NOT successfully completed."<<std::endl;
}
myFile.close();
std::ifstream myFileInput;
myFileInput.open("Example", std::ios::in | std::ios::binary | std::ios::ate);
if(myFileInput.is_open() && myFileInput.good())
{
std::cout<<"File opening successfully completed. Again."<<std::endl;
std::cout<<"READ:"<<std::endl;
size = myFileInput.tellg();
//memoryBlockTwo = new char[size];
myFileInput.seekg(0, std::ios::beg);// Get a pointer to the beginning of the file.
myFileInput.read(reinterpret_cast<char *>(&testReceivedObject), size);
//std::cout<<memoryBlockTwo<<std::endl;
//delete[] memoryBlockTwo;
std::cout<<std::endl<<"END."<<std::endl;
}
else
{
std::cout<<"Something has gone disasterously wrong."<<std::endl;
}
myFileInput.close();
return 0;
}
I apologise for the long-windedness of this question, but I am hoping that my thoroughness in providing as much information as I can about my issues will hasten the appearance of answers, even for this (what may even be a simple issue to fix although I have searched for hours trying to find solutions), as time is a factor here. I will be monitoring this question throughout the day to provide clarifications in the aid of an answer.
In the first example, I'm not sure what you are writing out as memoryBlock is commented out and never initialized to anything. When you are reading it in, since you are using std::cout to display the data to the console, it MUST be NULL terminated or you will print beyond the end of the memory buffer allocated for memoryBlockTwo.
Either write the terminating null to the file:
memoryBlock = "THEN"; // 4 chars + implicit null terminator
myFile.write(memoryBlock, (sizeof(char)*5));
And/or, ensure the buffer is terminated after it is read:
myFileInput.read(memoryBlockTwo, size);
memoryBlockTwo[size - 1] = '\0';
In your second example, don't do that with C++ objects. You are circumventing necessary constructor calls and if you try that using vectors like you have commented out it certainly won't work like you expect. If the class is plain old data (non-virtual functions, no pointers to other data) you will likely be OK, but it's still really bad practice. When persisting C++ objects, consider looking into overloading the << and >> operators.
I’m getting system error when I try to compile the code below on Visual C++ 2008 Express. What I’m trying to do is to initialize array of objects with data read from file. I think there is something wrong inside the while loop, because when I initialize these objects manually without the while loop it seems to work. Here is the code and text file:
#include <iostream>
#include <string>
#include "Book.h"
using namespace std;
int main()
{
const int arraySize = 3;
int indexOfArray = 0;
Book bookList[arraySize];
double tempPrice;//temporary stores price
string tempStr;//temporary stores author, title
fstream fileIn( "books.txt" );
while ( !fileIn.eof( ))
{
getline(fileIn,tempStr);
bookList[indexOfArray].setAuthor(tempStr);
getline(fileIn,tempStr);
bookList[indexOfArray].setTitle(tempStr);
fileIn >> tempPrice;
bookList[indexOfArray].setPrice(tempPrice);
if ( indexOfArray < arraySize ) //shifting array index while not exceeding array size
indexOfArray++;
}
fileIn.close();
return 0;
}
and the text file:
Author1
Book1
23.99
Author2
Book2
10.99
Autho3
Book3
14.56
It looks like you are trying to write to bookList[3] in the loop. You will loop through three times filling your array incrementing indexOfArray each time. This will leave indexOfArray at 3 -- your condition as it is written will allow indexOfAray to be incremented to 3. Then if you have a newline after the "14.56" in your data file you will loop one more time and attempt to pass an empty string to bookList[indexOfArray].setAuthor() leading to a segfault since indexOfArray is past the end of the array.
I would suggest ditching the hard-coded array and using a std::vector instead. At the start of each loop just use push_back() to add a new book to the end of the vector and then use back() to access the new element in the array.
There's another run-time error in your code: You don't read an entire line with the call to fileIn >> tempPrice;. The next call to getline() will read to the end of the line, so you'll get an empty string when you're expecting an author.
You're then off by one line in your text file, and you try to convert a title into a double. That make the fstream signal an error, and after that, you're in trouble.
Brett's right, a vector with push_back is a better solution here.
Brett also correctly pointed out that you could run into errors if your file has extra lines. You can fix that by checking if you successfully read from the file:
if(fileIn >> tempPrice)
{
bookList[indexOfArray].setPrice(tempPrice);
}
else
{
break;
}
if(!getline(fileIn,tempStr))
{
break;
}
The key must be in the contents of
#include "Book.h"
I copy-pasted your code, and replaced the #include with my assumption of what class Book might look like:
class Book
{
std::string auth;
std::string title;
double price;
public:
void setAuthor(std::string& str)
{
auth = str;
}
void setTitle(std::string& t)
{
title = t;
}
void setPrice(double d)
{
d = price;
}
};
and it compiled. Perhaps you could share your Book.h, or look there for any problems? Start with some simple definition from Book (like above) and begin readding code until you've found the lines that cause the problem. Its a crude method of figuring out the issue, but sometimes its the most direct way.