Array Declaration causing weird behavior with while loop - c

I have recently encountered very weird behaviour with my c program. I gutted out most of the code just to isolate where the issue is happening for you guys.
The purpose of the program in its current state is to read in jobs from a txt file and print the contents to the screen. Here it is:
#include <stdio.h>
int main(){
char* user;
char process;
int arrival;
int duration;
scanf("%*[^\n]"); //skipping header in the file.
while(!feof(stdin))
{
scanf("%s\t%c\t%d\t%d\n", user, &process, &arrival, &duration);
printf("%s\t%c\t%d\t%d\n", user, process, arrival, duration);
}
int x[5]; //<----- Causing the weird behaviour
}
The file I pipe into it is:
User Process Arrival Duration
Jim A 2 5
Mary B 2 3
Sue D 5 5
Mary C 6 2
The issue that I'm running into is whenever I declare the int x array, whether it is at the bottom or top of my code, the while loop enters an infinite loop or the program seg faults.
It will go into an infinite loop or seg fault depending on the size I declare the array at. For example, if I declare the array to be size 5, it enters an infinite loop. However, if I declare the array to be size 2, it will seg fault.
I am compiling my program by running:
gcc -o myprog myprog.c
and I am piping the file by running:
cat jobs.txt | ./myprog
It is also worth noting that the program runs fine without the int x array declaration.
I am completely stumped as to what might be the issue, any thought?

You have an undefined behavior because of the pointer user which is not initialized. user must point to a memory area capable of storing what you want (see malloc() for example).

As you have found from the other answer, your initial problem is with char *user; which declares a character pointer that is uninitialized (e.g. it does not point to any valid block of memory). While you can dynamically allocate with malloc, calloc, or realloc, that may be an over-complication for your circumstance.
All you really need is to declare a character array sufficient to hold the user names. 16 chars is more than sufficient here.
Next while (!feof(fp)) is almost always wrong. (see link in my comment). scanf provides a return of the number of valid conversions that take place. In your case, with "%s..%c..%d..%d" (4-conversion specifiers), a return of 4 will indicate that no matching or input failure occurred. So instead of your feof check, just use the scanf return, e.g.
scanf("%*[^\n]"); //skipping header in the file.
while (scanf ("%15s %15s %d %d", user, process, &arrival, &duration) == 4)
printf ("%s\t%c\t%d\t%d\n", user, *process, arrival, duration);
(note: for the simplified scanf format string "%15s %15s %d %d", arrival is declared as a character array (see below) and read as a string (to take advantage of leading white-space skipping) and then *arrival is used to pick off the character. This provides a bit more robust way to read your input in the event your input is space separated instead of *tab separated)
To avoid using magic numbers in your code (e.g. 16), declare a constant if you need one for the max characters in your arrays, e.g.
#define MAXC 16
int main (void) {
char user[MAXC] = "", process[MAXC] = "";
note: it may look like "%15s %15s %d %d" breaks this rule, but sadly, there is no way to include a constant of variable in the scanf field width specifier that protects against reading more than 15 chars into your arrays -- remember, you must leave room for the final character -- the nul-terminating character.
Putting it altogether, you could do something like the following:
#include <stdio.h>
#define MAXC 16
int main (void) {
char user[MAXC] = "", process[MAXC] = "";
int arrival, duration;
scanf("%*[^\n]"); //skipping header in the file.
while (scanf ("%15s %15s %d %d", user, process, &arrival, &duration) == 4)
printf ("%s\t%c\t%d\t%d\n", user, *process, arrival, duration);
return 0;
}
Example Use/Output
$ ./bin/scanf_usr_arriv <dat/userprocarriv.txt
Jim A 2 5
Mary B 2 3
Sue D 5 5
Mary C 6 2
You may also want to consider reading all "lines of input" with a line-oriented input function like fgets and then calling sscanf on the resulting buffer to parse each of the variables. This has the benefit of allowing separate validation on the line being read, and then an independent validation on the parse of each variable from the line.
Look things over and let me know if you have any questions.

Related

Basic "C" programming error Trying to write Player's name before averages

This is the code I have written.
#include<stdio.h>
int main()
{
char playerName;
int gameScores [10]= {12,5,21,15,32,10};
int totalPoints=0;
int x;
float avg;
for (playerName='A'; playerName<='Z'; playerName=playerName+1)
{
print("%c",playerName);
scanf ("/%s",playerName[x]);
}
putchar('\n');
for(x=6; x<10; x++)
{
printf ("What did the player score in game %d?". x+1);
scanf ("%d", &gameScores [x]);
}
for(x=0; x<10; x++)
{
totalPoints +=gamesScores[x];
}
avg=((float) totalPoints/10);
printf("\n\nThe Play's scoring average is %.1f.\n", avg);
return (0)
}
I am running into problems around line 22 with
scanf("/%s",playerName [x]);
it keeps coming up with an error. It says that the subscripted value is not an array, pointer, or vector. I would like it to list the player's name before the score average. Without the scanf if just gives me the alphabet above the input not a players name.
char playerName is just a single char, one byte. C strings are arrays of char, terminated by a zero (NUL) character. The single character treated as a string could therefore only hold the terminating NUL, and nothing else, i.e., it can only be the empty string.
You need to define playerName as an array large enough to hold the name + the terminating NUL, e.g.,:
#define MAX_NAME_LENGTH 100
char playerName[MAX_NAME_LENGTH + 1];
This would suffice for a single name, but since you have multiple players, you need an array of these arrays, e.g.:
#define MAX_PLAYERS 10
#define MAX_NAME_LENGTH 100
char playerName[MAX_PLAYERS][MAX_NAME_LENGTH + 1];
(You can also allocate the memory dynamically, such as with malloc, but that's outside the scope of the question.)
Also, your scanf has an extra slash / in the format string, which causes it to fail unless each player name is preceded by a slash in the input, which I'm guessing is not the case. Maybe you were going for the backslash \ to escape the %, but that is also not required here since the percent sign is only "special" in the printf/scanf format strings, not in C itself.
In a proper program you also need to guard against buffer overflow when reading with scanf. And should check the return value of scanf to verify that the input was successfully read (and perhaps retry or abort the program if not). See the documentation of scanf (and every other function you use) for the return values and arguments.
(Overall I would suggest learning to read lines into a buffer with fgets, as it may often be hard to recover from scanf errors in a sensible way, especially when the format strings become more complicated.)

Limiting the type length of the user on C

My code asks a the 10 digit number, it reads it as a string, pass it to another function that checks if the user's input is a real number and has no characters or symbols with ASCII, and then if it's good, with atof it changes the string into a number for a variable.
I want the user to only introduce 10 digits/characters on the input console, I mean, if the user would put a 11 character for example, the console just don't grab it, or in the case this is impossible for C, make that if the user put more than 12 characters on the input, then the program launches an error message saying it exceeds the limit, the problem is, when i tried to use this method, for example if i put some big numbers like a 40 digit number, then the program goes crazy and send just incomprehensible results like "1.#J", or if I put a character in middle of the numbers, then it sends the corresponding error message i set for the user to not put characters, but it still grabs part of the number and accept it as it is nothing wrong, here's the main of code I tried:
int main() {
char chain[10];
float N;
int valid=0;
do{
printf("introduce real numbers: ");
fgets(chain, sizeof(chain), stdin);
if( chain[strlen(chain)-1] == '\n')
chain[strlen(chain)-1] = '\0';
valid=validate_numbers(chain);
}while(valid==0);
N=atof(chain);
printf("float number is: %.2f", N);
getche();
return 0;
}
Here's the rest of the code for more extense check: Pastebin
And sorry if there's some novice errors or the question is plain simple, im quite new programing.
Change this:
char chain[10];
to this:
char chain[11]; // +1 for the NULL terminator
since C-strings should be NULL terminated, thus we need one cell reserved for the NULL-terminator in our array (which will store our string).
I mean, if the user would put a 11 character for example, the console just don't grab it
Not possible in C.
or in the case this is impossible for C, make that if the user put more than 12 characters on the input, then the program launches an error message saying it exceeds the limit.
Yes, let's do that! Read the string, and if the length of it is more than 10 characters, then print an error message.
Allow chain array to be of size 12 (10 for the maximum length of the valid input, 1 for an extra character (if any) and 1 for the NULL-terminator), so that we can store the extra character, if any.
Example:
#include <stdio.h>
#include <string.h>
int main(void)
{
char chain[12];
printf("introduce real numbers:\n");
fgets(chain, sizeof(chain), stdin);
chain[strcspn(chain, "\n")] = '\0';
if(strlen(chain) > 10)
{
printf("Error: Maximum length of chain is 10! Exiting..\n");
return 1;
}
return 0;
}
Note: You could use EXIT_SUCCESS and EXIT_FAILURE, instead of plain numbers (1 and 0 respectively): Should I return 0 or 1 for successful function?
Irrelevant to OP's question: In the full version of your code though, there is a plethora of problems, such as this top line of code int valid=validate_numbers(char number[]);, which wishes to declare the method. It should be just validate_numbers(char number[]);. The same holds true for the definition of the method too. Make sure you go through all your code again, and read the messages the compiler gifts to you. :)
What about using scanf instead of fgets? This should read 9 characters and save them as a string:
scanf("%9s" , &chain)
I'd suggest reading https://en.m.wikipedia.org/wiki/Scanf_format_string and man pages as well.

I have two char arrays and when their sizes are different the program doesn't read/write them properly

I have a program to do basic things with two char arrays. Everything works fine when the size limit of the first is equal to size limit of the second, but when the size of the first char array is different to the size of the other, the program starts to read/write the strings in a strange way.
For example, if the limit of the first is 31 and the limit of the other is 5, if the typed characters in the first are more than 8 or something like that the program won't let the user typed anything on the second array as if it was full already.
I tried to fix it without using the functions of string.h, but the programs still did the same when the size limit of the two char arrays were different.
#include <stdio.h>
#include <string.h>
#define LIMIT1 31
#define LIMIT2 5
/*Function: void copy_string(char *pointer_destination, char *pointer_source)
Precondition: it needs a pointer to the direction of memory of the first element of two char vectors and the size limit of the 'destination' vector
Postcondition: it puts all the elements of the 'source' vector into the other until the last element that */
void copy_string(char *pointer_destination, char *pointer_source, int LIMd){
//Variable declaration
int i = 0;
/*Cycle for replacing the element of the 'destination' vector by the element of the 'source' vector.
When the element of the 'destination' OR of the 'source' is the null character, this cycle ends*/
for(; i < LIMd && *(pointer_source + i) != '\0'; i++){
*(pointer_destination + i) = *(pointer_source + i);
}
*(pointer_destination + i) = '\0';
}
int main(){
//Variable declaration
int restart;
char username[LIMIT1], string2[LIMIT2];//Here we define the limit for obvious reasons
//Restart cycle starts here
do{
//Data input
printf("Type your username (maximum 30 characters)\n");
fgets(username, LIMIT1 - 1, stdin);
fflush(stdin);
printf("Type a string of maximum 30 characters\n");
fgets(string2, LIMIT2 - 1, stdin);
fflush(stdin);
printf("Your typed username and your typed second string are, respectively:\n");
fputs(username, stdout);
fputs(string2, stdout);
printf("Concatenating, the username is now\n");
strcat(username, string2);
fputs(username, stdout);
printf("Now I'll copy what is in your username and I'll put it in the second string,\n");
copy_string(string2, username, LIMIT2 - 1);
fputs(string2, stdout);
//Restart cycle switch
printf("Type '0' to close this program, otherwise it'll restart itself\n");
scanf("%d", &restart);
fflush(stdin);
//Restart cycle ends here
}while(restart);
return 0;
}
I expected that if the size of the two arrays were different, the program would still read and write them properly (if the size of the first is 3, read from the user only the first three characters and put behing a \0 and if the size of the other is 25 do the same but with 25 as the size limit)
You're not very specific about your actual and expected output, but I imagine it's this:
Steps to reproduce:
Run the program as posted
Get the prompt Type your username (maximum 30 characters)
Enter this is an especially long string
Expected result:
A prompt that says Type a string of maximum 30 characters and lets you enter a new string
Actual result:
Type a string of maximum 30 characters is written to screen but the program continues immediately without letting you enter another string.
This happens because the first fgets is set up to read no more than 30 characters from the user. If you enter more, it will only consume the first 30.
The next fgets will then consume the remainder of that line instead of a new line, giving the appearance of skipping the prompt.
You should use a large enough buffer to accomodate the line, so that this is not an issue. Alternatively, you can manually read and discard one character at a time until you find a \n, effectively draining stdin of the rest of the line.
You seem to be relying on fflush(stdin) to clear any unread input. This is undefined behaviour in standard C, and only works on some platforms as a non-standard extension. I suspect it doesn't work on yours, and either breaks input altogether or does nothing and causes the next fgets to read the rest of the input intended for the previous one.
Instead of fflush, you can check whether the string read by fgets ends in a newline ('\n', which you probably want to remove if it is there). If not, keep reading (and discarding) input until either a newline '\n' or EOF is encountered.
(In general I would also recommend not using scanf for user input - it's a lot easier to read into a temporary buffer with fgets and parse that with sscanf as needed.)
Another obvious, but unrelated, problem is strcat(username, string2); – this may exceed the length of username. You need to leave at least LIMIT2 - 1 extra space (that you don't allow fgets to use), or simply allocate a new array of the correct size after you know the lengths of each.
Warning fgets will save the \n if there is enough place to save it, I think your problem comes because in your examples the end of line is saved at least in the usename. So you need to remove it if present.
Warning you give the size minus 1 to fgets, but fgets already read the given length minus 1 to have place to put the null character at the end.
Note the message to read the second string is wrong because it indicates a length 30 rather than 4.

Writing 5 character to char[5] affects int

Easy code down below.
Mac OS X 10.10.5, Xcode 7.2, C-file.
If I input 1, and afterwards qwert, I get 0 and qwert back.
1 and qwer gives 1 and qwer.
1 and e.g. qwerty gives 121 and qwerty.
What have I missed - why can I write more than 4 chars (+null) to a 5 char variable?
Why is the integer affected?
#include <stdio.h>
int main() {
int userInput;
char q[5];
printf("Hello\n");
scanf("%d", &userInput);
printf("%d\nAnd\n", userInput);
scanf("%s", q);
printf("\n");
printf("%d\n%s", userInput, q);
return 0;
}
What have I missed - why can I write more than 4 chars (+null) to a 5 char variable?
There is nothing stopping you from accessing out of bounds portions of an array in c. This will compile:
char a[2];
a[10000] = 10;
Why is the integer affected?
What you are causing is undefined behavior and is likely the reason that your int is affected. You can learn more about this by reading about c arrays. This is happening because you are putting a 5 character string plus a null terminating character ( ie 6 chars) into a space only meant for 5. You are going outside the bounds of your array.
As a further note, scanf("%s" offers no method of protecting against this behavior. If a user puts in a string that is too long then too bad. That is why you should protect your input by using something like a format string of "%4s" or use fgets:
fgets(q, sizeof q, stdin);
Which are both ways you can protect your input from entering more than 4 characters.
[Edit] User/code can try to "write more than 4 chars (+null) to a 5 char variable". C does not specify what should happen when code does not prevent such an event. C is coding without the safety net/training wheels.
scanf("%s", q); reads and saves the 5 characters of "qwert" and it also appends a null character '\0'. #Weather Vane
Since q[] has only room to 5 characters, undefined behavior occurs (UB). In OP's case, it appear to have over-written userInput.
To avoid, use a width limit on "%s" such as below. It will not consume more than 4 non-white-space from the user. Unfortunately, extra text will remain in stdin.
char q[5];
scanf("%4s", q);
Or better, review fgets() for reading user input.
The reason that the int userInput is affected is that you are writing past the end of the char array (q). Since both of these are stack variables, the compiler you're using seems to be allocating memory on the stack for the local variables in "reverse order", it, they are being "pushed" in the order defined, so the first local variable listed is lower on the stack. So, in your case, when you write past the end of q, you are writing in the memory space allocated for userInput, which is why it is affected.

Using getchar() and malloc() together

I found a C Program whose purpose is to input a string while using dynamic memory allocation.
However I am having difficulty understanding the logic behind it.
#include <stdio.h>
#include <stdlib.h>
#define MAX 10
int main(void)
{
char *A;
int max_int=0;
printf("Enter max string length: ");
scanf("%d",&max_int);
while ((getchar())!='\n');
A=(char *)malloc(max_int+1); //room for null char
printf("Enter string: ");
fgets(A,max_int,stdin);
}
What is the purpose of while ((getchar())!='\n'); ? It seems redundant to me, since your only inputing a number before it gets called.
while ((getchar())!='\n');
The above line is used to flush anything on the line not read by scanf, for example non-digits and spaces, so the next input starts in a new line.
Also:
scanf-call should check the number of assigned matches (0 matches is possible.
the result of malloc should never be cast (that just hides bugs).
the result of malloc should be checked for NULL. (Warning: Undefined Behavior)
fgets expects the buffer length and guarantees 0-termination. Passing one less means you get a shorter string.

Resources