I am trying to read both negative and positive numbers from a file into an array.
Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>
#include <string.h>
void printData(int data[]) {
for (int i = 0; i < 21; i++) {
printf("%d\n", data[i]);
}
}
void findMaximumDataValue(int data[]) {
int max = -INFINITY;
for (int i = 0; i < 21; i++) {
if (data[i] > max) {
max = data[i];
}
}
printf("The maximum value in the file is: %d\n", max);
}
void userInterface(int data[]) {
while(1) {
char choice;
printf("a) Print out the values\n");
printf("b) Find the maximum Value\n");
printf("c) Calculate the RMS (root mean square)\n");
printf("d) Count the number of negative values\n");
printf("e) Exit Back to Main Menu\n");
printf("Enter selection: ");
scanf("%s", &choice);
printf(" %c", choice);
switch (choice) {
case 'a': {
printData(data);
break;
}
case 'b': {
findMaximumDataValue(data);
break;
}
case 'e': {
return;
}
}
}
}
void dataTxtFunctions() {
FILE* fp;
int data[21];
// Failed to open file
if ( !(fp = fopen("data.txt", "r")) ) {
printf("%s\n", strerror(errno));
return;
}
for (int i = 0; i < 21; i++) {
fscanf (fp, "%d", &data[i]);
}
fclose(fp);
userInterface(data);
}
int main() {
dataTxtFunctions();
}
When I run this code against my data.txt I get
1
6
4
5
5
9
12
14
15
-17
-19
21
-23
0
37
0
-31
32
34
-37
-39
0
a) Print out the values
b) Find the maximum Value
c) Calculate the RMS (root mean square)
d) Count the number of negative values
e) Exit Back to Main Menu
Enter selection: a
a0
0
0
0
0
0
0
0
192327
0
0
0
8
48
1497598080
32767
1
6
4
5
5
The first part, before the menu is displaying the correct values from the file, however, when the selection is made and the array is passed the data changed.
Why is that?
You have a large number of small errors. (addressed in the comments in the code). Your primary problem is understanding where and how to declare data and how to go about getting values from your file into data as part of your menu system. While you can declare your array data in your userinterface function, in general you will want to declare your variables in main() (or the calling function) and then pass them as parameters (properly qualified) to any function that will need them.
As part of the misconception of how to fill data you have a void dataTxtFunctions() that is more or less randomly floating around in your code, not attached anywhere. The name also suggests a large:
"I don't know what I'm going to do with this thing, so I'll call it some generic dataTxtFunctions and hope it works itself out somehow?"
(note: that approach to coding never works out...)
What you need is a readdata function to prompt for a filename, open and validate the file, and then read integers from the file, providing a meaningful return of the number of integers read (or 0 indicating failure). That way you could create a " r) Read data from file\n" menu entry to handle those operations.
(do not hardcode values or names (e.g. data.txt) in your code, magic numbers and magic strings limit the usefulness of your code and make it hard to maintain)
The remainder of the suggestions are provide in comments below. The following example can be used with your data.txt (I used the first 22 values you show in your question as the input file below). If I understood what you were trying to accomplish, you could do something like the following:
#include <stdio.h>
#include <limits.h>
#define MAXN 256 /* if you need a constant, define one */
/* don't skimp on array sizes */
/* empty any characters that remain in stdin -- following scanf */
void empty_stdin()
{
for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) {}
}
/* print values in data (both data and number of values are const) */
void printdata (const int *data, const size_t n)
{
for (size_t i = 0; i < n; i++)
printf ("data[%3zu]: %d\n", i, data[i]);
}
/* find/return max value in data (data and n are const) */
int findmaxdatavalue (const int *data, const size_t n)
{
int max = INT_MIN; /* use INT_MIN from limits.h */
for (size_t i = 0; i < n; i++)
if (data[i] > max)
max = data[i];
return max; /* provide a return to gauge success/failure */
}
/* prompt for filename, read up to max values from file.
* returns number of values read (0 indicates failure).
*/
size_t readdata (int *data, size_t max)
{
int tmp; /* temporary value to validate read */
char filenm[PATH_MAX] = ""; /* buffer to hold filename */
FILE *fp = NULL; /* FILE stream pointer */
size_t n = 0; /* number of values read */
for (;;) { /* loop until valid filename provided or EOF */
int rtn;
printf ("\nenter filename: ");
/* if EOF, user canceled with ctrl+d (ctrl+z on windoze) */
if ((rtn = scanf ("%[^\n]", filenm)) == EOF)
return 0;
empty_stdin(); /* remove '\n' (or can chars beyond PATH_MAX) */
if (rtn == 1) /* if return 1, good string in filenm */
break;
/* otherwise, handle error */
fprintf (stderr, "error: invalid input - filename.\n");
}
if (!(fp = fopen (filenm, "r"))) { /* validate open */
fprintf (stderr, "error: file open failed '%s'\n", filenm);
return 0;
}
/* read up to 'max' values from file */
while (n < max && fscanf (fp, "%d", &tmp) == 1)
data[n++] = tmp;
fclose (fp);
return n; /* return number of integers read */
}
void userinterface (int *data, size_t *n, size_t max)
{
while (1) { /* loop continually */
char choice = 0;
int rtn; /* you only need 1 printf */
printf ("\n r) Read data from file\n"
" a) Print out the values\n"
" b) Find the maximum Value\n"
" c) Calculate the RMS (root mean square)\n"
" d) Count the number of negative values\n"
" e) Exit Back to Main Menu\n\n"
"Enter selection: ");
if ((rtn = scanf ("%c", &choice)) == EOF)
return;
empty_stdin(); /* empty '\n' (and any other chars) */
if (rtn != 1 || /* validate return and proper choice */
(choice != 'r' && (choice < 'a' || 'e' < choice))) {
fprintf (stderr, "error: invalid input - choice.\n");
continue; /* on bad choice, redisplay menu */
}
if (!*n && choice < 'e') { /* if no data, only 'e' or 'r' valid */
fprintf (stderr, "error: data is empty.\n");
continue; /* data empty, redisplay menu */
}
switch (choice) { /* handle choice */
case 'a':
printdata (data, *n);
break;
case 'b':
printf ("max value: %d\n", findmaxdatavalue (data, *n));
break;
case 'c':
fprintf (stderr, "RMS not yet implemented.\n");
break;
case 'd':
fprintf (stderr, "Negative count not yet implemented.\n");
break;
case 'e':
return;
break;
case 'r': /* read data, handle error, warn if max values read */
if (!(*n = readdata (data, max)))
fprintf (stderr, "error: nothing read from file.\n");
else if (*n == max)
fprintf (stderr, "warning: data is full.\n");
break;
default : fprintf (stderr, "error: something wrong with switch.\n");
break;
}
}
}
int main (void) {
int data[MAXN] = {0}; /* declare values in main() */
size_t max = MAXN,
n = 0;
userinterface (data, &n, max); /* pass to userinterface */
printf ("\nsuccessfully processed '%zu' integers from file.\n", n);
return 0;
}
Also, note on style, C generally avoid camelCase and MixedCase names, instead using all lowercase names for variables and functions reserving uppercase for constants and macros. Since it is style, it is completely up to you, but it does say something about your code.
Example Use/Output
$ ./bin/menureadint
r) Read data from file
a) Print out the values
b) Find the maximum Value
c) Calculate the RMS (root mean square)
d) Count the number of negative values
e) Exit Back to Main Menu
Enter selection: a
error: data is empty.
... <menu - snip>
Enter selection: z
error: invalid input - choice.
... <menu - snip>
Enter selection: r
enter filename: dat/intvalues.txt
... <menu - snip>
Enter selection: a
data[ 0]: 1
data[ 1]: 6
data[ 2]: 4
data[ 3]: 5
data[ 4]: 5
data[ 5]: 9
data[ 6]: 12
data[ 7]: 14
data[ 8]: 15
data[ 9]: -17
data[ 10]: -19
data[ 11]: 21
data[ 12]: -23
data[ 13]: 0
data[ 14]: 37
data[ 15]: 0
data[ 16]: -31
data[ 17]: 32
data[ 18]: 34
data[ 19]: -37
data[ 20]: -39
data[ 21]: 0
... <menu - snip>
Enter selection: b
max value: 37
... <menu - snip>
Enter selection: c
RMS not yet implemented.
... <menu - snip>
Enter selection: e
successfully processed '22' integers from file.
Look things over and let me know if you have further questions.
That's the correct way to read from a file:
while (fread(&data[i], sizeof(int), 1, fp));
Related
I am stuck in this problem, 2 integers A and B must be entered, and A must be separated into digits with respect to B, example: A:1234 B:2 You must separate 12, 23, 34, and then verify how many prime numbers are between these digits
This is what I was able to do so far, I would appreciate it if you could guide me to know which way to go
#include <stdio.h>
int main() {
int A, B, i, contador;
//Introducimos el Numero A y verificamos que sea mayor a 0
for (i = 0; i != -1; i++) {
printf("\nIngrese el numero A :");
scanf("%d", &A);
if (A > 0) {
break;
}
}
//Introducimos el Numero B y verificamos que sea mayor a 0
for (i = 0; i != -1; i++) {
printf("\nIngrese el numero B :");
scanf("%d", &B);
if (B > 0) {
break;
}
}
printf("A\t\tB\t\tCifras\t\tCantidad Primos\n");
printf("%d\t\t%d", A, B);
return 0;
}
First we can greatly simplify validating input with a while loop for each variable that will loop until it scans in a number successfully and it's greater than zero.
Second, we can convert that input number to a string with sprintf which makes looping over individual chunks easier.
Having isolated substrings representing chunks of digits, strtol can convert them back to ints. Below I have simply printed them, but you could readily store them in an array for further manipulation.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main() {
int A = 0, B = 0;
while (scanf("%d", &A) != 1 || A <= 0);
while (scanf("%d", &B) != 1 || B <= 0);
char s[1024];
sprintf(s, "%d", A);
for (int i = 0; i < strlen(s) - B + 1; i++) {
char ss[B+1];
for (int j = 0; j < B; j++) {
ss[j] = s[i + j];
}
ss[B] = '\0';
int n = strtol(ss, NULL, 10);
printf("%d\n", n);
}
return 0;
}
There's no need for conversion to/from a string of digits.
You've got the "user input", so far.
The following skips that step, using 2 compile-time int's instead.
int main() {
int v1 = 1357924680, v2 = 3; // simulate two reasonable values from the user.
printf( "Using %d and %d as input\n", v1, v2 );
// calculate the power of 10 that has 'v2' zeros.
int divisor = 1;
while( v2-- ) divisor *= 10;
// from right-to-left, push 2-3-4-... digit values onto a stack
int stack[32] = { 0 }, spCnt = -1;
while( v1 > divisor/10 )
stack[ ++spCnt ] = v1 % divisor, v1 /= 10;
// retrieve (in order) pairs of values from the stack.
for( ; spCnt > 0; spCnt-- ) {
printf( "%d and %d\n", stack[ spCnt ], stack[ spCnt - 1 ] );
/* call your function that prints all the primes between these values. */
}
return 0;
}
Using 1234567890 and 2 as input
12 and 23
23 and 34
34 and 45
45 and 56
56 and 67
67 and 78
78 and 89
89 and 90
Using 1357924680 and 3 as input
135 and 357
357 and 579
579 and 792
792 and 924
924 and 246
246 and 468
468 and 680
There are a number of ways you can approach parsing the input of digits into groups of digits to be checked as prime. A very straight-forward approach is to read the digits into a C-string and then convert the groups of digits desired into integer values. That process can also be done a number of different ways. One instructive way is simply to manually handle grouping the number of desired digits to check and manually converting each group of digits to an integer value to check if prime.
Whenever you are reading input from the user, you are encourage to read a line of input at-a-time to avoid the pitfalls inherent in a formatted-read using scanf() (matching-failures and characters left unread in stdin just waiting to bite you on your next input --> which is the reason for the infinite-loop discussed below your other answer) Reading input with fgets() into a sufficiently sized buffer (character array) ensures are characters are read and nothing remains unread in stdin.
So long as you are not on an embedded system or microcontroller, C provides the constant BUFSIZ which you can use to size your character array, that is sufficient for most inputs you will encounter. In your case two arrays are needed, a separate buffer to hold the digits you will group and convert, and a second general buffer to take the other (B) input with. You can declare your buffers and read the digits with something similar to the following:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXDIGITS BUFSIZ
...
int main (void) {
char buf[MAXDIGITS], /* general input buffer */
digits[MAXDIGITS]; /* buffer to hold digits */
int ndigits = 0; /* digits to separate and test */
/* read/validate input A */
fputs ("enter number : ", stdout);
if (!fgets (digits, sizeof digits, stdin)) {
puts ("(user canceled input)");
return 0;
}
digits[strcspn (digits, "\n")] = 0; /* trim trailing '\n' */
...
(note: fgets() reads and includes the '\n' generated by the user pressing Enter. A simple way to remove it is to overwrite the '\n' character with 0 -- the nul-terminating character. strcspn() provides an efficient manner for doing so as shown above)
Since you are reading digits, you will want to check that the input is comprised of all digits. You can write a simple function to check if each character entered isdigit() with the function (or macro) provided in ctype.h, e.g.
/* simple function checks if s is composed of all digits,
* returns 1 if so, 0 otherwise.
*/
int all_digits (const char *s)
{
int i = 0;
if (!*s || *s == '\n') { /* check empty-string or empty-line */
return 0;
}
for (; s[i]; i++) { /* loop over each digit */
if (!isdigit((unsigned char)s[i])) {
break;
}
}
return !s[i]; /* end-of-string */
}
If the user input into digits is all-digits, you can proceed to take the second input of the number of digits to group together to check for prime. The approach is the same, read with fgets() into buf and then convert the number to int and validate the number represents a group-size that is possible given the number of digits in digits, e.g.
if (!all_digits (digits)) { /* validate input all digits */
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
/* read / validate input B */
fputs ("combine ndigits : ", stdout);
if (!fgets (buf, sizeof buf, stdin)) {
puts ("(user canceled input)");
return 0;
}
/* convert string to int / validate */
if (sscanf (buf, "%d", &ndigits) != 1) {
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
/* validate the number of digits to combine is possible */
if (ndigits <= 0 || (size_t)ndigits > strlen (digits)) {
fputs ("error: requested combination of digits not possible.\n",
stderr);
return 1;
}
With both inputs taken and the number of digits ndigits to group validated, you can convert your groups to int and check for prime. A simple manual way to do so is to loop over digits beginning with the index ndigits - 1 and looping ndigits times for each group manually converting the digits to int. Only positive values of the groupings are considered, which reduces the task to:
/* loop starting at ndigits digit, combine every ndigits digits into int
* (only positive combinations are considered)
*/
for (int i = ndigits - 1; digits[i]; i++) {
int j = i, /* index for scanning over digits to separate */
n = ndigits, /* copy of number of digits to separate */
num = 0, /* separated number */
mult = 1; /* digit multiplier */
while (n--) { /* loop ndigits times */
num += mult * (digits[j--] - '0'); /* add mult * value to num */
mult *= 10; /* increment mult by 10 */
}
printf (" %*d\n", ndigits, num); /* (optional) output of num */
/** check if num is prime here **/
}
That's it. Checking for prime is left to you. The input, grouping and conversion of the group to int can be checked by printing each grouping that would be checked for prime.
Example Use/Output
Putting the code together (a complete source is provided at the end), compiling and running your would get:
$ ./bin/group-digits
enter number : 1234
combine ndigits : 1
1
2
3
4
Groupings of two:
$ ./bin/group-digits
enter number : 1234
combine ndigits : 2
12
23
34
Groupings of three:
$ ./bin/group-digits
enter number : 1234
combine ndigits : 3
123
234
Groupings of four:
$ ./bin/group-digits
enter number : 1234
combine ndigits : 4
1234
What about a grouping of five with four-digits?
$ ./bin/group-digits
enter number : 1234
combine ndigits : 5
error: requested combination of digits not possible.
What about a larger string of digits, that itself exceeds the size of int, but so long as the group-size is within the size of int, there is no reason that wouldn't work:
$ ./bin/group-digits
enter number : 12345678901234567890
combine ndigits : 4
1234
2345
3456
4567
5678
6789
7890
8901
9012
123
1234
2345
3456
4567
5678
6789
7890
(note: 0123 is properly converted to 123 instead of an octal value representing 83)
Complete Source for Example
The complete combined source code for the example for easy reference is:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXDIGITS BUFSIZ
/* simple function checks if s is composed of all digits,
* returns 1 if so, 0 otherwise.
*/
int all_digits (const char *s)
{
int i = 0;
if (!*s || *s == '\n') { /* check empty-string or empty-line */
return 0;
}
for (; s[i]; i++) { /* loop over each digit */
if (!isdigit((unsigned char)s[i])) {
break;
}
}
return !s[i]; /* end-of-string */
}
int main (void) {
char buf[MAXDIGITS], /* general input buffer */
digits[MAXDIGITS]; /* buffer to hold digits */
int ndigits = 0; /* digits to separate and test */
/* read/validate input A */
fputs ("enter number : ", stdout);
if (!fgets (digits, sizeof digits, stdin)) {
puts ("(user canceled input)");
return 0;
}
digits[strcspn (digits, "\n")] = 0; /* trim trailing '\n' */
if (!all_digits (digits)) { /* validate input all digits */
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
/* read / validate input B */
fputs ("combine ndigits : ", stdout);
if (!fgets (buf, sizeof buf, stdin)) {
puts ("(user canceled input)");
return 0;
}
/* convert string to int / validate */
if (sscanf (buf, "%d", &ndigits) != 1) {
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
/* validate the number of digits to combine is possible */
if (ndigits <= 0 || (size_t)ndigits > strlen (digits)) {
fputs ("error: requested combination of digits not possible.\n",
stderr);
return 1;
}
/* loop starting at ndigits digit, combine every ndigits digits into int
* (only positive combinations are considered)
*/
for (int i = ndigits - 1; digits[i]; i++) {
int j = i, /* index for scanning over digits to separate */
n = ndigits, /* copy of number of digits to separate */
num = 0, /* separated number */
mult = 1; /* digit multiplier */
while (n--) { /* loop ndigits times */
num += mult * (digits[j--] - '0'); /* add mult * value to num */
mult *= 10; /* increment mult by 10 */
}
printf (" %*d\n", ndigits, num); /* (optional) output of num */
/** check if num is prime here **/
}
}
If The Groups Are Exclusive
If you intended your groupings to be exclusive, meaning if you had ten-digits, "0123456789" and wanted groups of five digits, that would provide two exclusive integers 1234 and 56789, then there are a few easy changes to make.
As a convenience, you can save the length of digits at the same time you trim the '\n' from the end, e.g.
...
int main (void) {
char buf[MAXDIGITS], /* general input buffer */
digits[MAXDIGITS]; /* buffer to hold digits */
int ndigits = 0; /* digits to separate and test */
size_t len = 0; /* length of digits */
...
digits[(len = strcspn (digits, "\n"))] = 0; /* trim '\n' save len */
...
You need to ensure len is divisible by ndigits, so just add your check to the existing checks for combinations
/* validate the number of digits to combine is possible */
if (ndigits <= 0 || (size_t)ndigits > len || len % ndigits ) {
fputs ("error: requested combination of digits not possible.\n",
stderr);
return 1;
}
Finally adjust your for loop increment:
/* loop starting at ndigits digit, combine every ndigits digits into int
* (only positive combinations are considered)
*/
for (int i = ndigits - 1; digits[i]; i += ndigits) {
...
Modified Example Use / Output
Now only exclusive ranges of digits are considered for the check against prime:
./bin/group-digits2
enter number : 12345678901234567890
combine ndigits : 4
1234
5678
9012
3456
7890
Or by groups of 5:
$ ./bin/group-digits2
enter number : 12345678901234567890
combine ndigits : 5
12345
67890
12345
67890
If len isn't divisible by ndigits:
$ ./bin/group-digits2
enter number : 12345678901234567890
combine ndigits : 6
error: requested combination of digits not possible.
Let me know if you have questions.
my code problem:
this code only lets me input one command then jumps out of the loop without even inputting 'quit'
The problem is to parse a series of commands that instruct a robot arm on how to manipulate blocks that lie on a flat table. Initially, there are n blocks on the table (numbered from 0 to n − 1) with block bi adjacent to block bi+1 for all 0 ≤ i < n − 1 as shown in the diagram below:
https://ibb.co/WpWQBYT
The valid commands for the robot arm that manipulates blocks are:
• move a onto b
where a and b are block numbers, puts block a onto block b after returning any blocks that are stacked on top of blocks a and b to their initial positions.
• move a over b
puts block a onto the top of the stack containing block b, after returning any blocks that are stacked on top of block a to their initial positions.
• pile a onto b
moves the pile of blocks consisting of block a, and any blocks that are stacked above block a, onto block b. All blocks on top of block b are moved to their initial positions prior to the pile taking place. The blocks stacked above block a retain their order when moved.
• pile a over b
puts the pile of blocks consisting of block a, and any blocks that are stacked above block a, onto the top of the stack containing block b. The blocks stacked above block a retain their original order when moved.
• quit
terminates manipulations in the block world. Any command in which a = b or in which a and b are in the same stack of blocks is an illegal command. All illegal commands should be ignored and should have no effect on the configuration of blocks.
Input:
https://ibb.co/pWJ9c7Q
Output:
https://ibb.co/Nt03mm3
[I only type the code of the first command for now.]
my code:
#include<stdio.h>
#include<string.h>
int main(){
int noi=0;
printf("please input n:");
int n;
scanf(" %d",&n);
int arr[n][n];
int i,j;
for(i=0;i<n;i++){
for(j=0;j<n;j++){
arr[i][j]=-1;
}
arr[i][0]=i;
}
char str1[5],str2[5];
int s,d;
while(strcmp(str1,"quit")!=0){
s=0;
d=0;
while(!(s>=1&&s<=n&&d>=1&&d<=n)){
scanf(" %s %d %s %d",str1,&s,str2,&d);
}
if(strcmp(str1,"move")==0){
if(strcmp(str2,"onto"==0)){
//empty s
for(i=0;i<n&&arr[s][i]!=-1;i++){
arr[arr[s][i]][0]=arr[s][i];
arr[s][i]=-1;
}
//empty d
for(i=0;i<n&&arr[d][i]!=-1;i++){
arr[arr[d][i]][0]=arr[d][i];
arr[d][i]=-1;
}
//now move s to d
i=1;
while(arr[d][i]!=-1){
i++;
}
arr[d][i]=arr[s][0];
arr[s][0]=-1;
}else if(strcmp(str2,"over")==0){
}else{
continue;
}
}else if(strcmp(str2,"pile")==0){
}else{
continue;
}
}
//print results
for(i=0;i<n;i++){
printf("%d:",i);
for(j=0;j<n&&arr[i][j]!=-1;j++){
printf("%d ",arr[i][j]);
}
printf("\n");
}
}
There are an extremely large number of errors in your code. Probably the one of the largest impediments to recognizing the problems in your code, while not an error, is the lack of spacing in your code. Scrunching everything together makes your code very difficult to read (especially for older eyes). Open the spacing on your code up a bit.
As mentioned in the comments above, your first show-stopping problem is the use of str1 while it is uninitialized. That invokes undefined behavior and then defined operation of your code is over at that point. Your code could SegFault, appear to work normally, or anything in between.
When you are taking user-input, it is recommended you use a line-oriented input function like fgets() or POSIX getline(). That one change avoids a large number of pitfalls associated with attempting to take user-input with a formatted-input function like scanf(). If you don't know what each of the pitfalls are associated with its use -- don't use it for user input. 9 out of 10 of the user input questions on this site relate to the misuse of scanf().
Additionally, Don't Skimp On Buffer Size!!. What happens if the user enters "iskabibble" instead of "quit"? How are the characters that do not fit in str1[5] handled? What if the cat steps on the keyboard and 100 characters are entered? It's better to be 10,000 characters too long, than one character too short. Take input with fgets() and a sufficiently sized buffer, and then parse the values needed with sscanf() instead of trying to do both with scanf(), e.g.
#define MAXC 512 /* if you need a constant, #define one (or more)
* ( don't skimp on buffer size!! )
*/
int main (void) {
char buf[MAXC]; /* buffer to store all user input */
int n; /* if possible, declare variables at beginning of scope */
fputs ("please input n for (n x n array): ", stdout);
if (!fgets (buf, MAXC, stdin)) { /* read all user input with fgets() */
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &n) != 1) { /* validate every conversion */
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
Reusing a single buffer to handle user-input simplifies things greatly.
While it is fine to use a VLA (Variable Length Array) for practice problems, be aware compilers are not required to support them from C11 forward, and none were supported before C99. Better to dynamically allocate for production code.
Your variable declarations should be in the scope where the variables are needed. This helps prevent variable shadowing of common variables such as i, j, etc.. 300 lines down in your code. For example, loop variables can be declared as part of the loop declaration so long as they are not needed outside the loop, e.g.
int arr[n][n]; /* VLA's are an 'optional' feature since C11 */
for (int i = 0; i < n; i++) { /* i, j can be decalred with loop scope */
for (int j = 0; j < n; j++) {
arr[i][j] = -1;
}
arr[i][0] = i;
}
When you need to user to enter specific input, better to loop continually until the user provides valid input (respecting their ability to cancel input by generating a manual EOF with Ctrl + d, or Ctrl + z on windows). For example where str1, s, str2 and d are needed:
while (strcmp (buf, "quit")) { /* loop until quit */
char str1[MAXC] = "", str2[MAXC] = "";
int s = 0, d = 0;
while (1) { /* loop continually */
fputs ("enter str1 s str2 d: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read / validate input */
puts ("(user canceled input)");
return 0;
}
buf[strcspn (buf, "\n")] = 0; /* trim '\n' from end of buf */
if (strcmp (buf, "quit") == 0) /* if "quit", break */
break;
/* parse and validate separate values, always protect array bounds */
if (sscanf (buf, "%511s %d %511s %d", str1, &s, str2, &d) != 4) {
fputs (" error: invalid format or integer input.\n", stderr);
continue;
} /* validate range of integers (negated conditions are confusing) */
if ((0 <= s && s < n) && (0 <= d && d < n))
break; /* exit loop on good input */
else /* otherwise, handle error */
fputs (" error: value for s or d out of range.\n", stderr);
}
(note: fgets() reads and includes the '\n' generated by the user pressing Enter, so before comparing for "quit" you will need to remove the newline with strcspn())
Since str1 and str2 are parsed from buf using sscanf() there is no '\n' to remove. However, when using sscanf() you must use the field-width modifier to protect the array bounds from overrun -- otherwise the use of scanf() or sscanf() to fill the character array is no safer than gets(), see: Why gets() is so dangerous it should never be used!
if (strcmp (str1, "move") == 0) { /* handle move */
if (strcmp (str2, "onto") == 0) { /* onto? */
int i; /* declare i in scope needed */
// empty s
for (i = 0; i < n && arr[s][i] != -1; i++) {
arr[arr[s][i]][0] = arr[s][i];
arr[s][i] = -1;
}
// empty d
for (i = 0; i < n && arr[d][i] != -1; i++){
arr[arr[d][i]][0] = arr[d][i];
arr[d][i] = -1;
}
// now move s to d
i = 1;
while (arr[d][i] != -1) {
i++;
}
arr[d][i] = arr[s][0];
arr[s][0] = -1;
}
else if (strcmp (str2, "over") == 0) {
(void)str2; /* no-op prevents empty scope */
}
else {
continue;
}
}
else if (strcmp (str2, "pile") == 0) {
(void)str2;
}
else {
continue;
}
(note: the final else is not needed)
Complete Code
While I am still unclear on what your logic is supposed to do, handing the input can be done as shown above. Fixing the logic is left to you.
#include <stdio.h>
#include <string.h>
#define MAXC 512 /* if you need a constant, #define one (or more)
* ( don't skimp on buffer size!! )
*/
int main (void) {
char buf[MAXC]; /* buffer to store all user input */
int n; /* if possible, declare variables at beginning of scope */
fputs ("please input n for (n x n array): ", stdout);
if (!fgets (buf, MAXC, stdin)) { /* read all user input with fgets() */
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &n) != 1) { /* validate every conversion */
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
int arr[n][n]; /* VLA's are an 'optional' feature since C11 */
for (int i = 0; i < n; i++) { /* i, j can be decalred with loop scope */
for (int j = 0; j < n; j++) {
arr[i][j] = -1;
}
arr[i][0] = i;
}
while (strcmp (buf, "quit")) { /* loop until quit */
char str1[MAXC] = "", str2[MAXC] = "";
int s = 0, d = 0;
while (1) { /* loop continually */
fputs ("enter str1 s str2 d: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read / validate input */
puts ("(user canceled input)");
return 0;
}
buf[strcspn (buf, "\n")] = 0; /* trim '\n' from end of buf */
if (strcmp (buf, "quit") == 0) /* if "quit", break */
break;
/* parse and validate separate values, always protect array bounds */
if (sscanf (buf, "%511s %d %511s %d", str1, &s, str2, &d) != 4) {
fputs (" error: invalid format or integer input.\n", stderr);
continue;
} /* validate range of integers (negated conditions are confusing) */
if ((0 <= s && s < n) && (0 <= d && d < n))
break; /* exit loop on good input */
else /* otherwise, handle error */
fputs (" error: value for s or d out of range.\n", stderr);
}
if (strcmp (str1, "move") == 0) { /* handle move */
if (strcmp (str2, "onto") == 0) { /* onto? */
int i; /* declare i in scope needed */
// empty s
for (i = 0; i < n && arr[s][i] != -1; i++) {
arr[arr[s][i]][0] = arr[s][i];
arr[s][i] = -1;
}
// empty d
for (i = 0; i < n && arr[d][i] != -1; i++){
arr[arr[d][i]][0] = arr[d][i];
arr[d][i] = -1;
}
// now move s to d
i = 1;
while (arr[d][i] != -1) {
i++;
}
arr[d][i] = arr[s][0];
arr[s][0] = -1;
}
else if (strcmp (str2, "over") == 0) {
(void)str2; /* no-op prevents empty scope */
}
else {
continue;
}
}
else if (strcmp (str2, "pile") == 0) {
(void)str2;
}
else {
continue;
}
}
// print results
for (int i = 0; i < n; i++) {
printf ("%d:\n", i);
for (int j = 0; j < n; j++) {
if (arr[i][j] != -1)
printf (" % 3d", arr[i][j]);
else
fputs (" [ ]", stdout);
}
putchar ('\n');
}
}
Example Use/Output
With intentional errors in input:
$ ./bin/vla_quit
please input n for (n x n array): 5
enter str1 s str2 d: move 2 onto 3
enter str1 s str2 d: move bananas onto gorillas
error: invalid format or integer input.
enter str1 s str2 d: move 1 onto 4
enter str1 s str2 d: move -1 onto 2
error: value for s or d out of range.
enter str1 s str2 d: move 0 onto 2
enter str1 s str2 d: quit
0:
[ ] [ ] [ ] [ ] [ ]
1:
[ ] [ ] [ ] [ ] [ ]
2:
[ ] [ ] [ ] [ ] [ ]
3:
[ ] [ ] [ ] [ ] [ ]
4:
[ ] [ ] [ ] [ ] [ ]
Always compile with warnings-enabled, and do not accept code until it compiles without warning. To enable warnings add -Wall -Wextra -pedantic to your gcc/clang compile string (also consider adding -Wshadow to warn on shadowed variables). For VS (cl.exe on windows), use /W3. All other compilers will have similar options. Read and understand each warning -- then go fix it. They will identify any problems, and the exact line on which they occur. You can learn a lot by listening to what your compiler is telling you.
Let me know if you have further questions.
I am facing some problems in my C assignment program:
At option #4, the grade only sorted to descending, while at #5, the grade won't change, only the name of the students and their scores are swapped.
At option #8, the string and float that inputted from file won't show up and I want the option 8 to be flexible (show from file when file was inputted via option #7 or only show the input from #1 menu option). Here is the example of the file:
80.64 John
90.40 Jane
78.00 Jake
The code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Studata{
float min, max;
int many;
char *max1, *min1, gx, gn;
}studata;
struct Student{
char name[100], grade;
float score[100];
};
float average(struct Student student[100]){
float sum;
for(int i=0; i<student.many; i++){
sum += studata[i].score;
}
return sum/(float)student.many;
}
void MM(struct Student student[100]){
int i;
studata.min = 0;
studata.max = 100;
for (i=0; i<studata.many; i++){
if(*student[i].score > studata.min){
studata.min = student[i].score;
studata.min1 = student[i].name;
studata.gn = student[i].grade;
}
}
for (i=0; i<studata.many; i++){
if(student[i].score < studata.min){
studata.max = student[i].score;
studata.max1 = student[i].name;
studata.gx = student[i].grade;
}
}
}
void swapname(char *a, char *b){
char z[100];
strcpy(z, a);
strcpy(a, b);
strcpy(b, z);
}
void swapscore(float a, float b){
float temporary = a;
a = b;
b = temporary;
}
void swapgrade(char A1, char B1) {
char C1 = A1;
A1 = B1;
B1 = C1;
}
void Bubblesort(int mode, struct Student student[100]) {
int i, j;
if(mode == 1) {
for (i=0; i<studata.many; i++) {
for (j=i+1; j<studata.many; j++) {
if(student[j].score > student[i].score) {
swapname(student[i].name, student[j].name);
swapscore(student[i].score, student[j].score);
swapgrade(student[i].grade, student[j].grade);
}
}
}
}
else if(mode == 0) {
for(i=0; i<studata.many; i++) {
for(j=i+1; j<studata.many; j++) {
if(student[j].score < student[i].score) {
swapname(student[i].name, student[j].name);
swapscore(student[i].score, student[j].score);
swapgrade(student[i].grade, student[j].grade);
}
}
}
}
}
int main(){
struct Student student[100];
int selection=1;
FILE *file;
while (selection <= 8 && selection >= 1) {
printf("\n\n\t-------MENU-------\n\n");
printf("0. Enter Data of Students\n");
printf("1. Calculate the Average\n");
printf("2. Show Maximum and Minimum\n");
printf("3. Sort Score Ascending\n");
printf("4. Sort Score Descending\n");
printf("5. Save Scores\n");
printf("6. Load Scores from File\n");
printf("7. Load All Data\n");
printf("Choice (Other than 1-8 to Exit): ");
scanf("%d", &selection);
if(selection == 1) {
printf("=============================\n");
printf("\nHow many students would you like to input: ");
scanf(" %d", &studata.many);
for (int i=0; i<studata.many; i++) {
printf("\nStudent-%d Name\t: ", i+1);
scanf(" %[^\n]s", student[i].name);
printf("Student-%d Score\t: ", i+1);
scanf(" %f", &student[i].score);
while(student[i].score > 100 || student[i].score < 0) {
printf("Hey, wrong input, please input correctly, okay?");
printf("\nStudent-%d Score\t: ", i+1);
scanf(" %f",&student[i].score);
}
if (student[i].score <= 100 && student[i].score >= 90 ) {
student[i].grade= 'A';
}
else if (student[i].score < 90 && student[i].score >= 80) {
student[i].grade= 'B';
}
else if (student[i].score < 80 && student[i].score >=70) {
student[i].grade= 'C';
}
else if (student[i].score < 70 && student[i].score >=60) {
student[i].grade= 'D';
}
else if (student[i].score < 60 && student[i].score >=50) {
student[i].grade= 'E';
}
else {
student[i].grade = 'F';
}
}
}
else if(selection == 2) {
printf("=============================\n");
printf("Average of Score is %.2f", average(student));
}
else if(selection == 3) {
MM(student);
printf("=============================\n");
printf("Minimum\t: %s || %4.2f || %c\n", studata.max1, studata.max, studata.gx);
printf("Maximum\t: %s || %4.2f || %c\n", studata.min1, studata.min, studata.gn);
}
else if(selection == 4) {
printf("=============================\n");
Bubblesort(0,student);
for(int i=0; i<studata.many; i++) {
printf(" %s : %5.2f --> %c\n", student[i].name, student[i].score, student[i].grade);
}
}
else if(selection == 5) {
printf("=============================\n");
Bubblesort(1,student);
for(int i=0; i<studata.many; i++) {
printf(" %s : %5.2f --> %c\n", student[i].name, student[i].score, student[i].grade);
}
}
else if(selection == 6) {
char filename[100];
printf("=============================\n");
printf("Name of the file (with ext.): ");
scanf(" %[^\n]s", filename);
file = fopen(filename, "w");
for(int i=0; i<studata.many; i++) {
fprintf(file,"%.2f %s\n", student[i].score, student[i].name);
}
fclose(file);
}
else if(selection == 7) {
char filename[100];
char sub_ch;
int i;
printf("Enter name of file you want to open (with extension): ");
scanf(" %[^\n]s", filename);
file = fopen(filename, "r");
while (file == NULL) {
printf("I'm Error! Reinput? (Y/n): ");
scanf("%c", &sub_ch);
if(sub_ch == 'Y') {
printf("Enter name of file you want to open (with extension): ");
scanf(" %[^\n]s", filename);
}
file = fopen(filename, "r");
if(sub_ch == 'n') {
exit(1);
}
}
printf("=============================\n");
fscanf(file, "%f %s", &student[i].score, student[i].name);
while (!feof(file)) {
if (student[i].score <= 100 && student[i].score >= 90 ) {
student[i].grade= 'A';
}
else if (student[i].score < 90 && student[i].score >= 80) {
student[i].grade= 'B';
}
else if (student[i].score < 80 && student[i].score >=70) {
student[i].grade= 'C';
}
else if (student[i].score < 70 && student[i].score >=60) {
student[i].grade= 'D';
}
else if (student[i].score < 60 && student[i].score >=50) {
student[i].grade= 'E';
}
else {
student[i].grade= 'F';
}
printf("%s %8.2f --> %c\n", student[i].name, student[i].score, student[i].grade);
fscanf(file, "%f %s", &student[i].score, student[i].name);
}
fclose(file);
}
else if(selection == 8) {
printf("=============================\n");
for (int i=0; i<studata.many; i++) {
printf("Name || Score || Grade\t: %s || %3.2f || %c\n", student[i].name, student[i].score, student[i].grade);
}
}
}
return 0;
}
I don't know what to do again after I tried to give pointer on every possible variable.
Expanding on the tips above, the thing that makes your code so difficult to read (and maintain) is you ignore (2) from the comment above. Your implementation (the logic applied to your data) is not separate from your interface (how you interact with the user). Instead you have your code all jumbled together in a menu that spans screenfuls of lines.
Keeping your implementation separate from interface keeps the logic for each separate and readable. Here are some additional areas where you can improve both the interface code and the implementation:
Don't Clutter The Implementation With Unnecessary Repeated Output Function Calls
During compilation, the compile with concatenate all adjacent string-literals that a separate only by whitespace. That means you do not need 10 separate calls to printf() to output your menu. In fact, since there are no conversions necessary in the menu you display, you don't need to call the variadic printf() function at all, much less call it 10 times. Since you want end-of-line control, a single call to fputs() is all you need, e.g.
fputs ("\n\n\t-----------MENU-----------\n\n"
" 0. Enter Data of Students\n"
" 1. Calculate the Average\n"
" 2. Show Maximum and Minimum\n"
" 3. Sort Score Ascending\n"
" 4. Sort Score Descending\n"
" 5. Save Scores\n"
" 6. Load Scores from File\n"
" 7. Load All Data\n\n"
"Choice (Other than 1-8 to Exit): ", stdout);
If you wanted a '\n' output at the end, then simply using puts() would do. A single call makes your menu readable.
Create Logical Functions To Implement Your Interface
You don't need to include the 10 lines of menu within the while loop in the body of your code. It makes things much more readable and maintainable if you create a short function to display the menu such as:
void show_menu (void)
{
fputs ("\n\n\t-----------MENU-----------\n\n"
" 0. Enter Data of Students\n"
" 1. Calculate the Average\n"
" 2. Show Maximum and Minimum\n"
" 3. Sort Score Ascending\n"
" 4. Sort Score Descending\n"
" 5. Save Scores\n"
" 6. Load Scores from File\n"
" 7. Load All Data\n\n"
"Choice (Other than 1-8 to Exit): ", stdout);
}
Then your main loop can be kept readable, e.g.
while (selection <= 8 && selection >= 1) {
show_menu();
That is far more readable and easier to maintain than 10 printf() functions at the beginning of your main loop.
Validate EVERY Input To Your Program
You must validate ever input to your program and every conversion. What happens if the user slips reaching for '4' and instead presses 'e' for the menu choice? Try it.
scanf("%d", &selection);
With no validation, you invoke Undefined Behavior when selection (which now holds an indeterminate value) is access in if(selection == 1). At minimum you must catch the error and exit, e.g.
if (scanf("%d", &selection) != 1) {
fputs ("error: invalid integer input.\n", stderr);
exit (EXIT_FAILURE);
}
To gracefully handle the error, you would need to know that with scanf(), when a matching-failure occurs, character extraction from the input stream ceases at that point leaving the offending characters causing the failure unread in the input stream. You must clear the offending characters before the next attempted input or the input will fail again for the same reason resulting in an infinite loop in many cases. There are hundreds of answers on this site showing how to correctly use scanf().
The better ones explain that you do not take user input with scanf(), but instead use fgets() with an adequately sized buffer (character array) and then use sscanf() to retrieve any values from the buffer you need -- which eliminates any possibility of characters being left unread in your input stream.
Don't Clutter Your Interface With Your Implementation
There is no reason the if .. else if .. else if ... logic for determining the letter grade should be in the middle of your program loop. Just as with show_menu() above, that implementation should be in a short function you can call from your main loop, e.g.
char get_ltrgrade (float f) /* return letter grade given float score */
{
if (f >= 90) return 'A';
else if (f >= 80) return 'B';
else if (f >= 70) return 'C';
else if (f >= 60) return 'D';
else return 'F';
}
Then instead of a dozen lines of implementation after if(selection == 1) , you simply have a short call to get_ltrgrade();
Match Your Data Structures To The Data You Have
Your use of struct Student and struct Studata does not fit the input data you show. The input data you show has one score per student. To match your data, struct Student alone would do. Now if you have many classes of students, then struct Studata starts to make more sense. float score[100]; would allow multiple grades per-student, but again that doesn't match the data you show, and you have no counter within struct Student to track the number of valid grades for that student. From what you show, float score: makes more sense.
Sort Arrays With qsort()
C provides qsort() to handle sorting arrays of any type. The qsort() function will be orders of magnitude more efficient than any sort you write and it has been tested for decades. New C programmers usually avoid qsort() because it requires writing a short compare() function that is generally less than 5-10 lines of code telling qsort() how to compare elements of the array. It is actually quite easy.
For explantion of the compare function used by qsort and how to use it, see How to sort an array of a structure using qsort? and further detail in How can I stop taking inputs from user when I press the key 'q'?.
The following puts together the pieces of your implementation that reads the student data from the file, assigning a letter grade to the score and storing all data in an array of struct. The program then calls qsort() to sort the array of struct and outputs the data. Since you include char grade; (char ltrgrade; below), the grade is assigned when the data is read from the file, eliminating the all need to compute the grade within the middle of your implementation.
#include <stdio.h>
#include <stdlib.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
#define MAXN 128
#define MAXS 256
typedef struct { /* typedef allows use of student_t as type */
char name[MAXN],
ltrgrade;
float score;
} student_t;
char get_ltrgrade (float f) /* return letter grade given float score */
{
if (f >= 90) return 'A';
else if (f >= 80) return 'B';
else if (f >= 70) return 'C';
else if (f >= 60) return 'D';
else return 'F';
}
/* qsort compare by student_t->scoore (descending),
* change to >, < for ascending sort.
*/
int compare_score (const void *a, const void *b)
{
student_t *sa = (student_t*)a,
*sb = (student_t*)b;
return (sa->score < sb->score) - (sa->score > sb->score);
}
int main (int argc, char **argv) {
char buf[MAXC]; /* buffer to hold each line read from file */
student_t student[MAXS]; /* array of student_t (MAXS of them) */
size_t n = 0; /* array index (counter) */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
/* while array not full, read each line into buf, VALIDATE read */
while (n < MAXS && fgets (buf, MAXC, fp)) {
/* split line into score and name, VALIDATE conversion */
if (sscanf (buf, "%f %127[^\n]", &student[n].score, student[n].name) == 2) {
/* get letter grade based on score */
student[n].ltrgrade = get_ltrgrade (student[n].score);
n += 1; /* increment index only on valid converison */
}
else /* handle error */
fprintf (stderr, "error: invalid line format, student[%zu].\n", n);
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
qsort (student, n, sizeof *student, compare_score); /* sort by score */
for (size_t i = 0; i < n; i++) /* output result */
printf ("%-12s %6.2f %c\n",
student[i].name, student[i].score, student[i].ltrgrade);
return 0;
}
Note: in this case the compare() function takes a whole 2-lines of code (split into 3 for readability). Instead of the conditional (a < b) - (a > b) for sort descending, you could have used b - a, but that is prone to overflow when b is a large positive value and a a large negative value (or vice-versa). The difference of the conditional are used to return -1, 0, 1 for (-1 - a sorts before b), (0 - a and b are equal), and (1 - b sorts before a) which eliminates potential under/overflow.
Also note, qsorting any array, not matter how complicated, takes only 1-line of code:
qsort (student, n, sizeof *student, compare_score); /* sort by score */
Example Use/Output
Given your sample data in the file dat/studata.txt, the program operation and output is:
$ ./bin/studata dat/studata.txt
Jane 90.40 A
John 80.64 B
Jake 78.00 C
Whenever you go to write code that includes a user-interface, work to keep the interface and implementation separate (to the greatest extent possible). In theory, your interface should be a complete stand-alone program that doesn't depend on any given implementation. You should be about to operate and run your menu-system without having to worry about any other data (using dummy data where needed). Not only does this keep your code readable an maintainable, it also allows you to create your implementation logic in small testable units that do not depend on any given interface. This keeps your implementation short, readable and testable. There are always small areas of overlap, but your goal is to minimize and eliminate all but what is absolutely necessary.
There is a lot here, take your time going through it and if you have further questions, just let me know.
I need to convert 3 digit decimal number to binary using C.
My code:
#include <stdio.h>
#define TWO 2
int main() {
int dec_num; // demical number
int i = 0; // loop cunter
printf("type in 3 digits number to convert to binary\n");
scanf("%d", &dec_num);
while (++i <= 12) {
if (dec_num % TWO != 0) {
printf("1") ;
} else if (dec_num % TWO == 0) {
printf("0");
} else if (dec_num <= 0) {
break;
}
dec_num / TWO;
}
return 0;
}
The problem is that the number doesn't get divided by 2 at the end of the while loop how can I fix it?
You did not store the value of dec_num after the division.
dec_num / TWO; //<--------------------
Your while loop condition was also wrong.
while(++i <= 12) //<-------------------
You should perform the division operation until the number is greater than 0
According to the rules of binary to decimal, you should print 1s and 0s in reverse order. But in your code, you have changed the order. In order to fix that We could store the result in an array and then we could print the result in reverse order.
Here is your modified code,
#include <stdio.h>
#define TWO 2
int main()
{
int dec_num; // demical number
int i=0; // loop cunter
printf("type in 3 digits number to convert to binary\n");
int flag = scanf("%d",&dec_num); //<-----check the user input
if(flag!=1)
{
printf("Input is not recognized as an integer");
return 0;
}
int size=0;
int array[120] = {0}; //<-------to store the result
while(dec_num > 0){ //<------- while the number is greater than 0
if(dec_num % TWO !=0){
array[i] = 1;
}
else if(dec_num % TWO ==0){
array[i] = 0;
}
size = ++i; //<------- store the size of result
dec_num = dec_num / TWO; //<------- divide and modify the original number
}
for(i=size-1;i>=0;i--) //<------- print in reverse order
printf("%d",array[i]);
return 0;
}
My solution will assume that the input is a positive number and that maximal decimal 3 digit number can be presented by 10 bits (decimal 999 is binary 1111100111). I will also use the fact that the bit-wise operators are defined by the C standard and should take place. These operations on most of the architectures are very efficient and much faster than / or %.
#include <stdio.h>
#define MAX_BIN_DIGITS 10
#define ERR_INVALID_INPUT_VALUE 1
int main(void)
{
int dec_num; // demical input number
int i = MAX_BIN_DIGITS;
int bin_bit;
// storing the result at zero terminated string
char bin_res[MAX_BIN_DIGITS+1] = {0};
printf("Type in 3 digits number to convert to binary:\n");
if (1 != scanf("%d",&dec_num))
{
printf("Error: Invalid input value!");
return ERR_INVALID_INPUT_VALUE;
}
// Checking for 'i' here just to be safe in case of larger input than expected - 4 digits or more
while(i-- && dec_num)
{
bin_bit = dec_num & 1; // get the LSB
dec_num >>= 1; // remove the LSB from 'dec_num'
bin_res[i] = '0' + bin_bit; // store the LSB at the end as a char
}
// Print the array starting from the most significant bit which is '1'
// NOTE: Need to take care of special case when the input is '0', then 'i'
// will be equal to 'MAX_BIN_DIGITS-1'.
printf("%s\n", (i != MAX_BIN_DIGITS-1) ? &(bin_res[i+1]) ? "0");
return 0;
}
There are multiple problems in your code:
You do not test the return value of scanf(): you get undefined behavior if the user fails to input characters that can be converted to an integer.
You do not test if the number indeed has at most 3 digits.
You iterate up to 12 times, but 10 should suffice since 210 = 1024 which is greater than any 3 digit number
As a matter of fact, it should not even be necessary to limit the number of iterations, since you stop when the number drops to 0.
The tests are inconsistent: num % TWO is either 0 or 1, the second test is redundant and the third test is never executed, so the loop fails to detect proper termination condition.
dec_num / TWO; does not update dev_num, so your loop just keeps printing the least significant bit (and the test while (++i <= 12) is indeed necessary for the loop to stop).
if corrected, this loop would print the bits from the least significant to the most significant, which is probably not the expected behavior.
#define TWO 2 is not strictly speaking a mistake, but does not improve code readability or safety. Local coding rules might bar you from using non trivial numeric constants in the code: such a rule seems counter productive in this particular case.
It is considered good style to always end output lines with a newline.
beware of typos: demical number is cute and loop cunter is intriguing.
Here is a corrected version of your code, using your algorithm, to illustrate the unexpected output (last significant bit to most significant bit):
#include <stdio.h>
int main() {
int dec_num; // decimal number
printf("type in a 3 digit number to convert to binary\n");
if (scanf("%d", &dec_num) != 1) {
printf("invalid input\n");
return 1;
}
if (dec_num < 0 || dec_num > 999) {
printf("invalid value: must have at most 3 digits\n");
return 1;
}
for (;;) {
if (dec_num % 2 != 0) {
printf("1");
} else {
printf("0");
}
dec_num = dec_num / 2;
if (dec_num == 0)
break;
}
printf("\n");
return 0;
}
Here is a recursive version that outputs the bits in the proper order:
#include <stdio.h>
void output_binary(int num) {
if (num > 1)
output_binary(num / 2);
printf("%d", num % 2);
}
int main() {
int dec_num; // decimal number
printf("type in a 3 digit number to convert to binary\n");
if (scanf("%d", &dec_num) != 1) {
printf("invalid input\n");
return 1;
}
if (dec_num < 0 || dec_num > 999) {
printf("invalid value: must have at most 3 digits\n");
return 1;
}
output_binary(dec_num);
printf("\n");
return 0;
}
While you already have a valid answer correcting the failure to update dec_num following division and valid recursive methods provided by others, it is unclear whether you intend to allow entry of negative 3-digit values as well as positive values. An implementation that determines the size of the type and then shifts by one over each of the bits can provide a simple solution that will handle both positive and negative values.
For example the conversion part of the code shifting by 1 for each bit (indexed 31 -> 0) could be as simple as:
int main (void) {
int decnum = 0; /* user input number */
unsigned remain = 0; /* remainder after shift */
size_t nbits = sizeof decnum * CHAR_BIT; /* number of bits for type */
/* read decnum here */
printf ("decimal: %d\nbinary : ", decnum); /* output value */
while (nbits--) { /* write bits 31->0 while 1 bits remain */
if ((remain = decnum >> nbits)) /* shift saving shifted value */
putchar ((remain & 1) ? '1' : '0'); /* bit 0/1 output '0'/'1' */
}
putchar ('\n'); /* tidy up with newline */
}
(note: a simple ternary operator is used to determine whether to output '1' or '0')
The bigger part of your problem is the your failure to check the return of scanf. This is an open invitation for Undefined Behavior. Regardless of what function you use for user-input, you must validate that input succeeded before proceeding further. Otherwise you will invoke undefined behavior when you attempt to access an indeterminate value.
When using scanf in order to require valid input, you must handle three cases each time (1) did the user cancel input by pressing Ctrl+d generating a manual EOF? (Ctrl+z on windows), (2) did a matching or input failure occur? and (3) the good input case. Then, as is your case, you must further validate the input was in the proper range of values (a 3 digit number in your case). Additionally, to require valid input, you must handle any character that remains unread in the input buffer (as is the case for a matching failure, or if the user slipped and typed additional characters after the integer value.
Now it is completely fine to simply validate the input and return on an invalid input regardless of what caused the failure, but to require valid input it is up to you to handle all three cases, check that the input was within the valid range (and even then remove the trailing '\n' left by scanf preparing the input-buffer for whatever additional input may be taken later in your code.
Many times that will require more code than your actual calculation does, but it is critical. For instance in your case, if you wanted to require the user to enter valid input, you could replace the /* read decnum here */ with something similar to:
for (;;) { /* loop continually until valid input or canceled */
int rtn; /* variable to hold scanf return */
fputs ("enter 3 digit integer: ", stdout); /* prompt */
rtn = scanf ("%d", &decnum); /* read value, saving return */
if (rtn == EOF) { /* handle manual EOF cancelation */
fputs ("(user canceled input)\n", stderr);
return 1;
}
if (rtn == 0) { /* handle input failure */
empty_stdin(); /* always empty input buffer */
fputs (" error: invalid integer input.\n", stderr);
} /* validate 3 digit poisitive (or negative) number */
else if (decnum < -999 || (decnum > -100 && decnum < 100)
|| decnum > 999) {
empty_stdin();
fputs (" error: not a 3 digit number.\n", stderr);
}
else { /* handle good input case (break loop) */
empty_stdin();
break;
}
}
note: the helper function empty_stdin(). That can be implemented with getchar() to read and discard any characters causing problems, e.g.
void empty_stdin (void) /* helper function to empty stdin after bad input */
{ /* (as well as the trailing '\n' after good input) */
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
Putting it altogether, you could do something like the following:
#include <stdio.h>
#include <limits.h> /* for CHAR_BIT */
void empty_stdin (void) /* helper function to empty stdin after bad input */
{ /* (as well as the trailing '\n' after good input) */
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
int decnum = 0; /* user input number */
unsigned remain = 0; /* remainder after shift */
size_t nbits = sizeof decnum * CHAR_BIT; /* number of bits for type */
for (;;) { /* loop continually until valid input or canceled */
int rtn; /* variable to hold scanf return */
fputs ("enter 3 digit integer: ", stdout); /* prompt */
rtn = scanf ("%d", &decnum); /* read value, saving return */
if (rtn == EOF) { /* handle manual EOF cancelation */
fputs ("(user canceled input)\n", stderr);
return 1;
}
if (rtn == 0) { /* handle input failure */
empty_stdin(); /* always empty input buffer */
fputs (" error: invalid integer input.\n", stderr);
} /* validate 3 digit poisitive (or negative) number */
else if (decnum < -999 || (decnum > -100 && decnum < 100)
|| decnum > 999) {
empty_stdin();
fputs (" error: not a 3 digit number.\n", stderr);
}
else { /* handle good input case (break loop) */
empty_stdin();
break;
}
}
printf ("decimal: %d\nbinary : ", decnum); /* output value */
while (nbits--) { /* write bits 31->0 while 1 bits remain */
if ((remain = decnum >> nbits)) /* shift saving shifted value */
putchar ((remain & 1) ? '1' : '0'); /* bit 0/1 output '0'/'1' */
}
putchar ('\n'); /* tidy up with newline */
}
Example Use/Output
$ ./bin/prnbin3digit
enter 3 digit integer: why?
error: invalid integer input.
enter 3 digit integer: -75
error: not a 3 digit number.
enter 3 digit integer: 1024
error: not a 3 digit number.
enter 3 digit integer: 127 and more junk
decimal: 127
binary : 1111111
Or the case with a negative value:
$ ./bin/prnbin3digit
enter 3 digit integer: -127
decimal: -127
binary : 11111111111111111111111110000001
Or the case where the user cancels input:
$ ./bin/prnbin3digit
enter 3 digit integer: (user canceled input)
While this ended up being longer than what a simple right-shift implementation of the conversion would have been, it is worth building good user-input habits now to avoid untold problems later. Look things over and let me know if you have further questions.
Here's my code:
include <stdio.h>
int main()
{
char str[1000], ch;
int i, frequency = 0;
printf("Enter a string: ");
gets(str);
printf("Enter a character to find the frequency: ");
scanf("%c",&ch);
for(i = 0; str[i] != '\0'; ++i)
{
if(ch == str[i])
++frequency;
}
printf("Frequency of %c = %d", ch, frequency);
return 0;
I figured that the frequency of characters code I came up with is similar. How to implement the character which appears more / less often in standard input or text file?
Also, should I use StreamReader sr = new StreamReader("example.txt") for reading normal text files for this code?
EDIT: Have to use Switch /M for most often and /L for least often.
That's a good start...
include <stdio.h>
int main()
{
char str[1000], ch,lookup_Chars[256];
int i, frequency = 0;
char counter;
printf("Enter a string: ");
gets(str);
printf("Enter a character to find the frequency: ");
scanf("%c",&ch);
for(i = 0; str[i] != '\0'; ++i)
{
lookup_Chars[str[i]]++;
}
for(counter = 0; counter<sizeof(lookup_Chars); counter++)
{
printf("Frequency of %c = %d", counter, lookup_Chars[counter]);
}
return 0;
Never, never, never use gets. It is so insecure and so susceptible to buffer overrun, it has been removed from the C standard library. Use fgets instead, just be aware that fgets will read and include the trailing '\n' in the buffer it fills (just as all legitimate line oriented input functions do, such as POSIX getline). This prevents leaving a '\n' unread in the input buffer (e.g. stdin) following each user input.
You risk Undefined Behavior because you do not validate the contents of str in any way and then you fail to validate the return of scanf to insure a character was read. (the user could cancel input in either case by generating an EOF with Ctrl+d on *nix systems of with Ctrl+z on windoze).
Further, you must understand that scanf will leave characters in the input buffer (as will fgets if the line is longer than the amount of storage you have allocated). That is one of the most common pitfalls new C programmers fall victim to. (failing to validate a complete line of input was read and failing to handle characters that remain in the input buffer)
When taking user input with fgets (which is recommended), since it reads and includes the '\n' in the buffer it fills, you simply check the length of the buffer read with strlen and then make sure the last character is a '\n'.
scanf is full of pitfalls when used for user input. While it can be used, if used correctly with it's return validated and any remaining characters emptied from stdin before your next call to scanf, you have to approach it use that way. In your case ch is the last input for your file, but try taking input for ch before reading the string and see what happens...
Putting it altogether, and adding validations for both str and ch, and adding additional comments in-line below, you could do something similar to the following:
#include <stdio.h>
#include <string.h>
#define MAXS 1024 /* if you need a constant, define one */
int main (void) {
int frequency = 0;
char str[MAXS] = "",
*p = str, /* pointer to str */
ch;
printf ("Enter a string: ");
if (fgets (str, MAXS, stdin)) { /* validate input received */
size_t len = strlen (str); /* get length of str */
if (len && str[len - 1] != '\n') { /* validate all input read */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXS-2);
return 1;
}
}
else { /* if fgets failed - user generated EOF to cancel */
fprintf (stderr, "error: user canceled input (EOF).\n");
return 1;
}
printf ("Enter a character to find the frequency: ");
if (scanf ("%c", &ch) != 1) { /* note: chars will remain in stdin */
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
while (*p != '\n') /* just use a pointer to str */
if (*p++ == ch) /* compare to ch and increment to next char */
frequency++; /* increment frequency if they are equal */
printf ("\nFrequency of %c = %d\n", ch, frequency);
return 0;
}
(note: you could declare char ch[3] = ""; and use fgets to read fgets (ch, sizeof ch, stdin) and then simply compare if (*p++ == *ch) to prevent leaving the '\n' in stdin (but you would still need to validate that it was the final character read, and if not manually empty stdin))
Exammple Use/Output
$ ./bin/freqofc
Enter a string: a man a plan a canal panama
Enter a character to find the frequency: a
Frequency of a = 10
$ ./bin/freqofc
Enter a string: a man a plan a canal panama
Enter a character to find the frequency: p
Frequency of p = 2
$ ./bin/freqofc
Enter a string: a man a plan a canal panama
Enter a character to find the frequency: z
Frequency of z = 0
Look things over, think about the validations that were made, and let me know if you have any further questions.
Using a Frequency Array to Capture Count of all Chars
Using a frequency array allows you to capture the frequency of all characters (or the independent count of any element of a set). Essentially, you use an array initialized to zero with one element for each member of the set you want to count the frequency of each occurrence. Since there are 128 ASCII Characters, you can simply use an array of 128 elements, e.g. int frequency[128] = {0};.
If you look at the link provided, you see the ASCII value of each character corresponds to a value between 0-127, so when looping over each character in the input string, if you increment the array index that corresponds to the character, you end up with the total count for each character in its corresponding element. For example, if p is a pointer to the beginning of str, then you can loop over each character in the string capturing their frequency in the frequency[*p] element of the array:
while (*p != '\n') {
frequency[*p]++; /* increments element corresponding to char *p */
p++; /* note: cast to (int)*p intentional omitted */
}
Now that you have the frequency for every character stored in the frequency, you can simply loop over the elements you are concerned about to determine max/min, etc... Note: the normal printable characters begin with 'space' (ASCII 32, or hex 0x20) and end with '~' (ASCII 126 or hex 0x7e`). So just limit your check of values to the printable range, e.g.
/* loop over printable characters (see ASCII Chart), for max/min */
for (int i = ' '; i <= '~'; i++) {
/* require a frequency of at least 1 for min */
if (frequency[i] && frequency[i] < min) {
min = frequency[i]; /* save least frequent count */
minc = i; /* save least frequent char */
}
if (frequency[i] > max) { /* just find max */
max = frequency[i]; /* save same for max */
maxc = i;
}
}
(note: you can further micro-divide ranges for only lowercase, uppercase, digits, etc..)
Putting that altogether, you can do something similar to the following to report the number of occurrence if the wanted char, the max occurring char, the min occurring char (and then summarize by dumping the frequency of all chars):
#include <stdio.h>
#include <string.h>
#include <limits.h>
#define MAXS 1024 /* if you need a constant, define one */
#define NASCII 128 /* number of ASCII chars (includes non-printing) */
int main (void) {
int frequency[NASCII] = {0},
max = INT_MIN,
min = INT_MAX;
char str[MAXS] = "",
*p = str, /* pointer to str */
ch,
minc = 0,
maxc = 0;
printf ("Enter a string: ");
if (fgets (str, MAXS, stdin)) { /* validate input received */
size_t len = strlen (str); /* get length of str */
if (len && str[len - 1] != '\n') { /* validate all input read */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXS-2);
return 1;
}
}
else { /* if fgets failed - user generated EOF to cancel */
fprintf (stderr, "error: user canceled input (EOF).\n");
return 1;
}
printf ("Enter a character to find the frequency: ");
if (scanf ("%c", &ch) != 1) { /* note: chars will remain in stdin */
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
while (*p != '\n') /* just use a pointer to str */
frequency[(int)*p++]++; /* increment element representing ch */
/* loop over printable characters (see ASCII Chart), for max/min */
for (int i = ' '; i <= '~'; i++) {
/* require a frequency of at least 1 for min */
if (frequency[i] && frequency[i] < min) {
min = frequency[i]; /* save least frequent count */
minc = i; /* save least frequent char */
}
if (frequency[i] > max) { /* just find max */
max = frequency[i]; /* save same for max */
maxc = i;
}
}
/* ouput requested char freq, and max/min chars */
printf ("\nFrequency of %c = %d\n"
"least frequent occurrence: %c = %d\n"
" most frequent occurrence: %c = %d\n\n",
ch, frequency[(int)ch], minc, min, maxc, max);
/* output frequency of all printable chars */
printf ("frequency of all printable characters:\n");
for (int i = ' '; i < '~'; i++)
if (frequency[i])
printf (" '%c' : %d\n", i, frequency[i]);
return 0;
}
Exammple Use/Output
$ ./bin/freqofc2
Enter a string: a man a plan a canal panama
Enter a character to find the frequency: m
Frequency of m = 2
least frequent occurrence: c = 1
most frequent occurrence: a = 10
frequency of all printable characters:
' ' : 6
'a' : 10
'c' : 1
'l' : 2
'm' : 2
'n' : 4
'p' : 2
Adding /L or /M Switches for Least/Max Occurrences
To add command line switches, for a minimum number in a known order, you can simply use the allowable argument count and argument vector parameters to main(), e.g. int main (int argc, char **argv). Example:
int main (int argc, char **argv) {
...
/* validate "/L" or "/M" provided as an argument */
if (argc != 2 || (argv[1][1] != 'L' && argv[1][1] != 'M')) {
fprintf (stderr, "error: insufficient input, req'd /M or /L.\n");
return 1;
}
To test and output either the least or minimum, you simply test which character is present and act accordingly, e.g.
...
if (argv[1][1] == 'L') /* output requested lease or max */
printf ("requested least frequent occurrence: %c = %d\n\n",
minc, min);
else
printf ("requested most frequent occurrence: %c = %d\n\n",
maxc, max);
Putting that together an a complete example would be:
#include <stdio.h>
#include <string.h>
#include <limits.h>
#define MAXS 1024 /* if you need a constant, define one */
#define NASCII 128 /* number of ASCII chars (includes non-printing) */
int main (int argc, char **argv) {
int frequency[NASCII] = {0},
max = INT_MIN,
min = INT_MAX;
char str[MAXS] = "",
*p = str, /* pointer to str */
ch,
minc = 0,
maxc = 0;
/* validate "/L" or "/M" provided as an argument */
if (argc != 2 || (argv[1][1] != 'L' && argv[1][1] != 'M')) {
fprintf (stderr, "error: insufficient input, req'd /M or /L.\n");
return 1;
}
printf ("Enter a string: ");
if (fgets (str, MAXS, stdin)) { /* validate input received */
size_t len = strlen (str); /* get length of str */
if (len && str[len - 1] != '\n') { /* validate all input read */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXS-2);
return 1;
}
}
else { /* if fgets failed - user generated EOF to cancel */
fprintf (stderr, "error: user canceled input (EOF).\n");
return 1;
}
printf ("Enter a character to find the frequency: ");
if (scanf ("%c", &ch) != 1) { /* note: chars will remain in stdin */
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
while (*p != '\n') /* just use a pointer to str */
frequency[(int)*p++]++; /* increment element representing ch */
/* loop over printable characters (see ASCII Chart), for max/min */
for (int i = ' '; i <= '~'; i++) {
/* require a frequency of at least 1 for min */
if (frequency[i] && frequency[i] < min) {
min = frequency[i];
minc = i;
}
if (frequency[i] > max) { /* just find max */
max = frequency[i];
maxc = i;
}
}
/* ouput requested char freq, and max/min chars */
printf ("\nFrequency of %c = %d\n"
"least frequent occurrence: %c = %d\n"
" most frequent occurrence: %c = %d\n\n",
ch, frequency[(int)ch], minc, min, maxc, max);
if (argv[1][1] == 'L') /* output requested lease or max */
printf ("requested least frequent occurrence: %c = %d\n\n",
minc, min);
else
printf ("requested most frequent occurrence: %c = %d\n\n",
maxc, max);
/* output frequency of all printable chars */
printf ("frequency of all printable characters:\n");
for (int i = ' '; i < '~'; i++)
if (frequency[i])
printf (" '%c' : %d\n", i, frequency[i]);
return 0;
}
Let me know if you have any questions.