fgets inside of a For loop causing strange behavior - c

I'm trying to get user input using fgets and some funky (not correct) things are happening and I can't seem to understand why.
The program is run with an argument that indicates how many values the user is to input.
Here is how the program is supposed to run:
./a.out 6
Enter 6 integer values to place in tree:
5
4
3
2
1
6
Input values:
5
4
3
2
1
6
If I have 1 as the argument, it doesn't even allow me to enter an input, and where did the 0 come from?
./a.out 1
Enter 1 integer values to place in tree:
Input values:
0
If I have 2 as the argument, it only allows me to enter 1 input and the phantom 0 appears again.
./a.out 2
Enter 2 integer values to place in tree:
1
Input values:
1
0
If I have 3 or more arguments, it functions correctly.
Here's the source:
int main (int argc, const char* argv[]){
int numIntegers;
char buffer[20];
if (argc == 1){
printf("Usage: a.out #\n");
return EXIT_FAILURE;
}
else{
numIntegers = atoi(argv[1]);
if (numIntegers <= 0){
printf("# must be greater than 0\n");
return EXIT_FAILURE;
}
else{
int intArray[numIntegers];
printf("Enter %d integer values to place in tree: \n", numIntegers);
for (int i = 0; i < numIntegers; i++){
fgets(buffer, numIntegers, stdin);
intArray[i] = atoi(buffer);
}
printf("Input values:\n");
for (int i = 0; i < numIntegers; i++){
printf(%d\n", intArray[i]);
}
}
}
}//end main

The size argument to fgets() refers to the size of buffer, which should be 20 in your case.
fgets(buffer, sizeof(buffer), stdin);
By the way, your code won't actually compile.
printf(%d\n", intArray[i]); // missing a quotation mark

Related

C: Reading multiple values with scanf and saving them into an array

I found an answer to the first part of my question (how to read multiple values with scanf) but it doesn't seem to work for me (I think it's because of putting the values into an array and maybe also because I'm checking if the values given are 6 ints for sure):
I am writing a program that stores co-ordinates of 4 triangles in an array. Each line has 6 values and stores co-ordinates of one triangle. I want to read 6 co-ordinates at one time and do this operation for 4 triangles separately.
int tab[4][6];
for (int i = 0; i < 4; i++){
while (scanf("%d %d %d %d %d %d", &tab[i][0], &tab[i][1], &tab[i][2], &tab[i][3], &tab[i][4], &tab[i][5]) != 6){
printf("Error, try again: ");
while (getchar() != '\n'){}
}
}
So for example if first triangle's co-ordinates are (2,1), (5,6), (2,7), then I want to type in: "2 1 5 6 2 7" and as a result I want it to fill the first line of the array with the said numbers in the order I typed them in.
Obviously it doesn't work, the program stops working (not finishes the work, it stops) after the first line is given.
I get this error after debugging (after giving first line):
"Unhandled exception at 0x0FDCC28C (msvcr120d.dll) in xxx.exe: 0xC0000005: Access violation writing location 0xCCCCCCCC."
How to fix it?
You need to subtract the pointer i when detecting input error like this for example ->
#include <stdio.h>
int main(int argc, const char * argv[]) {
int tab[4][6];
for (int i = 0; i < 4; i++){
printf("Enter 6 values \n");
int retVal=scanf("%d %d %d %d %d %d", &tab[i][0], &tab[i][1], &tab[i][2], &tab[i][3], &tab[i][4], &tab[i][5]);
if (retVal == 6) {
printf("You did enter -> %d %d %d %d %d %d\n",tab[i][0],tab[i][1],tab[i][2],tab[i][3],tab[i][4],tab[i][5]);
} else {
printf("Error entering values.. (Enter numbers). \n");
while (getchar() != '\n'){}
i--;
}
}
return 0;
}
Unclear why OP's code failed without posting input used and prior code.
How to fix it?
Use fgets() to read a line of user input. Avoid mixing scanf() with fgets() in prior code. Then parse the buffer. Use " %n" at the end to look for success and extra text.
int tab[4][6];
char buf[6*12 * 2]; // Use a buffer twice expected max needs
for (int i = 0; i < 4; i++) {
while (1) {
if (fgets(buf, size buf, stdin) == NULL) {
return "Failed to read enough data"; // Handle end-of-file in some fashion
}
int n = 0;
sscanf(buf, "%d%d%d%d%d%d %n",
&tab[i][0], &tab[i][1], &tab[i][2], &tab[i][3], &tab[i][4], &tab[i][5], &n);
if (n > 0 && buf[n] == 0) {
break; // Success!
}
printf("Error - bad input, try again: ");
}
}

Organizing inputs using a struct - C programming

I am working on making a program that will take in an input parameter 'N' using argv. The value N will then allow the user to enter in N value of lines about a chemical element. For example, one line would look like
1 Hydrogen H other_nonmetals 1.008 1 0 0 0 0 0 0
and using a struct, I will scan and print the input in an organized matter.
I am having two main problems currently. The first problem is scanning in the electron values. In the Hydrogen example above, I would need to scan in 1 0 0 0 0 0 0 and reprint it out in my function print_element. When I do so, only the value 1 is stored. I want all the zeros to be omitted, but if the electron values were 1 0 0 0 0 0 1, then only the 1 would be printed in my function.
The next problem I am having is in my for loops. While looping the function print_element, an extra element with no values will be printed. For example, if the user inputs values for Hydrogen and then Barium, it will print Hydrogen then a completely blank element with all zeros, and then Barium. I cannot figure out how to get rid of the blank element.
#include <stdio.h>
#include <stdlib.h>
#define MAX_ELEMENTS 20
typedef struct{
int num;
char name[MAX_ELEMENTS];
char symbol[MAX_ELEMENTS];
char class[MAX_ELEMENTS];
double weight;
char electrons[MAX_ELEMENTS];
} element_t;
void scan_element(element_t *uno){
scanf("%d %s %s %s %lf %20s", &uno->num, uno->name, uno->symbol, uno->class, &uno->weight, uno->electrons);
}
void print_element(element_t uno){
printf("---------------\n| %d\t%.4f\n| %s\t%s\n| %s\n---------------\n", uno.num, uno.weight, uno.symbol, uno.name, uno.electrons);
}
int main (int argc, char *argv[]){
int i;
if (argc != 2){
printf("ERROR: You must provide exactly one argument to this program.\n");
return 0; }
int N = (int)strtol(argv[1], NULL, 10);
if(N <= 0){
printf("ERROR: Your must provide an integer greater than 0 and less than or equal to 20.\n");
return 0; }
element_t uno[MAX_ELEMENTS];
for(i=0; i<=argc; i++){
scan_element(&uno[i]); }
printf("%d total elements.\n", N);
printf(" had the smallest atomic number.\n");
printf(" had the largest atomic number.\n");
for(i=0; i<=argc; i++){
print_element(uno[i]); }
return 0;
}
Replace for(i=0; i<=argc; i++) with for(i=0;i<N;i++).
To omit the first 0 and everything after it in electrons, add
char*tmp;
if(tmp=strstr(uno->electrons," 0"))
*tmp=0;
to scan_element.
It is faster if you only pass the pointer in print_element, because 4 or 8 bytes will be copied instead of 92.
It is not good to use your current way to get string from input. See How to prevent scanf causing a buffer overflow in C?.

C - scanf has gone ROGUE

I have this c program where I am inputing a number N followed by N more numbers. For example, I'll enter 100 followed by 100 more numbers. For some reason, after so many inputs the scanf function will stop working properly. It's as if it has stopped taking input and will just continue one with whatever value is in size.
The use case I came up with is 100 1 2 3 4 5 6 7 8 9 10... (repeated ten times). then after three or four times of that I'll type in 100 10 9 8 7 6 5 4 3 2 1... (repeated ten times) and then there will be an infinite loop of print statements.
int main(int argc, const char * argv[]) {
int histogram[10000];
int i;
while (1) {
int *rectPtr = histogram;
int size;
scanf("%d", &size);
if (!size) return 0;
for (i = 0; i < size; ++i) {
scanf("%d", rectPtr);
rectPtr++;
}
printf("%d", 1);
printf("\n");
}
return 0;
}
Distrust infinite loops.
In a series of comments, I said:
You're not testing the return value from scanf(), so you don't know whether it is working. The pair of printf() statements is odd; why not write printf("%d\n", 1); or even puts("1");?
Your code does not test or capture the return value from scanf(), so you do not know whether scanf() is reporting a problem. As a general rule, test the return value of input functions to make sure what you thought happened did in fact happen. You could also print out the values read just after you read them:
if (scanf("%d", rectPtr) != 1)
{
fprintf(stderr, "scanf() failed\n");
return 1;
}
printf("--> %d\n", *rectPtr);
rectPtr++;
Similarly when inputting size. Also consider if (size <= 0) return 0;. And using fgets() plus `sscanf() can make reporting errors easier.
j.will commented:
It is great to know if scanf fails, but I want to know why it fails and prevent it from failing. How do I do that?
I responded:
I understand you'd like to know. With scanf(), the best you can do after a failure is usually to read all the characters that follow up to a newline or EOF, and if you want to know what went wrong, then you print those characters too, because scanf() leaves the last character that it read in the input buffer ready for the next input operation.
void gobble(void)
{
printf("Error at: <<");
int c;
while ((c = getchar()) != EOF && c != '\n')
putchar(c);
puts(">>");
if (c == EOF)
puts("<<EOF>>");
}
The first character in the output is what caused the failure.
See also How to use sscanf() in loops?
Hacking your code to match this:
#include <stdio.h>
static void gobble(void)
{
printf("Error at: <<");
int c;
while ((c = getchar()) != EOF && c != '\n')
putchar(c);
puts(">>");
if (c == EOF)
puts("<<EOF>>");
}
int main(void)
{
enum { MAX_VALUES = 10000 };
int histogram[MAX_VALUES];
int size;
while (printf("Number of items: ") > 0 && scanf("%d", &size) == 1 &&
size > 0 && size <= MAX_VALUES)
{
int *rectPtr = histogram;
for (int i = 0; i < size; ++i)
{
if (scanf("%d", rectPtr) != 1)
{
gobble();
return 1;
}
rectPtr++;
}
printf("size %d items read\n", size);
}
return 0;
}
IMO, you need to check the return value of scanf() for proper operation. Please check the below code. I have added some modifications.
To exit from the program, you need to press CTRL+ D which will generate the EOF. Alternatively, upon entering some invalid input [like a char instead of int] wiil also cause the program to beak out of while() llop and terminate.
I have put the sequence to check first scanf(). All others need to be checked, too.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
int histogram[10000] = {0};
int i;
int *rectPtr = histogram;
int size = 0;
int retval = 0;
printf("Enter the number of elements \n");
while ( (retval = scanf("%d", &size)) != EOF && (retval == 1)) {
rectPtr = histogram;
if (!size) return 0;
printf("Enter %d elements\n", size);
for (i = 0; i < size; ++i) {
scanf("%d", rectPtr); //check in a simmilar way to above
rectPtr++;
}
printf("%d\n", 1111111);
printf("Enter the number of elements: \n");
}
return 0;
}
The output of a sample run
[sourav#broadsword temp]$ ./a.out
Enter the number of elements: 2
Enter 2 elements
1
2
1111111
Enter the number of elements: 3
Enter 3 elements
1
2
3
1111111
Enter the number of elements: 9
Enter 9 elements
0
9
8
7
6
5
4
3
2
1111111
Enter the number of elements: r
[sourav#broadsword temp]$
histogram is declared to have size 10000. You say you do 100 1 2 3 ... repeated 10 times. If I correctly understand that uses 1000 slots in histogram.
If you repeat the test more than 10 times, you exhaust histogram and begin to write past the end of array causing undefined behaviour.
So you must either :
reset recPtr = histogram at each iteration
control recPtr - histogram + size <= sizeof(histogram) after reading size (IMHO better)
And as other said, you should always control input operations : anything can happen outside of your program ...

fgets causing an infinite for loop in C

It seems my implementation of fgets() is incorrect here, would very much appreciate some extra eyes to look over what I've done!
Here's the code
int main(int argc, const char* argv[]){
int numIntegers;
char buffer[20];
int intArray[10];
//if no argument is passed in, terminate
if (argc == 1){
printf("no argument given, terminating..\n");
return EXIT_FAILURE;
}
else{
numIntegers = atoi(argv[1]);
//we only want numbers greater than 0
if (numIntegers <= 0){
printf("# must be greater than 0\n");
return EXIT_FAILURE;
}
else{
printf("Enter %d integer values to place in array: \n", numIntegers);
for (int i = 0; i < numIntegers; i++){
fgets(buffer, numIntegers, stdin);
intArray[i] = atoi(buffer);
printf("Index is = %d \n", i);
}
}
}
//for (int i =0; i < numIntegers; i++){
// printf("Index[%d] = %d \n", i, intArray[i]);
//}
}
Here's the output, the line with no other text besides an integer is user input. Notice how the value of i resets. The issue only occurs when I give an initial argument of anything more than 10. It turns the for loop into an endless loop, for whatever reason.
$ ./a.out 11
Enter 11 integer values to place in array:
5
Index is = 0
2
Index is = 1
1
Index is = 2
2
Index is = 3
3
Index is = 4
4
Index is = 5
123
Index is = 6
123
Index is = 7
123
Index is = 8
1
Index is = 9
2
Index is = 2
2
Index is = 3
3
Index is = 4
5
Index is = 5
1
Index is = 6
12
Index is = 7
You are using
fgets(buffer, numIntegers, stdin);
The second parameter should be the size of the buffer - in your case, 20. That is at least one obvious problem...
The next problem: you are allowing numIntegers to be greater than 10 - so you will be writing values beyond the end of your intArray. Need to fix that too...
if(numIntegers > 10) {
printf("cannot have number greater than 10!\n");
// abort, retry, ignore...
}
In fact - here is your code, with the bugs ironed out: note the use of defined sizes for BUFSIZE and MAXNUM just so you don't have to change it in multiple places if you change your mind...
#include <stdio.h>
#define BUFSIZE 20
#define MAXNUM 10
#define EXIT_FAILURE 0
int main(int argc, const char* argv[]){
int i, numIntegers;
char buffer[BUFSIZE];
int intArray[MAXNUM];
//if no argument is passed in, terminate
if (argc == 1){
printf("no argument given, terminating..\n");
return EXIT_FAILURE;
}
else{
numIntegers = atoi(argv[1]);
//we only want numbers greater than 0
if (numIntegers <= 0 || numIntegers > MAXNUM){
printf("# must be greater than 0 and less than %d!\n", MAXNUM);
return EXIT_FAILURE;
}
else{
printf("Enter %d integer values to place in array: \n", numIntegers);
for (i = 0; i < numIntegers; i++){
fgets(buffer, BUFSIZE, stdin);
intArray[i] = atoi(buffer);
printf("Index is = %d \n", i);
}
}
}
}
Finally - you may wonder why your integer counter seems to "reset"? Well - your intArray is a block of 10 integers on the stack; and when you declare loop variable i, it occupies the next place in memory (as int intArray[10]; was the last time a variable was declared before you got to the for loop) - which you happen to get to when you "index" to intArray[10] (a memory location you are not allowed to access, but you did anyway). You happened to enter the value 2 - and thus, i was reset to 2...
If you had declared i at the start of the program (as I did, since my compiler doesn't "do" C99 by default - I'm that old!), the problem would have shown up differently - or not at all.

How to read a multi line using fscanf

I want to read my data.txt file which looks like and store it in an array called buffer[i][j]
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
I am writing a code which looks like
#include"stdio.h"
#include"stdlib.h"
int main() {
FILE *fp1;
int i,j;
int buffer[4][4]={0};
fp1 = fopen("exact_enumerated_config_442_cub_mc","r");
for(i=0;i<4;i++) {
for(j=0;j<4;j++) {
fscanf(fp1,"%d", &buffer[i][j]);
}
// fscanf(fp1,"\n");
}
fclose(fp1);
for(i=0;i<4;i++) {
for(j=0;j<4;j++) {
printf("%d ",buffer[i][j]);
}
printf("\n");
}
}
but i get the output...
1 1 2 1
5 1 6 1
17 1 18 1
21 1 22 1
why????
Always check the result of fopen() to ensure the file has been opened.
Always check the result of fscanf() to ensure it was successful and prevent subsequent code processing variables that may not have been assigned a value (it returns the number of assignments made).
Add a leading space character to the format specifier to skip whitespace, including newline characters: " %d".
The code will treat a single line with sixteen ints the same as four lines with four ints. If it is important that the format of the file is four ints per line then read a single line using fgets() and then use sscanf() to extract the ints with the %n format specifier to ensure full buffer was processed:
int ints[4][4] = { { 0 } };
char buffer[1024];
for (int i = 0; i < 4 && fgets(buffer, 1024, fp); i++)
{
int pos;
if (sscanf(buffer,
"%d %d %d %d%n",
&ints[i][0],
&ints[i][1],
&ints[i][2],
&ints[i][3],
&pos) != 4 || pos != strlen(buffer) - 1)
{
fprintf(stderr, "Invalid format: <%s>\n", buffer);
exit(1);
}
}
Add space at the beginning of the string format it should be " %d" to avoid the newline problems
fscanf(fp1," %d", &buffer[i][j]);
BTW you could use the following code instead
for(i=0;i<4;i++) {
fscanf(fp1," %d %d %d %d", &buffer[i][0], &buffer[i][1], &buffer[i][2], &buffer[i][3]);
}
To get required output (1111,2222,...) change:
fp1 = fopen("exact_enumerated_config_442_cub_mc","r");
to:
fp1 = fopen("data.txt","r");
Clarification: when using fopen you should write the name of file you want to read. In you case you have to write data.txt, not exact_enumerated_config_442_cub_mc...
There is no file with this name, moreover there is no any data like 1 1 1 1, 2 2 2 2, 3 3 3 3, 4 4 4 4...
For more detailes visit:
wikibooks.org/wiki/C_Programming/C_Reference/stdio.h/fopen
Here is your "modified" (excess/waste/extra { } removed and data.txt is written) code that gives you required output: 1 1 1 1, 2 2 2 2, 3 3 3 3, 4 4 4 4.
It prints the array named buffer. It means that...
...data was successfully copied! from "data.txt" to buffer[4][4]:
#include"stdio.h"
#include"stdlib.h"
int main()
{
FILE *fp1;
int i,j;
int buffer[4][4];
for ( i = 0; i < 4; i++ )
for ( j = 0; j < 4; j++ )
buffer[i][j] = 0;
fp1 = fopen("data.txt","r");
for(i=0; i<4; i++)
for(j=0; j<4; j++)
fscanf(fp1,"%d", &buffer[i][j]);
fclose(fp1);
for(i=0; i<4; i++)
{
for(j=0; j<4; j++)
printf("%d ",buffer[i][j]);
printf("\n");
}
return 0;
}
P.S.
If data.txt will contain not
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
but
1 1 1 1
2 2 2 2
3 3 3 3
4 444 // the last two elements are absent
the program will read 1-st, 2-nd and 3-rd line properly, and the output of the 4-th line will be
4 444 0 0
It prints 4, then 444, and then 0 and 0: the last two elements are '0's because buffer had been initialized by zeros, so all elements changed their values, but the last two remained to be zeros.
You have said you want to read data.txt then why are you opening the file exact_enumerated_config_442_cub_mc
Try changing this
fp1 = fopen("exact_enumerated_config_442_cub_mc","r");
to
fp1 = fopen("data.txt","r");

Resources