I have a program to read a file (given as a parameter in argv) line by line and output it to a different file. Here is the code;
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define CAT_PREFIX_LEN 4
#define CAT_PREFIX "bsh_"
int main(int argc, char** argv) {
FILE *toread, *towrite;
char *line;
size_t len = 0;
ssize_t read;
char catfile[CAT_PREFIX_LEN + strlen(argv[1]) + 1];
// Opening file to read
toread = fopen(argv[1], "r");
// Create output file name
strcpy(catfile, CAT_PREFIX);
strcat(catfile, argv[1]);
// Opening file to write
towrite = fopen(catfile, "w");
while((read = getline(&line, &len, toread)) != -1) {
fprintf(towrite, line);
}
fclose(toread);
fclose(towrite);
}
However if I try to run this program with a test file test.txt I get the error:
convert_script(21115,0x7fffcb3f43c0) malloc: *** error for object 0x10caca4c0: pointer being realloc'd was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6
I've tried multiple things; the first thing I tried was to comment out the whole for loop. If I do comment out the whole for loop, the error goes away. I also tried removing the opening, closing and writing to test.txt. If I remove it, the error also goes away. I don't really know what could be causing it. I am running it on a MacBook Air and compiling it using gcc to compile it.
char *line;
is not initialized and contains an indeterminate value.
The Linux getline() man page says
If *lineptr is set to NULL and *n is set 0 before the call, then
getline() will allocate a buffer for storing the line. This buffer
should be freed by the user program even if getline() failed.
Since you're on a Mac, the POSIX getline() standard adds this:
The application shall ensure that *lineptr is a valid argument that could be passed to the free() function. If *n is non-zero,
the application shall ensure that *lineptr either points to an
object of size at least *n bytes, or is a null pointer.
For strict compliance with the POSIX standard, you should initialize line to NULL:
char *line = NULL;
Edit: The official Apple getline() man page states:
The caller may provide a pointer to a malloced buffer for the line in
*linep, and the capacity of that
buffer in *linecapp. These functions expand the buffer as needed, as if via realloc(). If linep
points to a NULL pointer, a new buffer will be allocated. In either case, *linep and *linecapp will be
updated accordingly.
Related
I'm getting a segmentation fault when running the code below.
It should basically read a .csv file with over 3M lines and do other stuff afterwards (not relevant to the problem), but after 207746 iterations it returns a segmentation fault. If I remove the p = strsep(&line,"|"); and just print the whole line it will print the >3M lines.
int ReadCSV (int argc, char *argv[]){
char *line = NULL, *p;
unsigned long count = 0;
FILE *data;
if (argc < 2) return 1;
if((data = fopen(argv[1], "r")) == NULL){
printf("the CSV file cannot be open");
exit(0);
}
while (getline(&line, &len, data)>0) {
p = strsep(&line,"|");
printf("Line number: %lu \t p: %s\n", count, p);
count++;
}
free(line);
fclose(data);
return 0;
}
I guess it'd have to do with the memory allocation, but can't figure out how to fix it.
A combination of getline and strsep often causes confusion, because both functions change the pointer that you pass them by pointer as the initial argument. If you pass the pointer that has been through strsep to getline again, you run the risk of undefined behavior on the second iteration.
Consider an example: getline allocates 101 bytes to line, and reads a 100-character string into it. Note that len is now set to 101. You call strsep, which finds '|' in the middle of the string, so it points line to what used to be line+50. Now you call getline again. It sees another 100-character line, and concludes that it is OK to copy it into the buffer, because len is still 101. However, since line points to the middle of the buffer now, writing 100 characters becomes undefined behavior.
Make a copy of line before calling strsep:
while (getline(&line, &len, data)>0) {
char *copy = line;
p = strsep(©, "|");
printf("Line number: %lu \t p: %s\n", count, p);
count++;
}
Now line that you pass to getline is preserved between loop iterations.
Look at the expression getline(&line, &len, data) and read the manpage:
If *line is set to NULL and *len is set 0 before the call, then
getline() will allocate a buffer for storing the line. This buffer
should be freed by the user program even if getline() failed.
This should be the case on your first time round the loop (although we can't see where len is declared, let's just assume your real code does this correctly)
Alternatively, before calling getline(), *line can contain a
pointer to a malloc(3)-allocated buffer *len bytes in size. If the
buffer is not large enough to hold the line, getline() resizes it
with realloc(3), updating *line and *len as necessary.
OK, so if line != NULL it must point to a buffer allocated by malloc of size len. The buffer allocated by your first call to getline (as above) satisfies this.
Note it's not good enough for line to point somewhere into that buffer, it must be the beginning.
Now look at the expression strsep(&line,"|") and read the manpage for that:
... This token is terminated by overwriting the delimiter with a
null byte ('\0'), and *line is updated to point past the token
So, the first argument (line) is changed so that you can call strsep again with the same first argument, and get the next token. This means line is no longer a valid argument to getline, because it isn't the start of a malloc'd buffer (and the length len is also now wrong).
In practice, either
getline will try to read len bytes into the buffer you gave it, but since you advanced line by the length of the first token, it writes off the end of your allocated block. This might just damage the heap rather than dying immediately
getline will try to realloc the buffer you gave it, but since it isn't a valid allocated block, you get heap damage again.
While we're here, you also don't check p is non-NULL, but damaging line is the main problem.
Oh, and if you think the problem is allocation-related, try using valgrind - it generally finds the moment things first go wrong.
The file I'm reading from just has names separated by a line. What happens is the program tries to print the contents of line_array, and it will print out about 20 of the last line in the txt file.
#include <stdio.h>
FILE* fp;
int main(){
char* line;
const char* line_array[255];
int i= 0;
int b =0;
fp = fopen("noob.txt","r");
while(fgets(line,255,fp)){
line_array[i]=line;
printf("%s",line);
printf("%s",line_array[i]);
i++;
}
for(;b<i;b++){
printf("%s",line_array[b]);
}
fclose(fp);
return 0;
}
The first issue, in your code,
while(fgets(line,255,fp))
line is used uninitialized. There is no memory allocated to line. It invokes undefined behavior.
Then, you did not check for the success of fopen() before using the returned file pointer. Again, possible UB.
And finally, by saying
line_array[i]=line;
what you did is to store the line itself to all the occurrences of line_array[n], so for the later printf() loop, the latest content of line is being printed over and over again.
Solution(s):
Allocate memory to line or use a fixed length-array.
Check for the success of fopen()before using the returned pointer.
Allocate memory to each line_array[n] and use strcpy() to copy the content. Ottherwise, you can directly use strdup(), too.
I'm reading a file's contents into a string with this function
void readFile2String(char **string, char location[]){
FILE *fileList;
int size;
char *temp;
char command[1024];
if((fileList=fopen(location,"r")) == NULL){
perror(" Error opening list of directories: ");
exit(2);
}
fseek(fileList,0,SEEK_END);
size = ftell(fileList);
rewind(fileList);
temp = malloc((size+1)*(sizeof(char)));
fread(temp,sizeof(char),size,fileList);
temp[size]=0; // Terminate string with 0
*string = temp;
fclose(fileList);
}
and I'm using the string for further manipulation. I'm calling this function as
char *temp;
readFile2String(&temp, fileName);
I successfully get the file contents in the string. But when at a later point I try to use fopen again, I get malloc error at runtime. I've tried commenting out the call to this function in this program and after that, I have been able to use fopen as many times as I want. What is wrong with my function?
Thanks.
"fopen()" isn't causing the memory corruption' Neither is failing to "free()" (if in fact you're not doing a free().
You're validating the return from "fopen()" - good. Q: Why aren't you also checking fseek(), ftell(), rewind() and fread() for error conditions?
My guess is temp[size]=0; might be the culprit that actually causes the memory corruption. Or perhaps fread(). Knowing "size" would definitely be useful.
SUGGESTION:
Carefully walk through the debugger, and validate your I/O returns each step of the way
I'm getting a seg fault when I try and print fname. Can someone explain why this is happening to me? Is it that I'm not allowed to write to a file, close it, and then read from a file? argv[2] is defined. I've tried with multiple different allocations.
int main(int argc, char *argv[]){
//Other stuff
char *rfile=calloc(1, 2049);
strcpy(rfile, argv[2]);
FILE *wfile;
wfile = fopen(argv[2], "w");
SaveIndexToFile(index, wfile, alpha); // This does a whole bunch of writing to a the file.
fclose(wfile);
//alpha is declared here, and works just fine.
RemakeIndex(rfile, alpha);
return 1;
}
int RemakeIndex(char *fname, WordList * alpha){
printf("%s", fname);
return 1;
}
You are not checking the return value of fopen. If the fopen fails it can
return NULL. If you are doing something with NULL that can undefined behavior. Place this line after opening the file.
if ( wfile == NULL ){
perror("fopen");
return;
}
And check whether the argc count is three. If you are not giving arguments to the ./a.out then accessing the argv[2] can also lead to segmentation fault.
Is it that I'm not allowed to write to a file, close it, and then read from a file?
Yes, you are not allowed to read from a file [stream] after it had been closed.
Note on the (OP's) wording:
char * rfile is called a "pointer to char".
FILE * is called a "file-pointer" (or also just "pointer to FILE) or commonly (but formally wrong) just "file".
Also RemakeIndex() is called in main() without proper protoyping.
To fix this
either add a prototype before main():
int RemakeIndex(char *, WordList *);
or move the whole implementation of RemakeIndex() before main().
Also the printf() calls' output might not show up immediately on the console, as stdout is line buffered.
To fix this
either print out a trailing new-line:
printf("%s\n", fname);
or printf to stderr, which itself isn't line bufferd by default:
fprintf(strerr, "%s\n", fname);
or flush stdout after having printed to it:
printf("%s\n", fname);
fflush(stdout);
Prototyping the function is very important, the GCC compiler will assume an implicitly declared function (RemakeIndex in your code) has two arguments which are both int, which would make your code look like this:
int RemakeIndex(int fname, int alpha) {
printf("%s", (char *)fname);
return 1;
}
On a 64 bit machine and with GCC where pointers are 64 bits and ints are 32 bits then your arguments will be truncated to 32 bits which is likely to cause a segfault. The other answers have mentioned prototyping the function and if you are using a 64bit compiler I would suggest that this is your problem.
It ended up being that I was allocating way too much memory on the heap. I had a loop that was allocating strings of max_length of an unsigned int. Thank you for all of your comments and help!
Switching to C from Java, and I'm having some troubles grasping memory management
Say I have a function *check_malloc that behaves as such:
// Checks if malloc() succeeds.
void *check_malloc(size_t amount){
void *tpt;
/* Allocates a memory block in amount bytes. */
tpt = malloc( amount );
/* Checks if it was successful. */
if ( tpt == NULL ){
fprintf(stderr, "No memory of %lu bytes\n", amount);
exit(1);
}
return tpt;
}
I also have the following variables to work with:
FILE *f = fopen("abc.txt", "r"); // Pointer to a file with "mynameisbob" on the first line and
// "123456789" on the second line
char *pname; // Pointer to a string for storing the name
}
My goal is to use *check_malloc to dynamically allocate memory so that the String pointed to by *pname is just the correct size for storing "mynamisbob", which is the only thing on the first line of the text file.
Here is my (failed) attempt:
int main(int argc, char *argv[]){
FILE *f = fopen("abc.txt", "r"); // A file with "mynameisbob" on the first line and
// "123456789" on the second line
char *pname; // Pointer to a string for storing the name
char currentline[150]; // Char array for storing current line of file
while(!feof(f)){
fgets(currentline,100,f);
pname = ¤tline;
}
But I know this probably isn't the way to go about this, because I need to use my nice check_malloc* function.
Additionally, in my actual text file there is a "<" symbol before the name on the first line.But I just want the *pname to point to a String saying "mynameisbob" without the "<" symbol. This isn't that important now, it just is reinforcement to me that I know I can't just set the pointer to point straight to currentline.
Can anyone help me fix my thinking on this one? Thanks a lot.
In C you need to copy chars, not the "strings" (which are just pointers). Check out strcpy() and strlen(). Use strlen() to determine how long the line actually is which fgets has read, then use your malloc() to allocate exactly that (plus 1 for the 0). Then copy the chars over with strcpy().
There are several problems in your code, see my comments in this example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Checks if malloc() succeeds.
void *check_malloc (size_t amount) {
void *tpt;
/* Allocates a memory block in amount bytes. */
tpt = malloc( amount );
/* Checks if it was successful. */
if (tpt == NULL) {
fprintf (stderr, "No memory of %lu bytes\n", amount);
exit (EXIT_FAILURE);
}
return tpt;
}
// To avoid subtle errors I have defined buffer size here
#define BUFFER_SIZE 150
// I have used the (void) version of main () here, while not strictly neccessary, you where not using argc and argv anyway, so the can be left out in this case
int main (void) {
// It might be a good idea to make the filename a char[] as well, but I leave that as an exercise to the reader.
FILE *f = fopen("abc.txt", "r"); // A file with "mynameisbob" on the first line and
// "123456789" on the second line
// You have to check whether the file was *actually openend*
if (f == NULL) {
fprintf (stderr, "Could not open file abc.txt\n"); // '"...%s\n", filename);' might better.
exit (EXIT_FAILURE);
}
char *pname; // Pointer to a string for storing the name
char currentline[BUFFER_SIZE]; // Char array for storing current line of file
while(!feof (f)) {
char *res = fgets (currentline, BUFFER_SIZE, f);
// fgets returns NULL when EOF was encountered before the next '\n'
if (res) {
size_t read = strlen (res);
// The line might have been empty
if (read) {
// Better use "sizeof *varname", while char is always 1 byte it is a good practice
pname = check_malloc ((read + 1) * sizeof *pname); // + 1 because we have to provide an extra char für '\0'
strncpy (pname, currentline, read); // You have to use strcpy or strncpy to copy the contents of the string rather than just assigning the pointer
// What was allocated must be freed again
free (pname);
}
}
}
fclose(f); // Always close everything you open!
return EXIT_SUCCESS;
}
Actually you really don't have to use pname in this simple case, because currentline already contains the line, but since you're trying to learn about memory management this should give you a general idea of how things work.
In your code you had this line:
pname = ¤tline;
There are two problems here:
As already mentioned in my code assigning currentline to pname only copies the pointer not the contents.
The correct assignment would be pname = currentline (without the address operator &), because currentline is also a pointer under the hood (it behaves like char *currentline even though it's statically allocated).