Read input into array results in segmentation violation - c

My program has the following requirements: If a command line argument is given, interpret it as a file name and read the input from that file. Otherwise, read input from stdin instead. As I am going to need the input later, I want to save it into an array. And because any non-ASCII characters are to be ignored, I decided to process the input character by character. This is my code:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define MAX_WORDS 999
#define MAX_WORD_LENGTH 50
typedef struct Data{
char words[MAX_WORDS][MAX_WORD_LENGTH+1];
int numwords;
} Data;
Data* data;
int main(int argc, char** argv){
data= malloc(sizeof data);
FILE* infile;
if(argc<=1){//if no arguments, read words from stdin
infile= stdin;
}else if(argc==2){//read words from file
infile= fopen(argv[1], "r");
if(infile==NULL){
printf("failed to open word file");
return 1;
}
}
char c;
int wordindex= 0;
int charindex= 0;
while(1){//read input character by character, only accepting ASCII characters
c= fgetc(infile);
if(c=='\n' || c==EOF){
data->words[wordindex][charindex]= '\0';
wordindex++;
charindex= 0;
if(wordindex>=MAX_WORDS || c==EOF)
break;
continue;
}
if(!isascii(c))
continue;
data->words[wordindex][charindex]= toupper(c);
charindex++;
if(charindex>=MAX_WORD_LENGTH){
wordindex++;
charindex= 0;
if(wordindex>=MAX_WORDS)
break;
}
}
if(argc==2) fclose(infile);
data->numwords= wordindex-1;
//check if everything worked as intended
printf("==================\n%d word(s) read:\n", data->numwords);
for (int i = 0; i < data->numwords; i++)
printf("%d %s\n", (int)strlen(data->words[i]), data->words[i]);
}
Everything works fine if I enter the input through stdin, but if I attempt to read the input from a text file, the program segfaults. It seems to work if the text file contains only one line of text, but if there are two or more then it crashes. I'm a beginner in C and I don't see any difference between reading from stdin or a file, so I have no idea why this is happening. Can somebody enlighten me?

This
Data* data;
data= malloc(sizeof data);
allocates bytes to suite the size of data, with data being "just" a pointer to Data, not Data itself. A pointer is 4/8 bytes depending whether on a 32/64 bit platform.
Allocating to few memory here leads to writing to invalid memory soon and with this invoking the infamous Undefined Behaviour. From this moment on anything can happen ranging from crash to nothing.
If you want the number of bytes to hold Data you want to allocate like this
data = malloc(sizeof (Data));
of even better like this
data = malloc(sizeof *data);
Also one should test the outcome of relevant system call, also malloc() could fail:
if (NULL == data)
{
perror("malloc() failed");
exit(EXIT_FAILURE);
}

Related

Read a file specified as an argument and return its' lines

I have an exercise in which I have to read a file containing strings and I have to return the content using one/multiple arrays (this is because the second part of this exercise asks for these lines to be reversed, I'm having problems - and therefore ask for help - with the input).
So far, I have this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LENGTH 1024
int main(int argc, char *argv[]){
char* input[LENGTH];
if(argc==2){
FILE *fp = fopen(argv[1], "rt");
if(fp!=NULL){
int i=0;
while(fgets(input, sizeof(input), fp)!=NULL){
input[i] = (char*)malloc(sizeof(char) * (LENGTH));
fgets(input, sizeof(input), fp);
i++;
}
printf("%s", *input);
free(input);
}
else{
printf("File opening unsuccessful!");
}
}
else{
printf("Enter an argument.");
}
return 0;
}
I also have to check whether or not memory allocation has failed. This program in its' current form returns nothing when run from the command line.
EDIT: I think it's important to mention that I get a number of warnings:
passing argument 1 of 'fgets' from incompatible pointer type [-Wincompatible-pointer-types]|
attempt to free a non-heap object 'input' [-Wfree-nonheap-object]|
EDIT 2:
Example of input:
These
are
strings
... and the expected output:
esehT
era
sgnirts
In the exercise, it's specified that the maximum length of a line is 1024 characters.
You probably want something like this.
Comments are in the code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LENGTH 1024
int main(int argc, char* argv[]) {
if (argc == 2) {
FILE* fp = fopen(argv[1], "rt");
if (fp != NULL) {
char** lines = NULL; // pointer to pointers to lines read
int nboflines = 0; // total number of lines read
char input[LENGTH]; // temporary input buffer
while (fgets(input, sizeof(input), fp) != NULL) {
char* newline = malloc(strlen(input) + 1); // allocate memory for line (+1 for null terminator)
strcpy(newline, input); // copy line just read
newline[strcspn(newline, "\n")] = 0; // remove \n if any
nboflines++; // one more line
lines = realloc(lines, nboflines * sizeof(char*)); // reallocate memory for one more line
lines[nboflines - 1] = newline; // store the pointer to the line
}
fclose(fp);
for (int i = 0; i < nboflines; i++) // print the lins we've read
{
printf("%s\n", lines[i]);
}
}
else {
printf("File opening unsuccessful!");
}
}
else {
printf("Enter an argument.");
}
return 0;
}
Explanation about removing the \n left by fgets: Removing trailing newline character from fgets() input
Disclaimers:
there is no error checking for the memory allocation functions
memory is not freed. This is left as an exercise.
the way realloc is used here is not very efficient.
you still need to write the code that reverses each line and displays it.
You probably should decompose this into different functions:
a function that reads the file and returns the pointer to the lines and the number of lines read,
a function that displays the lines read
a function that reverses one line (to be written)
a function that reverses all lines (to be written)
This is left as an exercise.

C Using char* in fscanf causing error Segmentation fault: 11

I am new to C and I came across an issue when using fscanf to read all strings from a .txt file.
The code is as follow:
#include <stdlib.h>
#include <stdio.h>
int main() {
FILE *spIn;
char *numIn;
spIn = fopen("data.txt", "r");
if (spIn == NULL) {
printf("Can't Open This File \n");
}
while ((fscanf(spIn, "%s", numIn)) == 1) {
printf("%s\n", numIn);
};
fclose(spIn);
return 1;
}
This throws an error: Segmentation fault: 11.
The original data on txt file is:
1 2 345 rrtts46
dfddcd gh 21
789 kl
a mix of ints, strings, white space and newline characters.
At least 4 candidate undefined behaviors (UB) that could lead to a fault of some kind.
Code fails to pass to fscanf(spIn,"%s",numIn) an initialized pointer.
Code calls fscanf() even if fopen() fails.
Code calls fclose() even if fopen() fails.
No width limit in fscanf(spIn,"%s",numIn)), worse than gets().
Text files really do not have strings ('\0' terminated data) nor int, they have lines (various characters with a '\n' termination).
To read a line in and save as a string, use fgets(). Do not use fscanf() to read lines of data.
#include <stdlib.h>
#include <stdio.h>
int main() {
FILE *spIn = fopen("data.txt", "r");
if (spIn == NULL) {
printf("Can't Open This File \n");
} else {
char buf[100];
while (fgets(buf, sizeof buf, spIn)) {
printf("%s", buf);
}
fclose(spIn);
}
}
char* numIn is a pointer, and it is uninitalized, you can't really store anything in it, you need to either allocate memory for it or make it point to some valid memory location:
#include<stdlib.h> // for malloc
char* numIn = malloc(100); // space for 99 char + null terminator byte
//...
while ((fscanf(spIn, "%99s", numIn)) == 1)
{
printf("%s\n",numIn);
};
Or:
char str[100];
char *numIn = str;
Which in this small code makes little sense, you should probably make numIn a fixed size array to begin with:
char numIn[100];
Note that that you should use a width specifier in *scanf to avoid buffer overflow. This still has a problem though, it will read word by word, instead of line by line.
Looking at your input file, using fgets seems like a better option, it can read complete lines, including spaces:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
FILE *spIn;
char numIn[100];
spIn = fopen("data.txt", "r");
if (spIn != NULL)
{
while ((fgets(numIn, sizeof numIn, spIn)))
{
numIn[strcspn(numIn, "\n")] = '\0'; // removing \n
printf("%s\n", numIn);
}
fclose(spIn);
}
else
{
perror("Can't Open This File");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Since fgets also parses the \n character, I'm removing it with strcspn.
Though you do verify the return value of fopen the execution continues even if it fails to open, I also addressed that issue.

Do i need to use 2D arrays for an array of strings in C?

I want my program to read N words from a text file and save them in an array. My question is, do i need a 2D Array e.g: char **wordList or is the 1D Array in the example below sufficient? The output is correct except from the last string which as you can see is weird. Also, am i allocating sufficient memory for the array and why does the last output string come out wrong?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void populateWordsArray(int);
FILE *file;
char *word;
char **wordList;
/*
* Function populateWordsArray: reads N words from
* the given file and creates an array with them.
* Has one argument: int N - the number of words to read.
*/
void populateWordsArray(int N) {
int i = 0;
while(!feof(file) && i < N) {
fscanf(file,"%s",&word[i]);
printf("%s\n",&word[i]);
i++;
}
}
int main(int argc,char *argv[]) { // argv[1] = op argv[2] = name
int N = 0;
file = fopen(argv[2],"r");
if(file == (FILE *) NULL) { // check if the file opened successfully
fprintf(stderr,"Cannot open file\n");
}
fscanf(file,"%d",&N); // get the N number
word = malloc(N * sizeof(char));
populateWordsArray(N);
// write a switch method for the various ops
// call the appropriate function for each operation
free(word);
fclose(file);
return 0;
}
Output:
this
is
a
test!
with
files.
new
line,
here.
ere.
text file content:
10 this is a test! with files.
new line, here.
Your example is wrong. When executing the line fscanf(file,"%s",&word[i]);, the third argument should be the address where the function will write the read data. In your case, word[i] is the i-th element of the array and &word[i] is its address. So, the word will be stored with the first character at the word[i]. Your code only prints something because you print it immediately. Also, you don't get a segfault by pure chance.
If you want to read a string into a buffer, you first need to allocate the space for the buffer.
By using char **, you can make it into a 2D array by first allocating sufficient space for the array of pointers and then allocate sufficient space for each of the pointers to hold an address to a string.
I have rewritten your program for you:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_STRING_LENGTH 100
void populateWordsArray(int);
FILE *file;
char **wordList;
void populateWordsArray(int N)
{
int i = 0;
while(i < N && fscanf(file,"%s", wordList[i]) == 1) // fscanf returns the number of successfully read items. If it's not 1, the read failed. You can also check if the return value is not EOF but, in this situation, it's the same.
{
printf("%s\n", wordList[i]); // i-th element is an address to a buffer that contains 100 bytes
i++;
}
}
int main(int argc,char *argv[])
{
int N = 0, i;
file = fopen(argv[1],"r"); // Indexing starts from 0 in C. Thus, 0th argument is the executable name and 1st is what you want.
if(file == NULL) // No need to cast NULL into a specific type.
{
fprintf(stderr,"Cannot open file\n");
return 1; // You might want to end the program here, possibly with non-zero return value.
}
fscanf(file,"%d",&N);
wordList = malloc(N * sizeof(char*)); // Allocating space for pointers
for(i=0; i<N; i++)
{
wordList[i] = malloc(MAX_STRING_LENGTH); // Allocating space for individual strings
}
populateWordsArray(N);
for(i=0; i<N; i++)
{
free(wordList[i]);
}
free(wordList);
fclose(file);
return 0;
}
I'd also advise against using global variables here.
EDIT: As the comments may suggest, this code is not the best solution. First, all the words might not fit into a 100 byte buffer. To alleviate this issue, allocate a large, fixed-size buffer, read every word into it, then allocate corresponding number of bytes for wordList[i] (don't forget the terminating null byte) and copy the data from the fixed-size buffer into wordList[i].
Also, the code has some missing error checks. For instance, the file may exist but is empty, in which case fscanf(file,"%d",&N); will return EOF. Also, the number at the beginning of the file may not be corresponding to the number of the lines that follow or N might be a negative number (the code allows for it by specifying it to be int).
EDIT2: As #bruno suggested, I made a version that I think is more bulletproof than the previous one. It's possible that I omitted something, I'm in a bit of a hurry. If so, let me know below.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_STRING_LENGTH 512
char line_buffer[MAX_STRING_LENGTH]; // A line of the maximum size that can occur.
char** populateWordsArray(unsigned wantedLines, FILE* file, unsigned *readLines);
char** populateWordsArray(unsigned wantedLines, FILE* file, unsigned *readLines)
{
*readLines=0;
char** wordList;
// Allocating space for pointers
wordList = malloc(wantedLines * sizeof(char*));
if(!wordList)
{
fprintf(stderr,"Cannot allocate sufficient space for the pointers.\n");
exit(EXIT_FAILURE); // You may return NULL here and check it afterwards. The same goes for all the error checking inside this function
}
while(*readLines < wantedLines && fscanf(file,"%s", line_buffer) == 1)
{
wordList[*readLines] = malloc(strlen(line_buffer)+1);
if(!wordList[*readLines])
break;
if(NULL == (wordList[*readLines]=strdup(line_buffer)))
break;
(*readLines)++;
}
return wordList;
}
int main(int argc,char *argv[])
{
unsigned N = 0, i, M;
char **wordList;
FILE *file;
file = fopen(argv[1],"r"); // Indexing starts from 0 in C. Thus, 0th argument is the executable name and 1st is what you want.
if(file == NULL) // No need to cast NULL into a specific type.
{
fprintf(stderr,"Cannot open file\n");
return 1; // You might want to end the program here, possibly with non-zero return value.
}
if(fscanf(file,"%d",&N) != 1)
{
fprintf(stderr,"Cannot read the number of lines. Empty file?\n");
return 1;
}
wordList = populateWordsArray(N, file, &M);
printf("Printing the read lines:\n");
for(i=0; i<M; i++)
{
printf("%s\n", wordList[i]);
}
for(i=0; i<M; i++)
{
free(wordList[i]);
}
free(wordList);
fclose(file);
return 0;
}

Data entry into array of character pointers in C

this is my first question asked on here so if I'm not following the formatting rules here please forgive me. I am writing a program in C which requires me to read a few lines from a file. I am attempting to put each line into a cstring. I have declared a 2D character array called buf which is to hold each of the 5 lines from the file. The relevant code is shown below
#include <stdlib.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/un.h> /* UNIX domain header */
void FillBuffersForSender();
char buf[5][2000]; //Buffer for 5 frames of output
int main()
{
FillBuffersForSender();
return 0;
}
void FillBuffersForSender(){
FILE *fp;
int line = 0;
char* temp = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("frames.txt", "r");
printf("At the beginning of Fill Buffers loop.\n");
//while ((read = getline(&temp, &len, fp)) != -1){
while(line < 5){
//fprintf(stderr, "Read in: %s\n", temp);
fgets(temp, 2000, fp);
strcpy(buf[line], temp);
line++;
fprintf(stderr, "Line contains: %s.\n", temp);
temp = NULL;
}
while(line != 0){
fprintf(stderr, "Line contains: %s.\n", buf[line]);
line--;
}
}
The line
strcpy(buf[line], temp);
is causing a segmentation fault. I have tried this numerous ways, and cannot seem to get it to work. I am not used to C, but have been tasked with writing a bidirectional sliding window protocol in it. I keep having problems with super basic issues like this! If this were in C++, I'd be done already. Any help anyone could provide would be incredible. Thank you.
temp needs to point to an allocated buffer that fgets can write into.
In C programming, error checking is an important part of every program (in fact sometimes it seems like there's more error handling code than functional code). The code should check the return value from every function to make sure that it worked, e.g. if fopen returns NULL then it wasn't able to open the file, likewise if fgets returns NULL it wasn't able to read a line.
Also, the code needs to clean up after itself. For example, there is no destructor that closes a file when the file pointer goes out of scope, so the code needs to call fclose explicitly to close the file when it's finished with the file.
Finally, note that many of the C library functions have quirks that need to be understood, and properly handled. You can learn about these quirks by reading the man pages for the functions. For example, the fgets function will leave the newline character \n at the end of each line that it reads. But the last line of a file may not have a newline character. So when using fgets, it's good practice to strip the newline.
With all that in mind, the code should look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLINE 5
#define MAXLENGTH 2000
static char buffer[MAXLINE][MAXLENGTH];
void FillBufferForSender(void)
{
char *filename = "frames.txt";
FILE *fp;
if ((fp = fopen(filename, "r")) == NULL)
{
printf("file '%s' does not exist\n", filename);
exit(1);
}
for (int i = 0; i < MAXLINE; i++)
{
// read a line
if (fgets( buffer[i], MAXLENGTH, fp ) == NULL)
{
printf("file does not have %d lines\n", MAXLINE);
exit(1);
}
// strip the newline, if any
size_t newline = strcspn(buffer[i], "\n");
buffer[i][newline] = '\0';
}
fclose(fp);
}
int main(void)
{
FillBufferForSender();
for (int i = 0; i < MAXLINE; i++)
printf("%s\n", buffer[i]);
}
Note: for an explanation of how strcspn is used to strip the newline, see this answer.
When it comes to C you have to think of the memory. Where is the memory for a point with NULL assigned to it? How can we copy something to somewhere that we have no space for?

Difficulty using malloc and scanf

I have difficulty using malloc and fscanf.
I just want to read a file and print out the result using
I got a segmentation fault error when I executed this code.
I am not sure what I have done wrong. I would be very grateful if someone points out a fix.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
char* buffer = (char*)malloc(*argv[1]); // Allocate memory
if(buffer=NULL) // if *argv[1] is not existed, exit the program
exit(1);
int n = 0;
FILE* fp=fopen("file.txt","r"); // Open the file
do {
buffer[n] =fscanf(fp,"%c",buffer); // read from file until it gets EOF
} while(buffer[n] != EOF);
fclose(fp); // Close the file
printf("%s",buffer); // Print the stored string
free(buffer); // Return the memory
return 0;
}
Got it. This:
if(buffer=NULL)
should be this:
if(buffer==NULL)
You're setting buffer to NULL. I'm sure you can see what happens next.
More generally, this code is trying to do several things, and it's full of bugs. You should have tested the different functions separately and worked out those bugs along the way.
This here seems wrong:
char* buffer = (char*)malloc(*argv[1]);
The command line argument is a string, but you want a number. You have to convert the string to a number first.
Another problem: In your loop n is never increased, which is why only the first byte of the buffer is written.
Please find the fixed code and the comments inline:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
// Add the check if *argv[1] does not exist, exit the program
long mem_sz=strtol(argv[1],NULL,10);//Safer to atoi
char* buffer = (char*)malloc(mem_sz); // properly allocate memory
//You missed the == in the NULL check.
if(buffer==NULL)
exit(1);
int n = 0;
FILE* fp=fopen("file.txt","r"); // Open the file
if (fp == NULL)//Check fp too
exit(1);
do
{
buffer[n++]=fscanf(fp,"%c",buffer);
} // read from file until it gets EOF and n++
while(buffer[n-1] != EOF);//Check the last read character
buffer[n]=0;//Put an end of string, so that printf later will work correct
fclose(fp); // Close the file
printf("%s\n",buffer); // Print the stored string
free(buffer); // Return the memory
return 0;
}

Resources