File I/O in C Segmentation Fault - c

I know what a segmentation fault is, i don't need to know its definition :)
I just need to know where it's coming from in my code. This program is meant to get words as input, read from a text file, write to a separate text file and then print all the words from the read file and the input.
#include<stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char*argv[]){
FILE *read;
FILE *write;
char **list=malloc(sizeof(char*));
char **oldlist=malloc(sizeof(char*));
char *oldword=malloc(sizeof(char));
char exit[]="end";
int a, c, r=0, w=0, n=0, z= 0, y=0, d=0, g=0;
//check arg
for(a=0; a<argc; a++){
if(strcmp(argv[a], "-r")==0){
r =1;
read=fopen("read.txt", "r");
}
else if(strcmp(argv[a], "-w")==0){
w =1;
write=fopen("write.txt", "w");
}
}
if(r==0 && w==0){
printf("Error: Invalid Command.\n");
}
printf("Read = %d | Write = %d\n", r, w);
//getwords
printf("Enter your words: ");
while(1){
char *word=malloc(sizeof(char));
list=realloc(list, sizeof(char*)*(z+10));
word=realloc(word, sizeof(char)*(z+10));
scanf("%s", word);
if (strcmp(word,exit)==0){
break;
}
else{
*(list+z) = word;
z++;
}
}
//read
if (r==1){
do{
while(1){
*(oldword+d)=fgetc(read);
d++;
}
}while(feof(read) != 0);
}
*(oldword+(d-1))="\0";
printf("Your words are:\n");
puts(oldword);
for(c=0; c<n; c++){
puts(*(list+c));
}
//write
if (w ==1){
if(w==1){
fputs(*(oldlist+c),write);
}
for(c=0; c<n; c++){
fputs(*(list+c),write);
}
}
//end
free(list);
fclose(read);
fclose(write);
return 0;
}

You allocate 1 byte for the word:
char *word=malloc(sizeof(char));
You then read a string into it. This is a buffer overflow, and leads to great unhappiness (and questions like this on Stack Overflow). Specifically, reading long words will trample over control information in the 'heap' (the data space controlled by malloc() et al) and scramble data that is used to determine which space is available for use and which is not. What constitutes a 'long word' depends on the system; technically, any string except the empty string (just a terminal '\0') is too long, but you might get away with blue murder if the words are 7 bytes or less.
But shouldn't the realloc take care of the memory problem? I'm trying to make the string input unlimited.
Hmmm...it's a little odd as a way of doing business, but the realloc() of word before you actually use it gets you around some of the problem.
Specifically, you can read up to and including 9 characters before you overflow the memory allocation of word on the first time around the loop. However, that is a long way from making it 'unlimited', and making it unlimited is non-trivial. One issue is that %s stops scanning at a white space characters; that works in your favour, on the whole. Another is that you seem to be using z as a count of the number of strings you've entered and also as a length for the strings you can enter. You are not reallocating list if the list grows beyond 10 entries.
You can still run into various problems. Your handling of oldword doesn't do the extra realloc(), for example; it just allocates one byte. Then you have an infinite loop that is completely unbounded (if it is entered at all). This loop is a disaster:
while(1){
*(oldword+d)=fgetc(read);
d++;
}

If you compile with debugging on (the g flag) and run under valgrind it should give you a pretty good indication of why it seg faults.

Related

Storing String Inside a String?

My problem is when I try to save the string (series[0]) Inside (c[0])
and I display it, it always ignore the last digit.
For Example the value of (series[0]) = "1-620"
So I save this value inside (c[0])
and ask the program to display (c[0]), it displays "1-62" and ignores the last digit which is "0". How can I solve this?
This is my code:
#include <stdio.h>
int main(void)
{
int price[20],i=0,comic,j=0;
char name,id,book[20],els[20],*series[20],*c[20];
FILE *rent= fopen("read.txt","r");
while(!feof(rent))
{
fscanf(rent,"%s%s%s%d",&book[i],&els[i],&series[i],&price[i]);
printf("1.%s %s %s %d",&book[i],&els[i],&series[i],price[i]);
i++;
}
c[0]=series[0];
printf("\n%s",&c[0]);
return 0;
}
The use of fscanf and printf is wrong :
fscanf(rent,"%s%s%s%d",&book[i],&els[i],&series[i],&price[i]);
Should be:
fscanf(rent,"%c%c%s%d",&book[i],&els[i],series[i],&price[i]);
You have used the reference operator on a char pointer when scanf expecting a char pointer, also you read a string to book and else instead of one character.
printf("1.%s %s %s %d",&book[i],&els[i],&series[i],price[i]);
Should be:
printf("1.%c %c %s %d",book[i],els[i],series[i],price[i]);
And:
printf("\n%s",&c[0]);
Should be:
printf("\n%s",c[0]);
c is an array of char * so c[i] can point to a string and that is what you want to send to printf function.
*Keep in mind that you have to allocate (using malloc) a place in memory for all the strings you read before sending them to scanf:
e.g:
c[0] = (char*)malloc(sizeof(char)*lengthOfString+1);
and only after this you can read characters in to it.
or you can use a fixed size double character array:
c[10][20];
Now c is an array of 20 strings that can be up to 9 characters long.
Amongst other problems, at the end you have:
printf("\n%s",&c[0]);
There are multiple problems there. The serious one is that c[0] is a char *, so you're passing the address of a char * — a char ** — to printf() but the %s format expects a char *. The minor problem is that you should terminate lines of output with newline.
In general, you have a mess with your memory allocation. You haven't allocated space for char *series[20] pointers to point at, so you get undefined behaviour when you use it.
You need to make sure you've allocated enough space to store the data, and it is fairly clear that you have not done that. One minor difficulty is working out what the data looks like, but it seems to be a series of lines each with 3 words and 1 number. This code does that job a bit more reliably:
#include <stdio.h>
int main(void)
{
int price[20];
int i;
char book[20][32];
char els[20][32];
char series[20][20];
const char filename[] = "read.txt";
FILE *rent = fopen(filename, "r");
if (rent == 0)
{
fprintf(stderr, "Failed to open file '%s' for reading\n", filename);
return 1;
}
for (i = 0; i < 20; i++)
{
if (fscanf(rent, "%31s%31s%19s%d", book[i], els[i], series[i], &price[i]) != 4)
break;
printf("%d. %s %s %s %d\n", i, book[i], els[i], series[i], price[i]);
}
printf("%d titles read\n", i);
fclose(rent);
return 0;
}
There are endless ways this could be tweaked, but as written, it ensures no overflow of the buffers (by the counting loop and input conversion specifications including the length), detects when there is an I/O problem or EOF, and prints data with newlines at the end of the line. It checks and reports if it fails to open the file (including the name of the file — very important when the name isn't hard-coded and a good idea even when it is), and closes the file before exiting.
Since you didn't provide any data, I created some random data:
Tixrpsywuqpgdyc Yeiasuldknhxkghfpgvl 1-967 8944
Guxmuvtadlggwjvpwqpu Sosnaqwvrbvud 1-595 3536
Supdaltswctxrbaodmerben Oedxjwnwxlcvpwgwfiopmpavseirb 1-220 9698
Hujpaffaocnr Teagmuethvinxxvs 1-917 9742
Daojgyzfjwzvqjrpgp Vigudvipdlbjkqjm 1-424 4206
Sebuhzgsqpyidpquzjxswbccqbruqf Vuhssjvcjjylcevcisdzedkzlp 1-581 3451
Doeraxdmyqcbbzyp Litbetmttcgfldbhqqfdxqi 1-221 2485
Raqqctfdlhrmhtzusntvgbvotpk Iowdcqlwgljwlfvwhfmw 1-367 3505
Kooqkvabwemxoocjfaa Hicgkztiqvqdjjx 1-466 435
Lowywyzzkkrazfyjuggidsqfvzzqb Qiginniroivqymgseushahzlrywe 1-704 5514
The output from the code above on that data is:
0. Tixrpsywuqpgdyc Yeiasuldknhxkghfpgvl 1-967 8944
1. Guxmuvtadlggwjvpwqpu Sosnaqwvrbvud 1-595 3536
2. Supdaltswctxrbaodmerben Oedxjwnwxlcvpwgwfiopmpavseirb 1-220 9698
3. Hujpaffaocnr Teagmuethvinxxvs 1-917 9742
4. Daojgyzfjwzvqjrpgp Vigudvipdlbjkqjm 1-424 4206
5. Sebuhzgsqpyidpquzjxswbccqbruqf Vuhssjvcjjylcevcisdzedkzlp 1-581 3451
6. Doeraxdmyqcbbzyp Litbetmttcgfldbhqqfdxqi 1-221 2485
7. Raqqctfdlhrmhtzusntvgbvotpk Iowdcqlwgljwlfvwhfmw 1-367 3505
8. Kooqkvabwemxoocjfaa Hicgkztiqvqdjjx 1-466 435
9. Lowywyzzkkrazfyjuggidsqfvzzqb Qiginniroivqymgseushahzlrywe 1-704 5514
10 titles read

Reading line by line C

I have a txt file with some file names and their size.
This is how I wrote the txt file:
banana //file name
3 //the size of file banana
programs
12
music
524
I have to find a keyboard entered file name and display it's size.
This is my code:
FILE *text;
text=fopen("text.txt","r");
printf("Scan the number of letters of your file name");
int n;
scanf("%d",&n);
char s[++n];
printf("Scan the file name you are looking for: ");
int i;
for(i=0;i<=n;i++)
{
scanf("%c",&s[i]);
}
int l=0;
char c[n];
char g;
while(!feof(text))
{
if(l%2==1) {fgetc(text); fgetc(text); l++;}
if(l%2==0)
{
fgets(c,n,text);
fgetc(text);
for(i=0;i<n;i++)
{
printf("%c",c[i]);
}
l++;
}
}
Obviously, it's not correct. Can you help me? I'm a little bit confuse.
Ugh! Please learn more about basic input. Your program has various flaws:
fgetc reads single characters. This can be useful at times, but obviously you want to read whole lines. fgets does this. You use it once, but it is not advisable to mix these. Decide up front which input paradigm you want to use: char-wise (fgetc), line-wise (fgets) or token-wise (fscanf).
Please don't make the user enter the number of characters in the filename. Quick, how many characters are there in MySpiffyDocument.txt? That's work that the computer should do.
Don't use feof to control yopur input. All input functions have special return values toat indicate that either the end of the file was read or that an error occurred. For fgets, this return value is NULL, for fgetc, this return value is the special constant EOF. The functions feof and ferror are useful after you have encountered the special return values for a post mortem analysis of the two end conditions.
Your inner loop, which is responsible for the core program logic, doesn't make sense at all. For example, for an odd l, increment l and then test for an even l – which will be true, because you have just incrremented an odd l. Use else in such cases. And don't place things that happen anyway in conditional blocks: Increment l once after the if/else blocks.
Here's an example implementation:
#include <stdlib.h>
#include <stdio.h>
int process(const char *filename)
{
char line[80];
char name[80];
int size;
int count = 0;
FILE *f = fopen(filename, "r");
if (f == NULL) return -1;
while (fgets(line, sizeof(line), f)) {
if (count % 2 == 0) {
if (sscanf(line, "%s", name) < 1) continue;
} else {
if (sscanf(line, "%d", &size) < 1) continue;
printf("%12d %s\n", size, name);
}
count++;
}
fclose(f);
return 0;
}
int main()
{
char line[80];
char name[80];
puts("Please enter filename:");
while (fgets(line, sizeof(line), stdin)) {
if (sscanf(line, "%s", name) == 1) {
process(name);
break;
}
}
return 0;
}
Things to note:
The program uses 80 characters a max. buffer size; that means your lines can be up to 78 characters long – line content plus new-line '\n' plus null terminator '\0'. That should be okay for many cases, but eventually the line may overflow. (So your file-name letter count has some merit, but the real solution here is to allocate memory dynamically. I won't open that can of worms now.)
The code uses a double strategy: Read lines first, then scan into these lines with sscanf, so that only the first word on each line is read.
Empty lines are skipped. Even lines that don't hold a valid number are skipped, too. This is sloppy error handling and may trip the odd/even count.
Reading stuff interactively from the keyboard isn't very easy in C. The awkward fgets/sscanf construct in main tries to handle the case when the user enters an empty line or evokes an end-of-file signal via Ctrl-D/Z. A better and easier way is to provide arguments to the command line via argc and argv.
I've moved the file reading into a separate function.

Reading big numbers such as e and pi from files to arrays in C

I have a file called pi.txt which contains, as you probably imagine, the numbers of pi.
The format goes like this
Line 1: 3.
Line 2: 14159265358979323846264338327950288419716939937510
Line 3: 58209749445923078164062862089986280348253421170679
and it continues to
Line 20001: 56787961303311646283996346460422090106105779458151
There are 1020001 digits minus the 3 and the .(of 3.14xxxxxx...)
I have to read this file to an array (not a 2D one. What I want to do later will become much harder if I use a 2D array as I imagine it). This is my code:
void fpi();
char **arraypi;
int main(int argc, char *argv[]) {
fpi();
int i;
while (i<=10){
printf("%d", arraypi[i++]);}
return 0;
}
void fpi(){
char pi[1020001];
arraypi = malloc(1020001 * sizeof(int));
FILE *file;
int i=0;
file = fopen("pi.txt", "r");
if (file == NULL){
printf("The file cannot be opened.\n");
exit(1);
}
while (fscanf(file, "%c", &pi)==1){
strcpy(arraypi[i++], pi);
}
fclose(file);
}
I get a segmentation fault and I can't figure out why. I'm sure it has to do with the pointers I'm using and fscanf.
strcpy expects you to give a null terminated character array (string) and you're giving an array of 1020001 characters where except the first character, which is read from the file, everything else is undetermined.
char pi[1020001];
while (fscanf(file, "%c", &pi)==1){ // only one character gets copied to pi
strcpy(arraypi[i++], pi); // strcpy will expect pi to be a string and not just a char
}
Hence it may go on an infinite loop and write beyond the allowed memory allocated for it, thereby corrupting the stack structure and thus leading to the segmentation fault. You're also treading on undefined behaviour when you're doing this.

calculate mean length of a line using C

I have a text file which I have filled a number lines from different texts, having different line length.
What I want to do is calculate the average number characters per line which matters to me in my job. I wrote the following code in C to achieve this. However I cannot run the program once it is compiled.
#include<stdio.h>
#include<stdlib.h>
#define LENGTH 10000000
int main()
{
char c;
int i;
int line_length;
int j;
int char_count;
char char_a[LENGTH];
int line_a[LENGTH];
int line_count;
long sum;
float avg_char_count;
FILE *fp=fopen("input.txt","r");
if(!fp){
fprintf(stderr,"cannot open file");
exit(1);
}
/*read into file*/
i=0;
sum=0;
while(char_a[i++]=fgetc(fp))
sum++;
printf("chars count: %d \n",sum);
/*process array*/
char_count=i;
j=0;
line_count=0;
while(j++<char_count){
if(char_a[j]=='\n'){
sum--;
line_count++;
}
}
/* calculate the average*/
avg_char_count=sum/(float)line_count;
printf("\naverage # of chars in a line is: %f\n ",avg_char_count);
return EXIT_SUCCESS;
}
By the way I am using Borland C++ command-line tool BCC32, running on Windows 7 SP1.
What's wrong with my code?
Try declaring char_a and line_a as pointers to char and int as:
char *char_a;
int *line_a;
And then allocate memory dynamically using malloc.
char_a=(char*)malloc(10000000*sizeof(char));
line_a=(int*)malloc(10000000*sizeof(int));
Secondly, your while loop should end when you reach end of file, i.e. EOF.
while(char_a[i]=fgetc(fp)){
if(char_a[i++]==EOF)
break;
sum++;
}
And, you should initialize the line_count to 1 instead of 0, because when there is no '\n' in the text file, there can still be one line. If there is one '\n' in the text file, it means there are two lines (say, you are in line 1, and then you hit enter, which is '\n', and then you get to the new line, so for one '\n', there are 2 lines).
/*process array*/
char_count=sum;
j=0;
line_count=1;
while(j++<char_count){
if(char_a[j]=='\n'){
sum--;
line_count++;
}
}
NOTE-Currently your char_count include newlines ('\n') when it is printed. Print the statement in the end, because in the end of your program, you have already excluded the newlines by decrementing the sum in the if statement of the second while loop.
The most probable cause is that you allocate 20 Mb of variables on the stack.
I would change the program so that it reads the file on line at a time (or even one character at a time).
That way you only need to allocate space for one line and not for the entire file.

How to correctly input a string in C

I am currently learning C, and so I wanted to make a program that asks the user to input a string and to output the number of characters that were entered, the code compiles fine, when I enter just 1 character it does fine, but when I enter 2 or more characters, no matter what number of character I enter, it will always say there is just one character and crashes after that. This is my code and I can't figure out what is wrong.
int main(void)
{
int siz;
char i[] = "";
printf("Enter a string.\n");
scanf("%s", i);
siz = sizeof(i)/sizeof(char);
printf("%d", siz);
getch();
return 0;
}
I am currently learning to program, so if there is a way to do it using the same scanf() function I will appreciate that since I haven't learned how to use any other function and probably won't understand how it works.
Please, FORGET that scanf exists. The problem you are running into, whilst caused mostly by your understandable inexperience, will continue to BITE you even when you have experience - until you stop.
Here is why:
scanf will read the input, and put the result in the char buffer you provided. However, it will make no check to make sure there is enough space. If it needs more space than you provided, it will overwrite other memory locations - often with disastrous consequences.
A safer method uses fgets - this is a function that does broadly the same thing as scanf, but it will only read in as many characters as you created space for (or: as you say you created space for).
Other observation: sizeof can only evaluate the size known at compile time : the number of bytes taken by a primitive type (int, double, etc) or size of a fixed array (like int i[100];). It cannot be used to determine the size during the program (if the "size" is a thing that changes).
Your program would look like this:
#include <stdio.h>
#include <string.h>
#define BUFLEN 100 // your buffer length
int main(void) // <<< for correctness, include 'void'
{
int siz;
char i[BUFLEN]; // <<< now you have space for a 99 character string plus the '\0'
printf("Enter a string.\n");
fgets(i, BUFLEN, stdin); // read the input, copy the first BUFLEN characters to i
siz = sizeof(i)/sizeof(char); // it turns out that this will give you the answer BUFLEN
// probably not what you wanted. 'sizeof' gives size of array in
// this case, not size of string
// also not
siz = strlen(i) - 1; // strlen is a function that is declared in string.h
// it produces the string length
// subtract 1 if you don't want to count \n
printf("The string length is %d\n", siz); // don't just print the number, say what it is
// and end with a newline: \n
printf("hit <return> to exit program\n"); // tell user what to do next!
getc(stdin);
return 0;
}
I hope this helps.
update you asked the reasonable follow-up question: "how do I know the string was too long".
See this code snippet for inspiration:
#include <stdio.h>
#include <string.h>
#define N 50
int main(void) {
char a[N];
char *b;
printf("enter a string:\n");
b = fgets(a, N, stdin);
if(b == NULL) {
printf("an error occurred reading input!\n"); // can't think how this would happen...
return 0;
}
if (strlen(a) == N-1 && a[N-2] != '\n') { // used all space, didn't get to end of line
printf("string is too long!\n");
}
else {
printf("The string is %s which is %d characters long\n", a, strlen(a)-1); // all went according to plan
}
}
Remember that when you have space for N characters, the last character (at location N-1) must be a '\0' and since fgets includes the '\n' the largest string you can input is really N-2 characters long.
This line:
char i[] = "";
is equivalent to:
char i[1] = {'\0'};
The array i has only one element, the program crashes because of buffer overflow.
I suggest you using fgets() to replace scanf() like this:
#include <stdio.h>
#define MAX_LEN 1024
int main(void)
{
char line[MAX_LEN];
if (fgets(line, sizeof(line), stdin) != NULL)
printf("%zu\n", strlen(line) - 1);
return 0;
}
The length is decremented by 1 because fgets() would store the new line character at the end.
The problem is here:
char i[] = "";
You are essentially creating a char array with a size of 1 due to setting it equal to "";
Instead, use a buffer with a larger size:
char i[128]; /* You can also malloc space if you desire. */
scanf("%s", i);
See the link below to a similar question if you want to include spaces in your input string. There is also some good input there regarding scanf alternatives.
How do you allow spaces to be entered using scanf?
That's because char i[] = ""; is actually an one element array.
Strings in C are stored as the text which ends with \0 (char of value 0). You should use bigger buffer as others said, for example:
char i[100];
scanf("%s", i);
Then, when calculating length of this string you need to search for the \0 char.
int length = 0;
while (i[length] != '\0')
{
length++;
}
After running this code length contains length of the specified input.
You need to allocate space where it will put the input data. In your program, you can allocate space like:
char i[] = " ";
Which will be ok. But, using malloc is better. Check out the man pages.

Resources