Can't understand why I'm getting a segfault - c++

I'm using very little memory on the stack, and I have no recursion, and all my memory access is on the stack. So why am I getting a segfault?
#include <iostream>
#include <string>
#include <stdio.h>
using namespace std;
int main(int argc, char *argv[]){
FILE *file = fopen("test.cpp", "r");
struct item{
char *type;
int price;
bool wanted;
};
item items[100]; char *temp;
if (file)
cout << "works up to here" << endl;
fscanf(file,
"%s, %[for sale wanted], %d",
items[0].type,
temp,
&items[0].price);
}
It prints out
works up to here
Segmentation fault (core dumped)

You are passing pointers to fscanf that are not initialized. You need to do something like this:
(if you are using C)
FILE* file = fopen(...);
char* str = malloc(N);
fscanf(file, "%s", str);
printf("Read %s\n", str);
free(str);
fclose(file);
(if you are actually using C++)
std::ifstream file(...);
std::string str;
file >> str;
std::cout << "Read " << str << std::endl;

The scanf() functions won't allocate any memory. From the looks of it you are passing uninitialized pointer to fscanf() where the function expects arrays of sufficient size instead.
Most likely you'd use something like
items[0].type = new char[100];
char temp[20];
if (3 == fscanf("%100s, %[for sale wanted], %d",
items[0].type,
temp,
&items[0].price)) {
// deal with a read item
}
else {
// deal with an input error
}
(I'm not sufficiently familiar with fscanf() to be confident about the middle format specifier).

Did you check if the file pointer is not null?
This is from fopen reference doc:
If the file is successfully opened, the function returns a pointer to
a FILE object that can be used to identify the stream on future
operations.
Otherwise, a null pointer is returned.
And as Kevin mentioned, this is more like C than C++

I see couple of problems.
You are not checking whether fopen() was successful.
You are trying to read into items[0].type, which has not been initialized to point to anything valid.
You will be better off using std::ifstream and std::string instead of using FILE* and char*.

Related

SIGABRT on std::ifstream close

1I am currently working on a project of creating my own game in OpenGL. My problem is right now, that if I read a file, that my function reading that file results in a SIGABRT, because of something inside the std::ifstream deconstructor (more specifically in "std::basic_ifstream<char, std::char_traits<char> >::~basic_ifstream()"). This function previously worked for me, but suddenly stopped working.
My Goal is simple: A reliable implementation for reading a file to a char*. Multi threading is currently not my concern.
Here is my implementation of the file reading function.
It takes in a path, and should write the content of the file at that path into the out parameter.
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <cstring>
#include <cassert>
#include "Utility.h"
char * Utility::readFile(const char* path,char*& out){
#ifndef NDEBUG
std::cout<<"Getting file: "<<path<<"\n";
#endif
// Open the file, but freak out if not valid.
std::ifstream file=std::ifstream(path);
assert(file.good());
if(!file.good())
{
throw std::runtime_error((std::string)"Couldn't open file for loading: "+path);
}
// Read the file contents into a char buffer.
std::stringstream buffer;buffer << file.rdbuf();
std::string fileContentsStr = buffer.str();
out = new char[fileContentsStr.size()];
strcpy(out,fileContentsStr.c_str());
return out;
}
My code is located at C0D3-M4513R/OpenGlGame.
I already tried a minimal example, which is working and using the same compile flags (except linker flags). test.txt and test1.txt just contain some rubbish text generated by randomly hacking on my keyboard.
#include <cassert>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <cstring>
//This Function is the same as the one above!!!
char *readFile(const char *path, char *&out) {
#ifndef NDEBUG
std::cout << "Getting file: " << path << "\n";
#endif
// Open the file, but freak out if not valid.
std::ifstream file = std::ifstream(path);
assert(file.good());
if (!file.good()) {
throw std::runtime_error((std::string) "Couldn't open file for loading: " + path);
}
// Read the file contents into a char buffer.
std::stringstream buffer;
buffer << file.rdbuf();
//convert the stringstream to a string
std::string fileContentsStr = buffer.str();
//copy the contents of the string to a char array
out = new char[fileContentsStr.size()];
strcpy(out, fileContentsStr.c_str());
//return char array address (which should be the same as the start?)
return out;
}
int main() {
//The programm started!
std::cout << "Hello, World!" << std::endl;
//Define a space for the contents of the file to live
char *out;
//Read the contents of a file
out = readFile("test.txt", out);
//Print contents of the file
std::cout << out << std::endl;
char *out1;
//Read the contents of a file
out1 = readFile("test1.txt", out1);
//Print contents of the file
std::cout << out1 << std::endl;
return 0;
}
strcpy:
Copies the character string pointed to by src, including the null terminator, to the character array whose first element is pointed to by dest.
The behavior is undefined if the dest array is not large enough. The behavior is undefined if the strings overlap.
c_str:
Returns a pointer to a null-terminated character array with data equivalent to those stored in the string.
out = new char[fileContentsStr.size()];
strcpy(out,fileContentsStr.c_str());
You need to be careful when mixing std::string with c-strings, because a std::string is not null-terminated and does not count the nullterminator for its size. However, c_str does return a pointer to a null-terminated character array.
You are asking strcpy to write fileContentsStr.size()+1 (size + null terminator) into a char array with only fileContentsStr.size() elements.
PS: As mentioned in a comment, you should consider to return a std::string instead. You are using a raw owning pointer which is error prone and should be avoided. Either use a smart-pointer or let a std::string manage the char-array (thats what its made for actually ;).

Character Array of dynamic length

I have written a C++ Function which can be represented as below:
All it does is take a string (this is where it crashes) and reverse it.
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
int main()
{
cout<<"Enter a string: "<<endl;
char *str;
gets(str);
cout<<"Reversed String is: ";
for(int i=strlen(str)-1;i>=0;i--)
cout<<(str[i]);
return 0;
}
I guess there's some kind of memory access violation.
Any clue why this doesn't work?
Error: Segmentation fault (core dumped)
In c++ there is way more easier and less error prone solution to this problem via std::reverse from algorithm. Also its easier to use std::string.
#include <iostream>
#include <algorithm>
int main ()
{
std::string input;
std::cout << "Enter string to reverse: ";
std::cin >> input;
std::reverse(input.begin(),input.end());
std::cout << "Reversed string: " << input << std::endl;
return 0;
}
If you have to do it via char arrays, try this (you dont even need dynamic memory allocation)
#include <iostream>
#include <algorithm>
#include <cstring>
int main ()
{
char input[1024];
puts("Enter string to reverse: ");
fgets(input, 1024, stdin);
std::reverse(input, input + strlen(input));
printf("Reversed string: %s", input);
return 0;
}
Your code isn't c++ style and I recommend you take a look at the answer from Filip (https://stackoverflow.com/a/45903067/4386427)
I'll just address what goes wrong with your code.
When you do
char* str;
all you get is a pointer that can point to a char. You don't get any memory for holding a char. Further the value of the pointer variable str is uninitialized.
So when you do
strlen(str)
you read an uninitialized variable and try to treat this uninitialized value as a C-style string. That is undefined behavior and is very likely to cause a program crash.
You need to make sure that str is initialized before using it. As you want dynamic memory, you could do:
char *str;
str = new(char[100]); // Initialize str to point to a dynamic allocated
// char array with size 100
...
...
delete(str);
But again - I wouldn't use this style in c++ code

Dynamic char field reading one char at the time

#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
char * text = new char;
scanf("%c", text);
scanf("%c", text+1);
return 0;
}
Sorry for this lame question...
I'm trying to read a string one char at a time (because I need to check sth for every char..)
For input "ab" the output is "ab" and a ?random? character...
It doesn't work without the 'scanf("%c", text+1);' either.. I can do this using static field, but this version gives me one extra char in the end.. What am I doing wrong? :'(
You allocate one char giving you the pointer text. You then access text[0] and text[1] which clearly give an out of bounds access. I strongly recommend you don't access memory explicitly and use a std::string instead, e.g.:
std::string text;
for (std::istreambuf_iterator<char> it(std::cin), end; it != end; ++it) {
// do whatever checks you need to do
text.push_back(*it);
}
std::cout << "read '" << text << "'\n";

Segmentation fault error - C programming

I am receiving a Segmentation Fault error when I run this program. To summarize, the program reads in multiple data files (129 of them). Each file contains information about a specific name. The information includes how many people were named a specific name in a specific year and the gender. For now, I am trying to store each name in a linked list. Whenever I read in more than about 4 data files, I get the Segmentation Fault error. I have written this program in Java, which was much simpler. If anybody could simply point me in the right direction, as I am almost certain this has to do with memory allocation, but I cannot seem to solve this myself. Thank you.
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <string.h>
#include <cstdlib>
using namespace std;
//typedef struct Node NodeStruct;
struct Node {
char *namePtr;
Node *nextPtr;
};
int main() {
// Declare variables
Node *headPtr = NULL;
Node *tempPtr;
Node *currentPtr;
// Variables for reading in a file
FILE *filePtr;
char fileName[20];
int i;
int nameLength;
char inputLine[81];
cout << "Reading from data files, please be patient...\n";
// Loop through files
for (i = 1880; i <= 2009; i++) {
sprintf(fileName, "data/yob%d.txt", i);
filePtr = fopen(fileName, "r"); // Open the file
// Check to ensure file was opened
if(filePtr == NULL) {
cout << "Error opening input file...check location of data files\n";
exit(-1); // Exit program
} // End if statement
while (fscanf(filePtr, "%s", inputLine) != EOF) {
// Create a node
tempPtr = (Node *) malloc(sizeof(Node));
tempPtr->nextPtr = NULL;
// Set the head pointer of first node
if (headPtr == NULL) {
headPtr = tempPtr;
currentPtr = tempPtr;
} // End if statement
// Link the list
currentPtr->nextPtr = tempPtr;
currentPtr = currentPtr->nextPtr;
// Create pointer variables
char *startPtr = inputLine;
char *endPtr = NULL;
endPtr = strchr(inputLine, ','); // Point to end of name
int length = endPtr - inputLine; // Calculate length
// Create space for the name
tempPtr->namePtr = (char *) malloc(sizeof(length + 1));
strncpy(tempPtr->namePtr, startPtr, length); // Store pointer to name
// cout << tempPtr->namePtr << endl;
}
} // End of for (i = 1880...
cout << "Done reading from data files...\n";
} // End of main function
Surely
tempPtr->namePtr = (char *) malloc(sizeof(length + 1));
should be
tempPtr->namePtr = (char *) malloc(length + 1);
since you copy that many characters to the string. sizeof (length + 1) would evaluate to four on a 32-bit machine (eight on 64-bit). Not enough memory was being allocated, so the strncpy which followed this was overwriting memory not belonging to you.
Rather than find your bug, let's try to teach you some practical lessons. Here are my C++ rules for C programmers:
1) Don't use pointers to track your data. The standard containers work just fine for that.
2) Don't use pointers to manage your strings. The standard string type works just fine for that.
3) Don't use pointers for anything else, until you need to learn how polymorphism works.
4) Don't use malloc at all. Ever.
5) Don't use new, hardly at all.
The neat thing about not using pointers (or arrays), is that you will never make pointer bugs. No more buffer overflows, no more segmentation faults. Joy!
Here is my translation of your program into idiomatic C++. Because I let std::list do all of the list management, all of the silly headPtr, nextPtr, etc, goes away. Because I let std::string do all of the string management, I don't need to malloc(strlen()) (or fix my bug and malloc(strlen()+1). Because I use the RAII idiom, I don't have to worry about closing my files.
It all just works.
#include <iostream>
#include <fstream>
#include <string>
#include <list>
#include <cstdlib>
#include <sstream>
using std::string;
using std::cout;
using std::list;
using std::ifstream;
using std::stringstream;
int main() {
// Declare variables
list<string> list;
cout << "Reading from data files, please be patient...\n";
// Loop through files
for (int i = 1880; i <= 2009; i++) {
stringstream fileName;
fileName << "data/yob" << i << ".txt";
ifstream filePtr(fileName.str().c_str());
if(!filePtr.is_open()) {
cout << "Error opening input file: " << fileName.str() << " ...check location of data files\n";
exit(-1); // Exit program
}
string inputLine;
while (filePtr >> inputLine) {
list.push_back(inputLine);
}
} // End of for (i = 1880...
cout << "Done reading from data files...\n";
} // End of main function
I know this one is already answered, but you should also be in the habit of closing your files. For you with something like fclose(filePtr). By the way if you learn how to use std::ifstream then you don't have to close, it auto closes when the std::ifstream goes out of scope.

C++ - Convert FILE* to CHAR*

I found a C++ source file which calculates expressions from a command line argument (argv[1]), however I now want to change it to read a file.
double Utvardering(char* s) {
srcPos = s;
searchToken();
return PlusMinus();
}
int main(int argc, char* argv[]) {
if (argc > 1) {
FILE* fFile = fopen(argv[1], "r");
double Value = Utvardering(fopen(argv[1], "r"));
cout << Value << endl;
}else{
cout << "Usage: " << argv[0] << " FILE" << endl;
}
cin.get();
return 0;
}
However the Utvardering function requires a char* parameter. How can I convert the data read from a file, fopen to a char*?
The function fopen just opens a file. To get a string from there, you need to read the file. There are different ways to to this. If you know the max size of your string in advance, this would do:
const int MAX_SIZE = 1024;
char buf[MAX_SIZE];
if (!fgets(buf, MAX_SIZE, fFile) {
cerr << "Read error";
exit(1);
}
double Value = Utvardering(buf);
Note: this method is typical for C, not for C++. If you want more idiomatic C++ code, you can use something like this (instead of FILE and fopen):
ifstream in;
in.open(argv[1]);
if (!in) { /* report an error */ }
string str;
in >> str;
Use the fread() function to read data from the FILE* into a buffer. Send that buffer into Utvardering().
I have no idea what "Utvardering" expects, or how it's using the information.
There are two possibilities -
1) Utvardering may be defined using char*, but expecting a FILE* (in effect, treating char* like void*). I've seen this before, even though it's pretty awful practice. In that case, just cast fFile to char* and pass it in.
2) Utvardering may be expecting a null terminated string (char*) as input. If you're using fopen like this, you can use fread to read the file contents into a buffer (char[]), and pass it to your function that takes a char*.
It looks like you need to write code to read the file into a character array and pass that to Utvardering.
Just passing the return value of fopen will cause the address of the opaque data structure pointed to by that pointer to be passed to Utvardering. Utvardering will happily treat those bytes as character data when they are not. Not good.
Good example of reading data from a file here:
http://www.cplusplus.com/reference/clibrary/cstdio/fread/
then pass the buffer to your function