I have some code where I'm trying to read lines in from a file and store some information from each line in a struct. Since I don't know how long the file will be, I'm dynamically adjusting the array of structs using realloc.
My issue is that my code seems to work fine for the first 3 (technically 6) lines, and then I receive SIGSEGV (address boundary error). gdb says that this happens when trying to index the array (array[i]->string = (char*) _tmp).
typedef struct {
char* string;
int len;
} buffer;
int read_into_array(char *filename, buffer** array) {
int n;
size_t size;
char* buf = NULL;
FILE *file = fopen(filename, "r");
int i = 0;
while (1) {
buffer *tmp = (buffer*)realloc(*array, sizeof(buffer) * (i + 1));
if (!tmp)
printf("Failed realloc\n");
*array = tmp;
// First line is ignored, second line is taken as data.
getline(&buf, &size, file);
n = getline(&buf, &size, file);
if (n > 0) {
void* _tmp = malloc(sizeof(char) * n);
if (!_tmp)
printf("Failed malloc\n");
array[i]->string = (char*) _tmp;
array[i]->len = n-1;
strncpy(array[i]->string, buf, n-1);
}
i++;
if (feof(file)) {
printf("saw end of file, leaving.\n");
break;
}
}
return i;
}
int main(int argc, char* argv[]) {
char *filename = argv[1];
buffer *array = (buffer*) calloc(1, sizeof(buffer));
int num = read_into_array(filename, &array);
}
Apologies for the somewhat poor formatting, I've been trying to figure this out for a while.
Since it seems to work for the first few lines, my assumption is that I'm going wrong somewhere in the realloc calculation. My other guess is that I'm somehow using/reading the file incorrectly.
Thanks for any help. For posterity, the file looks something like this https://hastebin.com/vinidiyita.sm (the real file is thousands of lines long).
when you do *array=tmp you're allocating memory for array[0]
then you're using array[i] that should be a pointer to a buffer, but points to garbage or 0
You're confusing two ways to use data.
The first is by using arrays - there's the non-dynamic:
buffer array[x] = {0};
int num = read_into_array(filename, &array);
then you can use array[i]
and there's the dynamic type:
buffer **array = calloc(initial_len*sizeof(buffer *));
int num = read_into_array(filename, array, initial_len);
read_into_array(char *filename, buffer **&array, int initial_len)
{
int len = initial_len;
...
while()
{
...
if(i>len)
{
array = realloc(array, sizeof(buffer*) * (i + 1));
len = i;
}
array[i] = calloc(sizeof(buffer));
}
}
Related
I probably got an easy one for the C programmers out there!
I am trying to create a simple C function that will execute a system command in and write the process output to a string buffer out (which should be initialized as an array of strings of length n). The output needs to be formatted in the following way:
Each line written to stdout should be initialized as a string. Each of these strings has variable length. The output should be an array consisting of each string. There is no way to know how many strings will be written, so this array is also technically of variable length (but for my purposes, I just create a fixed-length array outside the function and pass its length as an argument, rather than going for an array that I would have to manually allocate memory for).
Here is what I have right now:
#define MAX_LINE_LENGTH 512
int exec(const char* in, const char** out, const size_t n)
{
char buffer[MAX_LINE_LENGTH];
FILE *file;
const char terminator = '\0';
if ((file = popen(in, "r")) == NULL) {
return 1;
}
for (char** head = out; (size_t)head < (size_t)out + n && fgets(buffer, MAX_LINE_LENGTH, file) != NULL; head += strlen(buffer)) {
*head = strcat(buffer, &terminator);
}
if (pclose(file)) {
return 2;
}
return 0;
}
and I call it with
#define N 128
int main(void)
{
const char* buffer[N];
const char cmd[] = "<some system command resulting in multi-line output>";
const int code = exec(cmd, buffer, N);
exit(code);
}
I believe the error the above code results in is a seg fault, but I'm not experienced enough to figure out why or how to fix.
I'm almost positive it is with my logic here:
for (char** head = out; (size_t)head < (size_t)out + n && fgets(buffer, MAX_LINE_LENGTH, file) != NULL; head += strlen(buffer)) {
*head = strcat(buffer, &terminator);
}
What I thought this does is:
Get a mutable reference to out (i.e. the head pointer)
Save the current stdout line to buffer (via fgets)
Append a null terminator to buffer (because I don't think fgets does this?)
Overwrite the data at head pointer with the value from step 3
Move head pointer strlen(buffer) bytes over (i.e. the number of chars in buffer)
Continue until fgets returns NULL or head pointer has been moved beyond the bounds of out array
Where am I wrong? Any help appreciated, thanks!
EDIT #1
According to Barmar's suggestions, I edited my code:
#include <stdio.h>
#include <stdlib.h>
#define MAX_LINE_LENGTH 512
int exec(const char* in, const char** out, const size_t n)
{
char buffer[MAX_LINE_LENGTH];
FILE *file;
if ((file = popen(in, "r")) == NULL) return 1;
for (size_t i = 0; i < n && fgets(buffer, MAX_LINE_LENGTH, file) != NULL; i += 1) out[i] = buffer;
if (pclose(file)) return 2;
return 0;
}
#define N 128
int main(void)
{
const char* buffer[N];
const char cmd[] = "<system command to run>";
const int code = exec(cmd, buffer, N);
for (int i = 0; i < N; i += 1) printf("%s", buffer[i]);
exit(code);
}
While there were plenty of redundancies with what I wrote that are now fixed, this still causes a segmentation fault at runtime.
Focusing on the edited code, this assignment
out[i] = buffer;
has problems.
In this expression, buffer is implicitly converted to a pointer-to-its-first-element (&buffer[0], see: decay). No additional memory is allocated, and no string copying is done.
buffer is rewritten every iteration. After the loop, each valid element of out will point to the same memory location, which will contain the last line read.
buffer is an array local to the exec function. Its lifetime ends when the function returns, so the array in main contains dangling pointers. Utilizing these values is Undefined Behaviour.
Additionally,
for (int i = 0; i < N; i += 1)
always loops to the maximum storable number of lines, when it is possible that fewer lines than this were read.
A rigid solution uses an array of arrays to store the lines read. Here is a cursory example (see: this answer for additional information on using multidimensional arrays as function arguments).
#include <stdio.h>
#include <stdlib.h>
#define MAX_LINES 128
#define MAX_LINE_LENGTH 512
int exec(const char *cmd, char lines[MAX_LINES][MAX_LINE_LENGTH], size_t *lc)
{
FILE *stream = popen(cmd, "r");
*lc = 0;
if (!stream)
return 1;
while (*lc < MAX_LINES) {
if (!fgets(lines[*lc], MAX_LINE_LENGTH, stream))
break;
(*lc)++;
}
return pclose(stream) ? 2 : 0;
}
int main(void)
{
char lines[MAX_LINES][MAX_LINE_LENGTH];
size_t n;
int code = exec("ls -al", lines, &n);
for (size_t i = 0; i < n; i++)
printf("%s", lines[i]);
return code;
}
Using dynamic memory is another option. Here is a basic example using strdup(3), lacking robust error handling.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **exec(const char *cmd, size_t *length)
{
FILE *stream = popen(cmd, "r");
if (!stream)
return NULL;
char **lines = NULL;
char buffer[4096];
*length = 0;
while (fgets(buffer, sizeof buffer, stream)) {
char **reline = realloc(lines, sizeof *lines * (*length + 1));
if (!reline)
break;
lines = reline;
if (!(lines[*length] = strdup(buffer)))
break;
(*length)++;
}
pclose(stream);
return lines;
}
int main(void)
{
size_t n = 0;
char **lines = exec("ls -al", &n);
for (size_t i = 0; i < n; i++) {
printf("%s", lines[i]);
free(lines[i]);
}
free(lines);
}
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 a Problem when I try to load a file into memory as an array.
I am trying to load a file into an array and print it out again but I would like to allow the memory to grow as the file length can be arbitrary.
When I run my Program locally on my Mac it seems to Work fine but when I try it on my Ubuntu VM, I get the following error
realloc(): invalid next size
Aborted (core dumped)
My Code is as follows
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **loadfile(char *filename, int *len);
int main(int argc, char *argv[])
{
if (argc == 1)
{
printf("Usage add file\n");
return 1;
}
int length = 0;
char **words = loadfile(argv[1],&length);
printf("%d\n", length);
for (int i = 0; i < length; i++) {
printf("%s\n",words[i]);
}
printf("Done\n");
return 0;
}
char **loadfile(char *filename, int *len)
{
const int STEPSIZE = 10;
FILE *f = fopen(filename,"r");
if (!f) {
fprintf(stderr, "Can't open file\n");
return NULL;
}
int arrlen = STEPSIZE;
char **lines = (char **)malloc(STEPSIZE);
char buf[100];
int i = 0;
int counter = 2;
while (fgets(buf,100,f))
{
if (i == arrlen)
{
counter++;
arrlen += STEPSIZE;
char **newlines = (char **)realloc(lines,counter * STEPSIZE);
if(!newlines)
{
printf("Out of memory\n");
//return 2;
}
lines = newlines;
}
buf[strlen(buf)-1] = '\0';
int slen = strlen(buf);
char *str = (char *)malloc(slen + 1 *sizeof(char ));
strcpy(str, buf);
lines[i] = str;
i++;
}
*len =i;
return lines;
}
and for the life of me I cannot find the problem.
I can only assume the problem is somewhere in this section but I may be wrong:
if (i == arrlen)
{
counter++;
arrlen += STEPSIZE;
char **newlines = (char **)realloc(lines,counter * STEPSIZE);
if(!newlines)
{
printf("Out of memory\n");
//return 2;
}
lines = newlines;
}
Your Help is greatly appreciated
const int STEPSIZE = 10;
char **lines = (char **)malloc(STEPSIZE);
char **newlines = (char **)realloc(lines,counter * STEPSIZE);
You don't want to allocate 10 bytes, but memory for 10 char * elements. Thus some subsequent access to lines[i] = str; is invalid.
What you meant to do is:
char **lines = malloc(sizeof(*lines) * STEPSIZE);
char **newlines = realloc(lines, sizeof(*newlines) * counter * STEPSIZE);
Alternatively you can use sizeof(char*).
Also:
char *str = (char *)malloc(slen + 1 *sizeof(char ));
although it is correct and will work, because sizeof(char) is 1, but it's more clearly the intention was:
char *str = malloc((slen + 1) * sizeof(char));
Also, it's good to think if you should cast the result of malloc.
You forgot that malloc and realloc take an amount of bytes, not an amount of 'cells' in your array.
The program works as expected if you replace your malloc(STEPSIZE) by malloc(STEPSIZE * sizeof(char*)) and realloc(lines, counter * STEPSIZE) by realloc(lines, (counter * STEPSIZE) * sizeof(char*))
I'm trying to make a program to crack passwords by searching through a file of md5 hashes and using bsearch to find them in a rockyou database. My problem is that I'm running into a segmentation fault that is either caused by my qsort or my printf (I've run Valgrind and it says printf, but manipulating qsort changes the error output). I can't seem to find the solution online, though I've tried flushing stdout and different ways to size the array in the qsort function.
char **dict = read_dict( argv[2] );
read_dict, which I haven't placed here because it's a hefty chunk of code, takes in the dictionary file, splits it into an array of strings, formats it into hash:password, and mallocs the space for it. It then returns the pointer of the array of pointers that contains each string.
int qcompare( const void *a, const void *b)
{
return strncmp( *((char **)a), *((char **)b), HASH_LEN);
}
qsort(dict, (sizeof(dict) / sizeof(dict[0])), sizeof(char *), qcompare);
for (int i = 0; dict[i] != NULL; i++)
{
printf("%s\n", dict[i]);
}
The printf shown here isn't the actual one I'm using, it's just a simpler one I was trying to use to debug my code. It's my first time posting so hopefully I haven't done anything atrociously wrong with formatting this question. Thank you in advance for any help I get.
read_dict as requested
char **read_dict(char *filename)
{
FILE *f = fopen(filename, "r");
if (!f)
{
printf("read_dict: file error message\n");
exit(1);
}
int arrlen = 0;
int i = 0;
char **dict = NULL;
char buf[PASS_LEN];
while (fgets(buf, PASS_LEN, f) != NULL)
{
if (i == arrlen)
{
arrlen += STEPSIZE;
char **newdict = realloc(dict, arrlen * sizeof(char*));
if (!newdict)
{
printf("read_dict: newdict error message\n");
exit(1);
}
dict = newdict;
}// end of if
buf[strlen(buf) - 1] = '\0';
int slen = strlen(buf);
char *pass = malloc( (slen + 1) * sizeof(char));
strcpy(pass, buf);
char output[(HASH_LEN + PASS_LEN + 1)];
sprintf(output, "%s:%s", md5(pass, strlen(pass)), pass );
dict[i] = output;
i++;
}// end of while
if (i == arrlen)
{
char **newarr = realloc(dict, (arrlen + 1) * sizeof(char*));
if (!newarr)
{
printf("read_dict: newarr error message\n");
exit(1);
}
dict = newarr;
}
dict[i] = NULL;
return dict;
}// end of read_dict
This is my first year with C, so I am a bit lost.
I have the function:
void read(char** lines){
FILE *fpointer = fopen("input1.txt","r");
char *p_input = (char*) malloc(sizeof(char)*200);
int i,len;
i=0;
lines = malloc(sizeof(char*));
while( fgets(p_input,200,fpointer) ){
len = strlen(p_input);
char temp[len];
strcpy(temp,p_input);
lines[i] = temp;
i++;
}
}
and in main:
int main(){
char **lines;
read(lines);
return 0;}
And when I try printing something from the array, I face errors and the code stops, something like:
printf("%s\n",lines[0]);
Can you please tell me what is wrong.
lines = malloc(sizeof(char*));
...
lines[i] = temp;
This is wrong, you don't have enough space for an array of pointer to chars (you need to know the number of lines to reserve)
Change to something like
char **read(void) {
size_t n = file_lines;
char **lines = malloc(sizeof(char*) * n);
...
return lines;
}
int main(void) {
char **lines;
lines = read();
return 0;
}
If you dont know the number of lines before-hand you can use realloc on each iteration of the while loop.
char **read(void) {
...
char **lines = NULL;
char **tmp;
...
while (fgets(p_input,200,fpointer)) {
...
tmp = realloc(lines, sizeof(char *) * (i + 1));
if (tmp != NULL) {
lines = tmp;
} else {
return NULL;
}
lines[i] = temp;
i++;
}
return lines;
}
int main(void) {
char **lines;
lines = read();
if (lines == NULL) {
perror("read");
exit(EXIT_FAILURE);
}
return 0;
}
In the realloc() example, if you use that together with the original code:
while( fgets(p_input,200,fpointer) ){
len = strlen(p_input);
char temp[len];
...
That's a mistake because temp[len] is declared inside the loop which means it'll be destroyed upon exiting the while loop. So your entries inside your realloc() array will point to nothing.
You would want to use malloc() inside the while loop to generate a separate space for each entry rather than declare a static array like the above.
And to tidy up at the end remember to free() the space at the end before your program exits completely.