Load Text File Into 2D Array Then Compare Against Literal - c

I have 2D array I want populate then compare to literal
below is compare code, i try different things with no success
char** list;
load(list);
if(strcmp(list[0], "aasdf"))
{
printf("win\n");
}
the above segfaults on strcmp
load function
void load(char **list)
{
int MAX_NUM_LINES = 1000;
FILE *fp;
list = malloc(MAX_NUM_LINES*sizeof(char*));
fp = fopen("list", "r");
line_ct = 0;
char line[256];
while ( fgets(line, 256, fp) != NULL )
{
int len = strlen(line);
list[line_ct] = malloc(len * sizeof(char));
strcpy(list[line_ct], line);
line_ct++;
if(line_ct == MAX_NUM_LINES)
{
break;
}
}
fclose(fp);
}
any ideas on why is segfault?
also i try before strcmp
printf("Line: %s\n", *list[0]);
it segfault to

when you come back from load the var list is not set, so when you do
if(strcmp(list[0], "aasdf"))
you have an undefined behavior using list (typically a crash)
The first solution is use an output var
you need to change
load(list);
by
load(&list);
and you need to change the type of list and dereference it in load, so :
void load(char ***list)
{
*list = malloc(MAX_NUM_LINES*sizeof(char*));
...
(*list)[line_ct] = malloc((len + 1) * sizeof(char));
strcpy((*list)[line_ct], line);
I also added 1 to len to have place for the ending null character.
(edit) The use of a *** is not very common, as suggested by #user3629249 in a remark you can look at Triple pointers in C: is it a matter of style? reading carefully the answers.
The second solution is to return the allocated array :
char** list = load();
with
char ** load()
{
char **list;
...
return list;
also adding 1 to len when you allocate each line
Out of that if you read more than MAX_NUM_LINES lines you write out of the array again with an undefined behavior, and the caller of load does not know how much lines you read.
To avoid that you can first initialize list with malloc(0) then use realloc to increase the size of list each time you read a line, that allows to allocate the right size. To indicate the size to the caller you can use an additional output var or to allocate one entry more to place NULL in the last entry (all depends on how you use the read array in the code calling load)

Related

What is the difference between char *var= NULL; and char var[LENGTH + 1];?

I am creating a function to load a Hash Table and I'm getting a segmentation fault if my code looks like this
bool load(const char *dictionary)
{
// initialize vars
char *line = NULL;
size_t len = 0;
unsigned int hashed;
//open file and check it
FILE *fp = fopen(dictionary, "r");
if (fp == NULL)
{
return false;
}
while (fscanf(fp, "%s", line) != EOF)
{
//create node
node *data = malloc(sizeof(node));
//clear memory if things go south
if (data == NULL)
{
fclose(fp);
unload();
return false;
}
//put data in node
//data->word = *line;
strcpy(data->word, line);
hashed = hash(line);
hashed = hashed % N;
data->next = table[hashed];
table[hashed] = data;
dictionary_size++;
}
fclose(fp);
return true;
}
However If I replace
char *line = NULL; by char line[LENGTH + 1]; (where length is 45)
It works. What is going on aren't they "equivalent"?
When you do fscanf(fp, "%s", line) it'll try to read data into the memory pointed to by line - but char *line = NULL; does not allocate any memory.
When you do char line[LENGTH + 1]; you allocate an array of LENGTH + 1 chars.
Note that if a word in the file is longer than LENGTH your program will write out of bounds. Always use bounds checking operations.
Example:
while (fscanf(fp, "%*s", LENGTH, line) != EOF)
They are not equivalent.
In the first case char *line = NULL; you have a pointer-to-char which is initialised to NULL. When you call fscanf() it tries to write data to it and this will cause it to dereference the NULL pointer. Hence segfault.
One option to fix that would have been to allocate (malloc() and friends) the required memory first, check the pointer is not NULL (allocation failed) before using it. Then you would need to free() the resources once you no longer need the data.
In the second case char line[LENGTH +1] you have an array-of-char of size LENGTH + 1. This memory has been allocated for you on the stack (the compiler ensures this happens automatically for arrays), and the memory is only 'valid' for use during the lifetime of the function: once you return you must no longer use it. Now, when you pass the pointer to fscanf() (to the first element of the array in this case), fscanf() has a memory buffer to write to. As long as the buffer is large enough to hold the data being written this works correctly.
char *line = NULL;
Says "I want a variable named 'line' that can point to characters, but is not currently pointing to anything." The compiler will allocate memory that can hold a memory address, and will fill it with zero (or some other internal representation of "points to nothing").
char line[10];
Says "allocate memory for 10 characters, and I would like to use the name 'line' for the address of the first one". It does not allocate space to hold the memory address, because that's a constant, but it does allocate space for the characters (and does not initialize them).
Declaring a pointer as NULL doesn't allocate memory for the array. When you access the pointer, then what gets executed is reading / writing to a null pointer, which is not what you want. How fscanf works is it writes out to the buffer you sent, hence meaning that the buffer must be allocated before hand. If you want to use a pointer, then you ought to do:
char* line = malloc(LEN + 1);
When declaring as an array, then the compiler allocates memory for it, not you. This is better, in case you forget to free the memory, which the compiler won't do. Note that if you do use an array (which is a local variable in this case), it cannot be used by functions higher up on the call stack, because as I stated above, the memory gets freed upon return from the function.

Tokenizing string from dynamic array into multiple lines in static 2D char array

I have a dynamic array that holds a string containing '\n' characters, so this string is made up of multiple lines. I'm trying to extract the lines and put them all into a 2D char array and I'm getting segmentation errors.
Here's my code:
char *input_lines = malloc(MAX_LINE_LEN*sizeof(char));
input_lines = extractInput(MAX_LINE_LEN, input_file);
char inputLines_counted[lineCount_input][MAX_LINE_LEN];
char *t = strtok(input_lines, "\n");
for(i = 0; i < lineCount_input; i++) {
strcpy(inputLines_counted[i], t);
// printf("%s\n", inputLines_counted[i]);
t = strtok(NULL, "\n");
}
Upon creating the dynamic array, I use the extractInput(MAX_LINE_LEN, input_file) function to populate the input_lines array with a string containing multiple lines.
Here's the extract function:
char *extractInput(int len, FILE *file) {
char tmp[len];
char *pos;
char *input_lines = malloc(len*sizeof(char));
char *lines;
while(fgets(tmp, len, file)) {
// if((pos = strchr(tmp, '\n')) != NULL) {
// *pos = ' ';
// }
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
strcat(input_lines, tmp);
}
return input_lines;
}
Why am I getting segfaults here?
The function call
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
takes your current allocated memory block and expands it, if it can. you should check the return value of realloc, it may fail.
btw when you allocate memory in C, you always need to have space for the ending \0.
see what happens with this file
hello\n
world\n
The first fgets reads in hello\n into tmp.
you now do realloc even though it is unnecessary, input_lines is already pointing to a buffer that could hold the string
char *input_lines = malloc(MAX_LINE_LEN*sizeof(char));
now with your realloc
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
you do strlen(input_lines) + len so you make the buffer strlen("hello\n") + len long.
but the important thing you need to notice is the following line
strcat(input_lines, tmp);
you have not initialized the memory that input_lines is pointing to, it can contain anything even \0's so your strcat could potentially put the string anywhere in the buffer and cause the error you describe.
Either do a memset or use calloc when you allocate the buffer.
If you use realloc you should keep track of the total size that you have allocated and how much you are using of it, before you copy into the buffer check if there is enough room. If not, add a certain number of bytes to the buffer.
I also noticed you read from the file line by line, then you concatenated the lines together to later use strtok to divide them again. It would be more efficient to return an array of lines.

Segmentation Fault on fputs

I am pretty new to C and memory allocation in general. Basically what I am trying to do is copy the contents of an input file of unknown size and reverse it's contents using recursion. I feel that I am very close, but I keep getting a segmentation fault when I try to put in the contents of what I presume to be the reversed contents of the file (I presume because I think I am doing it right....)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int recursive_back(char **lines, int lineNumber, FILE *input) {
char *input_line = malloc(sizeof(char) * 1000);
lines = realloc(lines, (lineNumber) * 1000 * sizeof(char));
if(fgets(input_line, 201, input) == NULL) {
*(lines + lineNumber) = input_line;
return 1;
}
else {
printf("%d\n", lineNumber);
return (1+recursive_back(lines, ++lineNumber, input));
}
}
void backward (FILE *input, FILE *output, int debugflag ) {
int i;
char **lines; //store lines in here
lines = malloc(1000 * sizeof(char *) ); //1000 lines
if(lines == NULL) { //if malloc failed
fprintf(stderr, "malloc of lines failed\n");
exit(1);
}
int finalLineCount, lineCount;
finalLineCount = recursive_back(lines, 0, input);
printf("test %d\n", finalLineCount);
for(i = finalLineCount; i > 0; i--) {
fputs(*(lines+i), output); //segfault here
}
}
I am using a simple input file to test the code. My input file is 6 lines long that says "This is a test input file". The actual input files are being opened in another function and passed over to the backward function. I have verified that the other functions in my program work since I have been playing around with different options. These two functions are the only functions that I am having trouble with. What am I doing wrong?
Your problem is here:
lines = realloc(lines, (lineNumber) * 1000 * sizeof(char));
exactly as #ooga said. There are at least three separate things wrong with it:
You are reallocating the memory block pointed to by recursive_back()'s local variable lines, and storing the new address (supposing that the reallocation succeeds) back into that local variable. The new location is not necessarily the same as the old, but the only pointer to it is a local variable that goes out of scope at the end of recursive_back(). The caller's corresponding variable is not changed (including when the caller is recursive_back() itself), and therefore can no longer be relied upon to be a valid pointer after recursive_back() returns.
You allocate space using the wrong type. lines has type char **, so the object it points to has type char *, but you are reserving space based on the size of char instead.
You are not reserving enough space, at least on the first call, when lineNumber is zero. On that call, when the space requested is exactly zero bytes, the effect of the realloc() is to free the memory pointed to by lines. On subsequent calls, the space allocated is always one line's worth less than you think you are allocating.
It looks like the realloc() is altogether unnecessary if you can rely on the input to have at most 1000 lines, so you should consider just removing it. If you genuinely do need to be able to reallocate in a way that the caller will see, then the caller needs to pass a pointer to its variable, so that recursive_back() can modify it via that pointer.

Beginner C : Dynamic memory allocation

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 = &currentline;
}
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 = &currentline;
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).

Pointer char function not running the second time

I have written this function which reads a file, compares a string to each line and if the line contains the string, it return it.
char* FileSearch2 (char* File_Name , char* String) {
FILE * T = fopen(File_Name, "rt");
char* Line = (char*) malloc (sizeof(char*) * 1024);
strcat(String,"\t");
if (T) {
while (fgets(Line,1024,T)) {
if (strstr(Line, String) != NULL) {
fclose(T);
return Line;
}
}
}
fclose(T);
return "0";
}
The problem is that, the second time I run this function it always returns "0".
For example
char* FirstRun = FileSearch2 ("File.txt", Value); // Assuming the value is "Hello", it returns the line
Now
char* SecondtRun = FileSearch2 ("File.txt", Value); // Assuming the value is "Hello", it returns "0"
I would like to know what exactly I am doing wrong.
Your code has a few serious errors.
For instance, your malloc() logic is all wrong.
You seem to want to allocate room for a line of 1024 characters, but you do this:
char* Line = (char*) malloc (sizeof(char*) * 128);
it should just be:
char *Line = malloc(1024);
Don't cast the return value of malloc() in C, and the business with sizeof(char *) is just plain wrong and confused.
Then you fail to free() the line if the line is not found, which is another problem.
Your code also changes the caller's data by calling strcat(), which means that if you call it with e.g. a string literal (as your example implies) it's totally undefined behavior what's going to happen. You should re-think that part with the '\t'-appending, it's not the best way to do it.
Well, I expect it's because you function FileSearch modifies String by appending a tab character to it. So the second time through, you're looking for a string with two tabs at the end.
You should make a copy of String, append the tab to the copy, and free the copy at the end.
char *copy = strdup(String);
strcat(copy,"\t");
if (T) {
while (fgets(Line,1024,T)) {
if (strstr(Line, copy) != NULL) {
fclose(T);
free(copy);
return Line;
And, as #unwind says, you need to free Line (and copy) if the string isn't found, too.

Resources