fgets causing an infinite for loop in C - 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.

Related

In C is their anyway way to read the contents of a file, line-by-line, and store each integer value (preline) into a array separately?

My C Code is as so:
#include <stdio.h>
int main( int argc, char ** argv){
FILE *myFile;
myFile = fopen("numbers.txt", "r");
//read file into array
int numberArray[16];
for (int i = 0; i < 16; i++){
fscanf(myFile, "%1d", &numberArray[i]);
}
for (int i = 0; i < 16; i++){
printf("Number is: %d\n", numberArray[i]);
}
}
My numbers.txt file contains the follow values:
5
6
70
80
50
43
But for some reason my output
Number is: 5
Number is: 6
Number is: 7
Number is: 0
Number is: 8
Number is: 0
Number is: 5
Number is: 0
Number is: 4
Number is: 3
Number is: 0
Number is: 0
Number is: 4195904
Number is: 0
Number is: 4195520
Number is: 0
However I'm expecting it to print out numberArray to print out the identical contents of the text file. I'm not exactly sure why it's doing this, does anyone happen to know the reason? I'm aware that I'm making an array bigger than the amount of values that I can store, but I'm still confused as to why it can't store 70, 80, etc into one index?
It is because you are reading only 1 digit at a time.
Hence change the below.
fscanf(myFile, "%1d", &numberArray[i]);
to
fscanf(myFile, "%d", &numberArray[i]);
And your array should be of size number of integers in the file.
int numberArray[6];
for (int i = 0; i < 6; i++)
or
while (fscanf(myFile, "%d", &numberArray[i++]) == 1);
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *myFile = fopen("numbers.txt", "r"); // just init your variable directly
if (!myFile) { // Always check if there is no error
return EXIT_FAILURE; // handle it as you like
}
#define SIZE 16 // Avoid magic number
int numberArray[SIZE];
size_t n = 0; // n will represent the size of valid values inside the array
// Always check if scanf family has parsed your input also "%1d" ask to only parse
// one digit, so use %d if you want parse an integer
while (n < SIZE && fscanf(myFile, "%d", numberArray + n) == 1) {
n++;
}
for (size_t i = 0; i < n; i++) {
printf("Number is: %d\n", numberArray[i]);
}
}

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 ...

Reading numbers from a text file into an array in C

I'm a programming noob so please bear with me.
I'm trying to read numbers from a text file into an array. The text file, "somenumbers.txt" simply holds 16 numbers as so "5623125698541159".
#include <stdio.h>
main()
{
FILE *myFile;
myFile = fopen("somenumbers.txt", "r");
//read file into array
int numberArray[16];
int i;
for (i = 0; i < 16; i++)
{
fscanf(myFile, "%d", &numberArray[i]);
}
for (i = 0; i < 16; i++)
{
printf("Number is: %d\n\n", numberArray[i]);
}
}
The program doesn't work. It compiles but outputs:
Number is: -104204697
Number is: 0
Number is: 4200704
Number is: 2686672
Number is: 2686728
Number is: 2686916
Number is: 2004716757
Number is: 1321049414
Number is: -2
Number is: 2004619618
Number is: 2004966340
Number is: 4200704
Number is: 2686868
Number is: 4200798
Number is: 4200704
Number is: 8727656
Process returned 20 (0x14) execution time : 0.118 s
Press any key to continue.
change to
fscanf(myFile, "%1d", &numberArray[i]);
5623125698541159 is treated as a single number (out of range of int on most architecture). You need to write numbers in your file as
5 6 2 3 1 2 5 6 9 8 5 4 1 1 5 9
for 16 numbers.
If your file has input
5,6,2,3,1,2,5,6,9,8,5,4,1,1,5,9
then change %d specifier in your fscanf to %d,.
fscanf(myFile, "%d,", &numberArray[i] );
Here is your full code after few modifications:
#include <stdio.h>
#include <stdlib.h>
int main(){
FILE *myFile;
myFile = fopen("somenumbers.txt", "r");
//read file into array
int numberArray[16];
int i;
if (myFile == NULL){
printf("Error Reading File\n");
exit (0);
}
for (i = 0; i < 16; i++){
fscanf(myFile, "%d,", &numberArray[i] );
}
for (i = 0; i < 16; i++){
printf("Number is: %d\n\n", numberArray[i]);
}
fclose(myFile);
return 0;
}
for (i = 0; i < 16; i++)
{
fscanf(myFile, "%d", &numberArray[i]);
}
This is attempting to read the whole string, "5623125698541159" into &numArray[0]. You need spaces between the numbers:
5 6 2 3 ...
Loop with %c to read the stream character by character instead of %d.
There are two problems in your code:
the return value of scanf must be checked
the %d conversion does not take overflows into account (blindly applying *10 + newdigit for each consecutive numeric character)
The first value you got (-104204697) is equals to 5623125698541159 modulo 2^32; it is thus the result of an overflow (if int where 64 bits wide, no overflow would happen). The next values are uninitialized (garbage from the stack) and thus unpredictable.
The code you need could be (similar to the answer of BLUEPIXY above, with the illustration how to check the return value of scanf, the number of items successfully matched):
#include <stdio.h>
int main(int argc, char *argv[]) {
int i, j;
short unsigned digitArray[16];
i = 0;
while (
i != sizeof(digitArray) / sizeof(digitArray[0])
&& 1 == scanf("%1hu", digitArray + i)
) {
i++;
}
for (j = 0; j != i; j++) {
printf("%hu\n", digitArray[j]);
}
return 0;
}
enter your file input like this
ex:
12
13
22
45
(after every number hit enter)
then run your programm it will run properly

fgets inside of a For loop causing strange behavior

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

Resources