Why segmentation fault after altering pointer to struct? - c

I currently have functioning code, that when I attempted to make a function out of the conversion of a file to array, I get a segmentation fault. I know that the objects inside of fileToArray are correct (as far as the myData objects are concerned) because when inside of the function, the myData.length, and myData.array all return correctly. However, after the pointer is referenced in the main, I get a seg fault. I'm new to c, but all of this is working without that specific pointer to the struct.
So, if I call this program with an argument with a file with multiple rows of text, the set fault happens.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
typedef struct {
int length;
char** array;
} FileStruct;
void fileToArray(FileStruct* fileDataPtr, int argc, char *argv[]){
int fd, i, n, count;
struct stat statbuf;
char *buf, *inbuf, *str, *saveptr;
char **array;
if ((fd = open(argv[1], O_RDONLY)) == -1) {
printf("Error opening file %s\n", argv[1]);
exit (-1);
}
if (lstat(argv[1], &statbuf) < 0) {
printf("Unable to lstat file %s\n", argv[1]);
exit (-1);
}
off_t filesize = statbuf.st_size;
buf = malloc(sizeof(char)*filesize);
array = malloc(sizeof(char *)*filesize);
count = 0;
if ((n = read(fd, buf, filesize)) > 0){
inbuf = buf;
for (i = 1; ; inbuf = NULL, i++) {
str = strtok_r(inbuf, "\n", &saveptr);
if (str == NULL)
break;
array[count] = malloc(sizeof(char)*(strlen(str)+1));
strcpy(array[count++], str);
}
} else {
printf("Error reading input file\n");
exit (-1);
}
close(fd);
// I know array works because it prints correctly here.
for (i = 0; i < count; i++) {
printf("%s\n", array[i]);
free(array[i]);
}
fileDataPtr->length = count;
fileDataPtr->array = array;
free(array);
free(buf);
}
int main(int argc, char *argv[]) {
int i;
FileStruct myData;
FileStruct* fileDataPtr = &myData;
fileToArray(fileDataPtr, argc, argv);
printf("length: %i", myData.length);
// I know this doesn't work because anything related to myData causes Seg fault.
// for (i = 0; i < 1; i++) {
// printf("%s\n", myData.array[i]);
// free(myData.array[i]);
// }
return 0;
}

Near the end of fileToArray, you assign array to fileDataPtr->array, then on the next line you free array. This will leave fileDataPtr->array pointing to freed memory (a dangling pointer). When you dereference it later, you're into Undefined Behavior and anything can happen.
Since the assignment transfers ownership of the allocated memory to fileDataPtr, you do not need to free array before returning from fileToArray.
Remove the free(array); line.

Related

Update array of strings in function

I have a working example of copy lines from a file into an array of strings. I want to move the code to copy the lines into a function to which I simply pass a pointer to the array of strings, where the lines will be stored, and a pointer to the file. However, I have tried to move the code into a function and keep getting seg faults. I have tried debugging using GDB and it seems like the problem is with the memory allocation to rows. But I can't work out what the problem is. realloc seems to be working correctly since I find the size of row increases on the 3rd iteration (using malloc_usable_size(*rows)), but then seg faults. I'm compiling with gcc -Wall -Wextra -pedantic -std=c99 -g c_programs/read_file_function.c on Linux.
Working example
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Please supply a file path:\n%s <file path>\n", argv[0]);
return EXIT_FAILURE;
}
FILE *fp = fopen(argv[1], "r");
if (!fp)
{
perror("ERROR");
return EXIT_FAILURE;
}
char **rows = (char **)malloc(sizeof(char *));
char *lineBuf = NULL;
size_t n = 0;
size_t nLines = 0;
ssize_t lineLength = 0;
size_t i = 0;
while ((lineLength = getline(&lineBuf, &n, fp)) != -1)
{
lineBuf[strcspn(lineBuf, "\n")] = 0;
lineBuf[strcspn(lineBuf, "\r")] = 0;
rows[i] = (char *)malloc(lineLength + 1);
strcpy(rows[i], lineBuf);
i++;
nLines = i;
rows = (char **)realloc(rows, (nLines + 1) * sizeof(char *));
}
printf("nLines: %lu\n", nLines);
printf("row 1: %s\n", rows[0]);
printf("row 2: %s\n", rows[1]);
printf("row 2: %s\n", rows[10]);
return 0;
}
Non working function version
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
size_t readFile(FILE **fp, char ***rows)
{
char *lineBuf = NULL;
size_t n = 0;
size_t nLines = 0;
ssize_t lineLength = 0;
size_t i = 0;
while ((lineLength = getline(&lineBuf, &n, *fp)) != -1)
{
lineBuf[strcspn(lineBuf, "\n")] = 0;
lineBuf[strcspn(lineBuf, "\r")] = 0;
*rows[i] = (char *)malloc(lineLength + 1);
strcpy(*rows[i], lineBuf);
i++;
nLines = i;
*rows = (char **)realloc(*rows, (nLines + 1) * sizeof(char *));
}
return nLines;
}
int main(int argc, char **argv)
{
if (argc != 2)
{
fprintf(stderr, "Please supply a file path:\n%s <file path>\n", argv[0]);
return EXIT_FAILURE;
}
FILE *fp = fopen(argv[1], "r");
if (!fp)
{
perror("ERROR");
return EXIT_FAILURE;
}
char **rows = (char **)malloc(sizeof(char *));
size_t nLines = readFile(&fp, &rows);
printf("nLines: %lu", nLines);
printf("row 1: %s", rows[0]);
printf("row 2: %s", rows[1]);
return 0;
}
*rows[i] is doing *(rows[i]) - accessing ith element in the array of rows, and then dereferencing it. You want to do (*rows)[i] - dereference rows and then access ith element.
I advise to:
readFile(..., char ***rows0) {
char **rows = NULL; // temporary internal variable
...
// use rows normally
rows = stuff();
...
// when finished, assign once
*rows0 = rows;
return nLines;
}
But do not be a 3-star programmer. At best, use a structure, -> is easy to use. Like:
struct string {
char *str;
};
struct lines {
struct string *strs;
size_t cnt;
};
// #return 0 on success, otherwise error
int readFile(...., struct lines *p) {
// initialization
p->cnt = 0;
p->strs = NULL;
...
void *pnt = realloc(p->strs, (p->cnt + 1) * ....);
if (!pnt) { /* handle error */ return -1; }
p->strs = pnt;
p->strs[p->cnt]->str = malloc(lineLenght + 1);
if (!p->strs[p->cnt]->str) { /* handle error */ return -2; }
strcpy(p->strs[p->cnt]->str, lineBuf);
p->cnt++;
...
return 0; /* success */
}
int main(int argc, char **argv) {
struct lines p = {0};
if (readFile(..., &p)) {
/* handle error */
}
printf("nLines: %zu\n", p.cnt);
Do not pre-allocate memory. Initialize memory with NULL and call realloc before using memory. realloc(NULL is the same as malloc().
Check for allocation errors.

C Programming - Read line from file and put into array [duplicate]

This question already has answers here:
Getting a stack overflow exception when declaring a large array
(8 answers)
Closed 1 year ago.
I have quite a big dictionary file. I want to take each line from the file and store it in an array so I can perform manipulations later. For example given the words:
aaaa
arggghhh
broooooo
Coooodee
If I call array[2], it should give me "broooooo". I have tried using the code below however I keep running into segmentation faults. Any help would be greatly appreciated.
Here is the code I have been trying:
int main (int argc, char* argv[]) {
char* file="/usr/share/dict/words";
FILE *dict;
char str[60];
char arr[80368][60];
int count = 0;
dict = fopen(file, "r");
if(dict == NULL){
perror("Error opening file");
return(-1);
}
while(fgets(str,sizeof(str),dict) != NULL){
strcpy(arr[count], str);
count++;
}
fclose(dict);
return 0;
}
char arr[80368][60];
This will try to allocate 4822080 bytes on the stack. The default maximum should be 8Mb so it is lower than that, but maybe it is configured lower on your system? You can inspect with ulimit -a | grep stack.
Does your program work if you test with a smaller input file, say generated with head -100 /usr/share/dict/words > input.txt, and then with the size of arr reduced correspondingly?
This is the best method to read file when you don't know the size.
If you want the file inside array you just need to use str_split else the file is inside char *
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <fcntl.h>
#include <stddef.h>
char **str_split(char *cmd, char split_by)
{
char **argv = malloc(sizeof(char *) * strlen(cmd));
int pos = 0;
for (int i = 0; cmd[i] != '\0'; i++) {
if (cmd[i] == split_by || cmd[i] == '\0') {
cmd[i] = '\0';
argv[pos] = strdup(cmd);
pos++;
cmd += i + 1;
i = 0;
}
}
argv[pos] = strdup(cmd);
argv[pos + 1] = NULL;
return argv;
}
char *load_file(char *file_path)
{
int fd;
struct stat file_stat;
stat(file_path, &file_stat);
int file_size = file_stat.st_size;
char *str = malloc(sizeof(char) * file_size + 2);
if ((fd = open(file_path, O_RDONLY)) == -1) {
printf("error");
exit(84);
}
read(fd, str, file_size);
str[file_size] = '\0';
return str;
}
int main(int ac, char **av)
{
char *file_in_string = load_file(av[1]);
printf("this is the file in one string:\n%s\n", file_in_string);
char **file_in_array = str_split(file_in_string, '\n');
printf("this is the file inside array");
for (int i = 0; file_in_array[i]; i++)
printf("line [%d]: %s\n", i, file_in_array[i]);
}

Read a file using "read()" function

i'm studying C, and I need to read a text-file, but I can only use "write, malloc, free, open, read, close".
That's my code:
#define MAXCHAR 10000
int open_fp(int check)
{
char *int_vector;
int fp,len;
int i,j;
char buffer[MAXCHAR];
if(check == 0) //standard list
{
if((fp = open("file.txt", O_RDONLY)) != -1) //check if the fp is opened. -1 = error
{
printf("\n%d\n",fp); // DEBUG FUNCTION
sleep(1);
if (!(int_vector = (char*)malloc(sizeof(char) * sizeof(char))))
{
printf("\nWrong allocation\n!"); // DEBUG FUNCTION
return(0);
}
len = read(fp,int_vector,MAXCHAR);
for(i=0;i<len;i++)
{
printf("%c",int_vector[i]);
}
}
else
{
printf("File error!");
return (0);
}
}
return(0);
}
Now my question is: As you can read here,
char buffer[MAXCHAR];
i've created static buffer, but i would like create a dynamic buffer which allow me to resize the buffer according to the number of the chars in the text file, but i don't know how... someone have a trick😥😥 ?
First of all your way of allocating memory is wrong in below line.
//This allocates only 2 bytes of memory, but you are trying to read 10000
if (!(int_vector = (char*)malloc(sizeof(char) * sizeof(char))))
correct that line as below
//better to add one byte extra and store \0 at the end, useful in case of string operations
if (!(int_vector = malloc(MAXCHAR+1)))
and as far as your question is concerned, you dont need to reallocate memory in this particular case because you are just reading the bytes to buffer and printing.
a single malloc will suffice.
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAXCHAR 100
int open_fp(int check)
{
char *int_vector;
int fp,len;
int i,j;
char buffer[MAXCHAR];
if(check == 0) //standard list
{
if((fp = open("file.txt", O_RDONLY)) != -1) //check if the fp is opened. -1 = error
{
printf("\n%d\n",fp); // DEBUG FUNCTION
sleep(1);
if (!(int_vector = (char*)malloc(MAXCHAR)))
{
printf("\nWrong allocation\n!"); // DEBUG FUNCTION
return(0);
}
//not doing memset on purpose because only limited bytes are accessed.
while(len = read(fp,int_vector,MAXCHAR))
{
printf("\n **number of bytes read is %d **\n",len);
for(i=0;i<len;i++)
{
printf("%c",int_vector[i]);
}
}
printf(" At last LEN = %d\n", len);
//free the memory at the end
free(int_vector);
int_vector = NULL;
close(fp);// better to as fd
}
else
{
printf("File error!\n");
return (0);
}
}
return(0);
}
int main()
{
open_fp(0);
return 0;
}
ehm, If you forget to set realloc to it as well, here is some sample code for reallocation (dynamic resizing buffer)
#include <stdio.h>
#include <stdlib.h>
int main () {
char *str;
/* Initial memory allocation */
str = (char *) malloc(15);
strcpy(str, "tutorialspoint");
printf("String = %s, Address = %u\n", str, str);
/* Reallocating memory */
str = (char *) realloc(str, 25);
strcat(str, ".com");
printf("String = %s, Address = %u\n", str, str);
free(str);
return(0);
}

Segmentation fault with recieved signal SIGSEGV

I'm working on an implementation of the tail Unix command, and this is my code so far:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
char *resize(char *data, int size)
{
char *newData = (char*) malloc((size + 1) * sizeof(char));
int counter;
for(counter = 0; counter < size; counter++)
newData[counter] = data[counter];
free(data);
return newData;
}
int printLines(char *data, int size)
{
int lines = 0, position, counter;
for(position = size - 1; position > -1; position--)
{
if (data[position] == '\n') lines++;
if (lines == 10) break;
}
if (lines == 10)
for(counter = position; counter < size; counter++)
{
write(STDOUT_FILENO, &data[counter], 1);
}
else write(STDOUT_FILENO, data, size);
return 0;
}
int stdIn(char *data, int size)
{
char buff, end = '\n';
int rState = 0;
while ((rState = read(STDIN_FILENO, &buff, 1)) > 0)
{
if(rState < 0)
{
if(errno == EINTR) rState = 0;
else
{
perror("read()");
return 1;
}
}
data = resize(data, size);
data[size - 1] = buff;
size++;
}
if(rState == 0) write(STDOUT_FILENO, &end, 1);
return 0;
}
int tailRead(char *data, char *fileName)
{
int size = 1;
data = (char*)malloc(size * sizeof(char));
if(fileName == 0 || fileName == "-")
{
if(stdIn(data, size) > 0) return 1;
}
else
{
}
printLines(data, size);
return 0;
}
int main(int argc, char *argv[])
{
char *data = 0;
int counter;
if(argc == 1)
{
tailRead(data, 0);
if(data > 0) return 1;
}
else for (counter = 1; counter < argc; counter++)
{
tailRead(data, argv[counter]);
if(data > 0) return 1;
}
return 0;
}
The problem is that somewhere in the resize() function i get a Segmentation Fault, and when i ran the program in GDB, i got Program received signal SIGSEGV Segmentation fault. 0x00000000004006f7 in resize (). This tells me that I have some kind of memory allocation problem in resize(), but so far I have been unable to find the bug. What should I do?
int tailRead(char *data, char *fileName)
/* ... */
int main(int argc, char *argv[])
{
char *data = 0;
/* ... */
tailRead(data, 0);
}
You seem to expect, that in main() data will point to the memory allocated in tailRead(). That is not the case. In tailRead() data is a copy of the pointer data from main() you only change the copy, not the original pointer. The original pointer still points to 0.
Then you call resize() with a null pointer, which of course will lead to a segmentation violation.
Solution
use a pointer to a pointer instead, to modify the original pointer.
int tailRead(char **data, char *fileName)
{
int size = 1;
*data = (char*)malloc(size * sizeof(char));
/* ... */
}
int main(int argc, char *argv[])
{
char *data = 0;
/* ... */
tailRead(&data, 0);
/* ... */
}
You have the same issue with stdIn(). It changes data without reflecting the change to the data-pointer at the call site in tailRead(). You leave stdIn() leaking memory and continue to work with a dangling pointer.
I think your problem is here...
for(counter = 0; counter < size; counter++)
newData[counter] = data[counter];
You are trying to access data[counter] when counter is greater than what you allocated for the previous malloc(). By this I mean, you are reading beyond the (current) legitimate end of data. Make sense?
EDIT: Now that I think about it, this may not cause the segfault, but it is a problem.

How to concatenate all of the command line parameters together and print out the resulting value?

I'm having trouble trying to figure out how to concatenate the file path with the strings. The user input the strings in the command prompt like this:
StringLabMain.exe Mary had a little lamb 1234
It supposed to print out something like this:
Concatenated arguments: d:\Documents and Settings\labadmin\My Documents\Visual Studio 2012\Projects\test\debug\StringLabMain.exeMaryhadalittlelamb1234
but my code prints out this:
Concatenated arguments: StringLabMain.exeMaryhadalittlelamb1234
Here is my code (I don't understand how the concatenate works to include the file path with the strings):
int main(int argc, char *argv[])
{
int i;
for (i = 0; i < argc; i++)
{
printf("%s", argv[i]);
}
return 0;
}
I hope I explained this clearly.
First, if your only purpose is to print the directory and the concatenated args, so you just have to print the current directory before the main loop. This may be done using getcwd().
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int i;
printf("%s", getcwd(0,0));
for (i = 0; i < argc; i++)
{
printf("%s", argv[i]);
}
return 0;
}
But for more general purposes I really recommend you to use stracat() which concatenates string. So you have to declare a "string" (using char *) with the current working directory, and then concatenate the args. This will be done like this way:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
char* toPrint;
int i;
toPrint = getcwd(0,0);
for (i = 0; i < argc; i++)
strcat (toPrint, argv[i]);
printf("%s\n",toPrint);
return 0;
}
I hope that know it's clear.
The following code demonstrates how to use strcat() to build up a string of all argv[] elements:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int i;
size_t outputSize = 1;
char *output = NULL;
/* Allocate a buffer large enough to hold the string termination character. */
output=malloc(outputSize);
if(!output)
{
fprintf(stderr, "malloc() failed.\n");
goto CLEANUP;
}
*output = '\0';
/* Iterate argv[] elements. */
for(i = 0; i < argc; i++)
{
char *tmp;
/* Increase the size of the output buffer to hold this argv[] element. */
outputSize += strlen(argv[i]);
tmp=realloc(output, outputSize);
if(!tmp)
{
fprintf(stderr, "realloc() failed.\n");
goto CLEANUP;
}
output=tmp;
/* Concatinate this argv[] element to the output string. */
strcat(output, argv[i]);
}
/* Print the result. */
printf("%s\n", output);
CLEANUP:
if(output)
free(output);
return 0;
}
On Linux, you can also include the path of the current working directory, like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int i;
size_t outputSize;
char *output = NULL;
output=getcwd(NULL,0);
if(!output)
{
fprintf(stderr, "getcwd() failed.\n");
goto CLEANUP;
}
outputSize = strlen(output) + 1;
for(i = 0; i < argc; i++)
{
char *tmp;
outputSize += strlen(argv[i]);
tmp=realloc(output, outputSize);
if(!tmp)
{
fprintf(stderr, "realloc() failed.\n");
goto CLEANUP;
}
output=tmp;
strcat(output, argv[i]);
}
printf("%s\n", output);
CLEANUP:
if(output)
free(output);
return 0;
}
The above example is Linux specific due to a Linux extension to 'getcwd()'. The Linux getcwd man page states:
As an extension to the POSIX.1-2001 standard, Linux (libc4, libc5, glibc) getcwd() allocates the buffer dynamically using malloc(3) if buf is NULL. In this case, the allocated buffer has the length size unless size is zero, when buf is allocated as big as necessary. The caller should free(3) the returned buffer.
Apparently, _getcwd() works the same way on MS Windows. MSDN states about _getcwd():
The _getcwd function gets the full path of the current working directory for the default drive and stores it at buffer. The integer argument maxlen specifies the maximum length for the path. An error occurs if the length of the path (including the terminating null character) exceeds maxlen. The buffer argument can be NULL; a buffer of at least size maxlen (more only if necessary) is automatically allocated, using malloc, to store the path. This buffer can later be freed by calling free and passing it the _getcwd return value (a pointer to the allocated buffer).
So, perhaps the following (untested) code would be suitable for a MS Windows environment:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <direct.h>
int main(int argc, char *argv[])
{
int i;
size_t outputSize;
char *output = NULL;
output=_getcwd(NULL,0);
if(!output)
{
fprintf(stderr, "_getcwd() failed.\n");
goto CLEANUP;
}
outputSize = strlen(output) + 1;
for(i = 0; i < argc; i++)
{
char *tmp;
outputSize += strlen(argv[i]);
tmp=realloc(output, outputSize);
if(!tmp)
{
fprintf(stderr, "realloc() failed.\n");
goto CLEANUP;
}
output=tmp;
strcat(output, argv[i]);
}
printf("%s\n", output);
CLEANUP:
if(output)
free(output);
return 0;
}

Resources