FILE *file;
file = fopen(argv[1], "r");
char *match = argv[2];
if (file == NULL) {
printf("File does not exist\n");
return EXIT_FAILURE;
}
int numWords = 0, memLimit = 20;
char** words = (char**) calloc(memLimit, sizeof(char));
printf("Allocated initial array of 20 character pointers.\n");
char string[20];
while (fscanf(file, "%[a-zA-Z]%*[^a-zA-Z]", string) != EOF) {
words[numWords] = malloc(strlen(string) + 1 * sizeof(char));
strcpy(words[numWords], string);
printf("Words: %s\n", words[numWords]);
numWords++; /*keep track of indexes, to realloc*/
if (numWords == memLimit) {
memLimit = 2 * memLimit;
words = (char**) realloc(words, memLimit * sizeof(char*)); /*Fails here*/
printf("Reallocated array of %d character pointers.\n", memLimit);
}
}
Code should open and read a file containing words with punctuation, spaces etc and store in a string, but after 20 tries it throws an error, and I can't seem to get realloc() to work here, which I'm expecting to be the problem. The array is dynamically allocated 20 char pointers, at which when limit is reached, it should realloc by double. How can I get around this?
Two notes. First, you shouldn't ever cast the return value of calloc/malloc/realloc. See this for more information.
Second, as others have pointed out in comments, the first calloc statement uses sizeof(char) and not sizeof(char*) like it should.
words is a pointer to a pointer. The idea is to allocate an array of pointers.
The below is wrong as it allocates for memLimit characters rather than memLimit pointers.
This is the main issue
char** words = (char**) calloc(memLimit, sizeof(char)); // bad
So use an easy idiom: allocate memLimit groups of whatever words points to. It is easier to write, read and maintain.
char** words = calloc(memLimit, sizeof *words);
Avoid the while (scanf() != EOF) hole. Recall that various results can come from scanf() family. It returns the count of successfully scanned fields or EOF. That is typically 1 of at least 3 options. So do not test for one result you do not want, test for the one result you do want.
// while (fscanf(file, "%[a-zA-Z]%*[^a-zA-Z]", string) != EOF) {
while (fscanf(file, "%[a-zA-Z]%*[^a-zA-Z]", string) == 1) {
The above example may not every return 0, but the below easily could.
int d;
while (fscanf(file, "%d", &d) == 1) {
#Enzo Ferber rightly suggests using "%s". Further recommend to follow the above idiom and restrict input width to 1 less than the size of the buffer.
char string[20];
while (fscanf(file, "%19s", string) == 1) {
Suggest the habit of checking allocation result.
// better to use `size_t` rather than `int `for array sizes.
size_t newLimit = 2u * memLimit;
char** newptr = realloc(words, newLimit * sizeof *newptr);
if (newptr == NULL) {
puts("Out-of-memory");
// Code still can use old `words` pointer of size `memLimit * sizeof *words`
return -1;
}
memLimit = newLimit;
words = newptr;
}
Errors
Don't cast malloc/calloc returns. There's not need for it.
Your first sizeof is wrong. It should be sizeof(char*)
That scanf() format string. %s does the job just fine.
Code
The following code worked for me (printed one word per line):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
FILE *file;
file = fopen(argv[1], "r");
char *match = argv[2];
if (file == NULL) {
printf("File does not exist\n");
return EXIT_FAILURE;
}
int numWords = 0, memLimit = 20;
char **words = calloc(memLimit, sizeof(char*));
printf("Allocated initial array of 20 character pointers.\n");
char string[20];
while (fscanf(file, "%s", string) != EOF) {
words[numWords] =
malloc(strlen(string) + 1 * sizeof(char));
strcpy(words[numWords], string);
printf("Words: %s\n", words[numWords]);
numWords++; /*keep track of indexes, to realloc */
if (numWords == memLimit) {
memLimit = 2 * memLimit;
words = realloc(words, memLimit * sizeof(char *));
printf
("Reallocated array of %d character pointers.\n",
memLimit);
}
}
}
Called with ./realloc realloc.c
Hope it helps.
Your first allocation is the problem. You allocate 20 chars and treat them as 20 char pointers. You overrun the allocated buffer and corrupt your memory.
The second allocation fails because the heap is corrupted.
Related
I am trying to read lines until EOF. If the line is longer than maxLength, the rest gets truncated.
If the number of rows exceed S, it should reallocate for double the amount.
Everything works well until the reallocation. When it gets to that part, the program crashes.
I have been sitting on this for 2 days.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define S 8
char ** readLines(int * wordCountp, int maxLength) {
char line[maxLength + 1];
int size = S;
char ** words = malloc(size * sizeof(char));
if (words == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(1);
}
int i = 0;
while (fscanf(stdin, "%s", line) == 1) {
words[i] = malloc((maxLength + 1) * sizeof(char));
strncpy(words[i], line, maxLength);
i++;
if (i == size) {
size *= 2;
words = realloc(words, size * sizeof(char));
if (words == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(1);
}
}
if (words == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(1);
}
free(words[i]);
}
words[i] = NULL;
*wordCountp = i;
return words;
}
Allocating with sizeof(char*) instead of sizeof(char) messes up the array, but still doesn't solve the problem.
Creating a separate char**, reallocating that one and then copying the original into that does not work either.
At least these problems:
Avoid allocation size mistakes
Allocate to the size of the referenced object, rather than the size of the type, which was wrong here.
// v----------v Wrong type and too small
// char ** words = malloc(size * sizeof(char));
char ** words = malloc(sizeof words[0] * size);
// ^-------------^ Just right
// words = realloc(words, size * sizeof(char));
words = realloc(words, sizeof words[0] * size);
Copy the whole string
words[i] = malloc((maxLength + 1) * sizeof(char));
// v-------v 1 too short
// strncpy(words[i], line, maxLength);
strcpy(words[i], line);
Use fgets() to read a line
fscanf(stdin, "%s", line) will not read a line, more like it will read a word. Without a width, buffer overflow possible.
// fscanf(stdin, "%s", line)
while (fgets(line, sizeof line, stdin)) {
I am trying to learn C pointer passing. So please forgive my ignorance.
I want to allocate a 2 dimensional dynamically allocated string array in a function.
The function signature is void so the parameters are by reference.
The test file contains these two lines.
I am testing.
This is not an empty file.
Here is what I have done so far.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void read_lines(FILE *fp, char** lines, int *num_lines) {
ssize_t read;
char * line = NULL;
size_t len = 0;
*num_lines = 0;
while ((read = getline(&line, &len, fp)) != -1) {
if (*num_lines == 0) {
// For the first time it holds only one char pointer
*lines = malloc(sizeof(char *));
} else {
// Every time a line is read, space for next pointer is allocated
*lines = realloc(*lines, (*num_lines) * sizeof(char *));
}
// allocate space where the current line can be stored
*(lines + (*num_lines)) = malloc(len * sizeof(char));
// Copy data
strcpy(*(lines + (*num_lines)), line);
printf("Retrieved line of length %zu:\n", read);
printf("%s\n", line);
(*num_lines)++;
// After first line subsequent lines get truncated if I free
// the storage here, then subsequent lines are not read completely
//if (line) {
// free(line);
//}
}
if (line) {
free(line);
}
}
int main(void)
{
FILE * fp;
char *array;
int num_lines;
fp = fopen("file.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
read_lines(fp, &array, &num_lines);
printf("After returning\n");
// Intend to access as array[0], array[1] etc
// That's not working
// If I access this way then I get seg violation after first line
printf("%s\n", &array[0]);
fclose(fp);
}
My questions are inline with code:
Why can't I free storage for line inside the while loop?
How do I access returned 2D array in main? array[0] array[1] doesn't seem to work? I want to do something similar.
Why seg fault is generated for the way I am doing it now?
Corrected code will help me understand. Also any good reference anybody can provide to get these concept clarified for C will be greatly appreciated.
If you free(line) inside the while loop, you have to reset line to NULL and len to 0, before the next calling of getline. Otherwise, getline will think line is a valid buffer of size len, and may try to write to it, which is actually a so called "dangling pointer" now.
In the realloc line, the size should be (*num_lines + 1) * sizeof(char *), one more element need to be allocate to hold the just read line.
And the array variable is char*, its address is taken and assiged to the parameter lines of read_lines. So lines is the address of array, and *lines is just array itself.
But
// allocate `char*[1]`
*lines = malloc(sizeof(char *));
and
// allocate `char*[N]` with N=`*num_lines`
*lines = realloc(*lines, (*num_lines) * sizeof(char *));
You assigned a char*[] to array, which is a char* in fact.
So, if you want your function return a array of strings (that is char*[] or char**) by parameter, you have to make the parameter a pointer to a array of strings (that is char***).
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
void read_lines(FILE * fp, char*** lines, int* num_lines) {
ssize_t read;
char* buffer = NULL;
size_t buffer_len = 0;
*num_lines = 0;
while ((read = getline(&buffer, &buffer_len, fp)) != -1) {
// `*lines` is actually `array`,
// modify `*lines` will effectively modify `array`
if (*num_lines == 0) {
// `array` now is `char*[1]`
*lines = (char**)malloc(sizeof(char*)); // A
}
else {
// `array` now is `char*[(*num_lines) + 1]`
*lines = (char**)realloc(*lines, (*num_lines + 1) * sizeof(char*)); // B
}
// *(x+n) is the same as x[n], this line is actually doing:
// `array[*num_lines] = malloc...
*(*lines + (*num_lines)) = (char*)malloc((read + 1) * sizeof(char)); // C
strcpy(*(*lines + (*num_lines)), buffer);
(*num_lines)++;
printf("Retrieved line of length %zu:\n", read);
printf("%s\n", buffer);
}
if (buffer) {
// `line` is `malloc`ed or `realloc`ed by `getline`,
// have to be `free`ed
free(buffer);
}
}
int main(void)
{
FILE* fp;
char** array;
int num_lines;
fp = fopen("file.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
read_lines(fp, &array, &num_lines);
printf("After returning\n");
for (int i = 0; i < *num_lines; i++) {
printf("%s\n", array[i]);
free(array[i]); // corresponding to C
}
free(array); // corresponding to A or B
fclose(fp);
}
I have a pointer of pointer to store lines I read from a file;
char **lines;
And I'm assigning them like this :
line_no=0;
*(&lines[line_no++])=buffer;
But it crashes why ?
According to my logic the & should give the pointer of zeroth index, then *var=value, that's how to store value in pointer. Isn't it ?
Here is my current complete code :
void read_file(char const *name,int len)
{
int line_no=0;
FILE* file;
int buffer_length = 1024;
char buffer[buffer_length];
file = fopen(name, "r");
while(fgets(buffer, buffer_length, file)) {
printf("---%s", buffer);
++line_no;
if(line_no==0)
{
lines = (char**)malloc(sizeof(*lines) * line_no);
}
else
{
lines = (char**)realloc(lines,sizeof(*lines) * line_no);
}
lines[line_no-1] = (char*)malloc(sizeof(buffer));
lines[line_no-1]=buffer;
printf("-------%s--------\n", *lines[line_no-1]);
}
fclose(file);
}
You have just a pointer, nothing more. You need to allocate memory using malloc().
Actually, you need first to allocate memory for pointers, then allocate memory for strings.
N lines, each M characters long:
char** lines = malloc(sizeof(*lines) * N);
for (int i = 0; i < N; ++i) {
lines[i] = malloc(sizeof(*(lines[i])) * M);
}
You are also taking an address and then immediately dereference it - something like*(&foo) makes little to no sense.
For updated code
Oh, there is so much wrong with that code...
You need to include stdlib.h to use malloc()
lines is undeclared. The char** lines is missing before loop
if in loop checks whether line_no is 0. If it is, then it allocates lines. The problem is, variable line_no is 0 - sizeof(*lines) times 0 is still zero. It allocates no memory.
But! There is ++line_no at the beginning of the loop, therefore line_no is never 0, so malloc() isn't called at all.
lines[line_no-1] = buffer; - it doesn't copy from buffer to lines[line_no-1], it just assigns pointers. To copy strings in C you need to use strcpy()
fgets() adds new line character at the end of buffer - you probably want to remove it: buffer[strcspn(buffer, "\n")] = '\0';
Argument len is never used.
char buffer[buffer_length]; - don't use VLA
It would be better to increment line_no at the end of the loop instead of constantly calculating line_no-1
In C, casting result of malloc() isn't mandatory
There is no check, if opening file failed
You aren't freeing the memory
Considering all of this, I quickly "corrected" it to such state:
void read_file(char const* name)
{
FILE* file = fopen(name, "r");
if (file == NULL) {
return;
}
int buffer_length = 1024;
char buffer[1024];
char** lines = malloc(0);
int line_no = 0;
while (fgets(buffer, buffer_length, file)) {
buffer[strcspn(buffer, "\n")] = '\0';
printf("---%s\n", buffer);
lines = realloc(lines, sizeof (*lines) * (line_no+1));
lines[line_no] = malloc(sizeof (*lines[line_no]) * buffer_length);
strcpy(lines[line_no], buffer);
printf("-------%s--------\n", lines[line_no]);
++line_no;
}
fclose(file);
for (int i = 0; i < line_no; ++i) {
free(lines[i]);
}
free(lines);
}
Ok, you have a couple of errors here:
lines array is not declared
Your allocation is wrong
I don't understand this line, it is pointless to allocate something multiplying it by zero
if( line_no == 0 )
{
lines = (char**)malloc(sizeof(*lines) * line_no);
}
You shouldn't allocate array with just one element and constantly reallocate it. It is a bad practice, time-consuming, and can lead to some bigger problems later.
I recommend you to check this Do I cast the result of malloc? for malloc casting.
You could write something like this:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void read_file(char const *name)
{
int line_no = 0, arr_size = 10;
int buffer_length = 1024;
char buffer[buffer_length];
char **lines;
FILE* file;
lines = malloc(sizeof(char*) * 10);
file = fopen(name, "r");
while(fgets(buffer, buffer_length, file)) {
buffer[strlen(buffer)-1] = '\0';
printf("---%s", buffer);
++line_no;
if(line_no == arr_size)
{
arr_size += 10;
lines = realloc(lines, sizeof(char*) * arr_size);
}
lines[line_no-1] = malloc(sizeof(buffer));
lines[line_no-1] = buffer;
printf("-------%s--------\n", lines[line_no-1]);
}
fclose(file);
}
PS, fgets() also takes the '\n' char at the end, in order to prevent this you can write the following line: buffer[strlen(buffer)-1] = '\0';
I have this code:
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
char **string = malloc(sizeof(char) * 20);
FILE *fp = fopen("input.txt", "r");
fscanf(fp, "%s", *string);
printf("%s\n", *string);
}
This code generates a segmentation fault. However, if I change **string to be a single character pointer and change the *strings to string it works. Why is this? And how can I use fscanf with arrays of pointers?
Thanks.
char **string = malloc(sizeof(char*)); // Pointer to pointer --> Alloc size of a POINTER
*string = malloc(sizeof(char) * 20); // Dereference and then you can malloc chars
When you allocate a pointer to a pointer, you allocate the size of the pointer first. You then dereference the variable and allocate the size of the contents of the pointer, in this case, the number of characters that it points to.
Also, your usage of fscanf is not only unsafe, but totally unneccessary as well.
Use fgets instead:
fgets( *string, 20, fp );
If you want to allocate an array of pointers to chars, then multiply the sizeof char* by the number entries when allocating the pointer-to-pointer. You must also use a for loop to allocate memory for each character pointer as shown above.
// Example code
char **string = malloc(sizeof(char*) * 10); // Allocates an array of 10 character pointers
if (string == 0) {
fprintf(stderr, "Memory allocation failed.");
exit(1);
}
int i = 0;
FILE *fp = fopen("input.txt", "r");
if (fp == 0) {
fprintf(stderr, "Couldn't open input.txt for reading.");
exit(1);
}
for (; i < 10; ++i) {
string[i] = malloc(sizeof(char) * 20); // For each char pointer, allocates enough memory for 20 characters
if (string[i] == 0) {
fprintf(stderr, "Memory allocation failed.");
exit(1);
}
fgets(string[i], 20, fp);
printf("%s\n", string[i]);
}
Use simple char * pointer instead of double pointer:
char *string = malloc(sizeof(char) * 20);
FILE *fp = fopen("input.txt", "r");
fscanf(fp, "%s", string);
printf("%s\n", string);
Double pointer is pointer to a string (or array of strings), and the first pointer is not initialized anywhere in the original code. Additionally, the first malloc would have to look like malloc(sizeof(char *)*20) - that would give array of 20 strings (which would then need to be properly initialized in the loop)...
Also, not specifying the maximum size of the string is prone to buffer overflow errors, so worth looking at the limits, return values and things like that too.
This solution is for an array of strings, it's also possible to do a realloc for each time we want to add another string to the array.
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
char **string = (char**)malloc(sizeof(char*) * 2); // array with 2 strings
string[0] = (char*)malloc(sizeof(char)*20);
FILE *fp = fopen("input.txt", "r");
fscanf(fp, "%s", string[0]);
fclose(fp); // Remember to close after you don't need file handle anymore
printf("%s\n", string[1]);
string[1] = (char*)malloc(sizeof(char)*20);
FILE *fp2 = fopen("input2.txt", "r");
fscanf(fp2, "%s", string[1]);
fclose(fp2);
printf("%s\n", string[1]);
return 0;
}
I need remove punctuation from a given string or a word. Here's my code:
void remove_punc(char* *str)
{
char* ps = *str;
char* nstr;
// should be nstr = malloc(sizeof(char) * (1 + strlen(*str)))
nstr = (char *)malloc(sizeof(char) * strlen(*str));
if (nstr == NULL) {
perror("Memory Error in remove_punc function");
exit(1);
}
// should be memset(nstr, 0, sizeof(char) * (1 + strlen(*str)))
memset(nstr, 0, sizeof(char) * strlen(*str));
while(*ps) {
if(! ispunct(*ps)) {
strncat(nstr, ps, 1);
}
++ps;
}
*str = strdup(nstr);
free(nstr);
}
If my main function is the simple one:
int main(void) {
char* str = "Hello, World!:)";
remove_punc(&str);
printf("%s\n", str);
return 0;
}
It works! The output is Hello World.
Now I want to read in a big file and remove punctuation from the file, then output to another file.
Here's another main function:
int main(void) {
FILE* fp = fopen("book.txt", "r");
FILE* fout = fopen("newbook.txt", "w");
char* str = (char *)malloc(sizeof(char) * 1024);
if (str == NULL) {
perror("Error -- allocating memory");
exit(1);
}
memset(str, 0, sizeof(char) * 1024);
while(1) {
if (fscanf(fp, "%s", str) != 1)
break;
remove_punc(&str);
fprintf(fout, "%s ", str);
}
return 0;
}
When I rerun the program in Visual C++, it reports a
Debug Error! DAMAGE: after Normal Block(#54)0x00550B08,
and the program is aborted.
So, I have to debug the code. Everything works until the statement free(nstr) being executed.
I get confused. Anyone can help me?
You forgot to malloc space for the null terminator. Change
nstr = (char *)malloc(sizeof(char) * strlen(*str));
to
nstr = malloc( strlen(*str) + 1 );
Note that casting malloc is a bad idea, and if you are going to malloc and then memset to zero, you could use calloc instead which does just that.
There is another bug later in your program. The remove_punc function changes str to point to a freshly-allocated buffer that is just big enough for the string with no punctuation. However you then loop up to fscanf(fp, "%s", str). This is no longer reading into a 1024-byte buffer, it is reading into just the buffer size of the previous punctuation-free string.
So unless your file contains lines all in descending order of length (after punctuation removal), you will cause a buffer overflow here. You'll need to rethink your design of this loop. For example perhaps you could have remove_punc leave the input unchanged, and return a pointer to the freshly-allocated string, which you would free after printing.
If you go with this solution, then use %1023s to avoid a buffer overflow with fscanf (unfortunately there's no simple way to take a variable here instead of hardcoding the length). Using a scanf function with a bare "%s" is just as dangerous as gets.
The answer by #MatMcNabb explains the causes of your problems. I'm going to suggest couple of ways you can simplify your code, and make it less susceptible to memory problems.
If performance is not an issue, read the file character by character and discard the puncuation characters.
int main(void)
{
FILE* fp = fopen("book.txt", "r");
FILE* fout = fopen("newbook.txt", "w");
char c;
while ( (c = fgetc(fp)) != EOF )
{
if ( !ispunct(c) )
{
fputc(c, fout);
}
}
fclose(fout);
fclose(fp);
return 0;
}
Minimize the number of calls to malloc and free by passing in the input string as well as the output string to remove_punc.
void remove_punc(char* inStr, char* outStr)
{
char* ps = inStr;
int index = 0;
while(*ps)
{
if(! ispunct(*ps))
{
outStr[index++] = *ps;
}
++ps;
}
outStr[index] = '\0';
}
and change the way you use remove_punc in main.
int main(void)
{
FILE* fp = fopen("book.txt", "r");
FILE* fout = fopen("newbook.txt", "w");
char inStr[1024];
char outStr[1024];
while (fgets(inStr, 1024, fp) != NULL )
{
remove_punc(inStr, outStr);
fprintf(fout, "%s", outStr);
}
fclose(fout);
fclose(fp);
return 0;
}
In your main you have the following
char* str = (char *)malloc(sizeof(char) * 1024);
...
remove_punc(&str);
...
Your remove_punc() function takes the address of str but when you do this in your remove_punc function
...
*str = strdup(nstr);
...
you are not copying the new string to the previously allocated buffer, you are reassigning str to point to the new line sized buffer! This means that when you read lines from the file and the next line to be read is longer than the previous line you will run into trouble.
You should leave the original buffer alone and instead e.g. return the new allocate buffer containing the new string e.g. return nstr and then free that when done with it or better yet just copy the original file byte by byte to the new file and exclude any punctuation. That would be far more effective