Related
#include<stdio.h>
#include<stdio.h>
#include<string.h>
struct stud
{
char nam[20];
int num;
char letter[5];
};
int main()
{
struct stud s[5];
int i, j;
for(i = 0; i < 5; i++){
printf("Enter the name of student #%d: ", i+1);
scanf("%s", s[i].nam);
printf("Enter the number grade of student #%d: ", i+1);
scanf("%d", &s[i].num);
}
for (j = 0; j < i; j++) {
if (strcmp(s[i].nam, s[j].nam) == 0)
printf("Error. Duplicate name detected.");
}
for(i = 0; i < 5; i++){
if(s[i].num >= 90 )
strcpy(s[i].letter, "A");
else if(s[i].num >= 80)
strcpy(s[i].letter, "B");
else if(s[i].num >= 70)
strcpy(s[i].letter, "C");
else if(s[i].num >= 60)
strcpy(s[i].letter, "D");
else
strcpy(s[i].letter, "F");
}
for(i = 0; i < 5; i++)
printf("\n%s has a %s ", s[i].nam, s[i].letter);
return 0;
}
This program has the user enter 5 names and 5 numeric grades, which will then result in the output of their respective letter grades for that student. I'm trying to make it so if the user enters a duplicate name, and message will print saying they can't do that. My attempt in trying to do this is as follows:
for (j = 0; j < i; j++) {
if (strcmp(s[i].nam, s[j].nam) == 0)
printf("Error. Duplicate name detected.");
}
Where I believe that s[j] is the previous string, and compare to see if it equals 0(duplicate) and prints a message. This obviously doesn't work however so I would like to know how to fix this so it can correctly detect duplicate names. Thank you.
Also I have posted this question before but the person that provided an explanation deleted their response before I could provide further questions and ask for clarification. So I am posting this again with an attempt in seeking further aid in what I did wrong in my code.
At the start of the detection loop, i is already 5, so using s[i] is undefined behavior
In your detection loop, i is invariant. you are just comparing a name against the last one [except for the UB, of course].
You need two loops to compare all names against one another.
Also, using 5 everywhere is a "magic number". Better to use a #define (e.g. SMAX)
In the code below, I use cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Here is the corrected code. It is annotated with the bugs and fixes:
#include <stdio.h>
#include <stdio.h>
#include <string.h>
struct stud {
char nam[20];
int num;
char letter[5];
};
#define SMAX 5 // maximum number of students
int
main()
{
struct stud s[SMAX];
int i, j;
for (i = 0; i < SMAX; i++) {
printf("Enter the name of student #%d: ", i + 1);
scanf("%s", s[i].nam);
printf("Enter the number grade of student #%d: ", i + 1);
scanf("%d", &s[i].num);
}
// NOTE/BUG: i is already SMAX, so using s[i] is UB (undefined behavior)
// NOTE/BUG: i never changes
#if 0
for (j = 0; j < i; j++) {
if (strcmp(s[i].nam, s[j].nam) == 0)
printf("Error. Duplicate name detected.");
}
#else
for (i = 0; i < (SMAX - 1); i++) {
for (j = i + 1; j < SMAX; j++) {
if (strcmp(s[i].nam, s[j].nam) == 0)
printf("Error. Duplicate name detected -- %s\n",s[j].nam);
}
}
#endif
for (i = 0; i < SMAX; i++) {
if (s[i].num >= 90)
strcpy(s[i].letter, "A");
else if (s[i].num >= 80)
strcpy(s[i].letter, "B");
else if (s[i].num >= 70)
strcpy(s[i].letter, "C");
else if (s[i].num >= 60)
strcpy(s[i].letter, "D");
else
strcpy(s[i].letter, "F");
}
// NOTE/BUG: newline should go at the end of the printf to prevent a hanging
// last line
#if 0
for (i = 0; i < SMAX; i++)
printf("\n%s has a %s ", s[i].nam, s[i].letter);
#else
for (i = 0; i < SMAX; i++)
printf("%s has a %s\n", s[i].nam, s[i].letter);
#endif
return 0;
}
UPDATE:
Thanks for the tip! On a side note, how would I make it so while the user is entering the duplicate names, the error message appears and the program ends right there.For example: Enter the name of student 1: dan Enter grade: 87 Enter the name of student 2: dan Enter the grade: 78 Error. No duplicate names allowed. And then the program ends there. –
User234567
Easy enough. I put the duplication detection code into functions.
But, I've added a few more enhancements so this may help you with your learning ;-)
I added reprompting the user if they enter a duplicate.
I hate scanf ;-) I reworked the prompting code by putting it into two functions. It will work better if input is a file. This is useful during testing
I changed the conversion from grade number to grade letter to use a table.
Anyway, here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
struct stud {
char nam[20];
int num;
char letter[5];
};
struct letter {
int num;
const char *letter;
};
#define LET(_num,_let) \
{ .num = _num, .letter = _let }
struct letter letters[] = {
LET(90,"A"),
LET(80,"B"),
LET(70,"C"),
LET(60,"D"),
LET(0,"F"),
LET(0,NULL)
};
#define SMAX 5 // maximum number of students
// chkall -- check entire array for duplicates
int
chkall(const struct stud *s,int smax)
{
int i;
int j;
int dup = 0;
for (i = 0; i < (smax - 1); i++) {
for (j = i + 1; j < smax; j++) {
if (strcmp(s[i].nam, s[j].nam) == 0) {
printf("Error. Duplicate name detected -- %s\n",s[j].nam);
dup += 1;
}
}
}
return dup;
}
// chkone -- check a given entry for duplicate (as they are added)
int
chkone(const struct stud *s,int i)
{
int j;
int dup = 0;
for (j = 0; j < i; j++) {
if (strcmp(s[i].nam, s[j].nam) == 0) {
printf("Error. Duplicate name detected -- %s\n",s[j].nam);
dup += 1;
}
}
return dup;
}
// prompt_string -- prompt user for a string
char *
prompt_string(const char *what,int i,char *buf,size_t siz)
{
static int tty = -1;
// decide if our input is tty or file
if (tty < 0) {
struct winsize ws;
tty = ioctl(0,TIOCGWINSZ,&ws);
tty = (tty >= 0);
}
printf("Enter the %s of student #%d: ", what, i + 1);
fflush(stdout);
char *cp = fgets(buf,siz,stdin);
do {
// handle EOF
if (cp == NULL)
break;
buf[strcspn(buf,"\n")] = 0;
// echo the data if input is _not_ a tty
if (! tty)
printf("%s\n",buf);
} while (0);
return cp;
}
// prompt_number -- prompt user for a number
long long
prompt_number(const char *what,int i)
{
char *cp;
char buf[100];
long long val;
while (1) {
cp = prompt_string(what,i,buf,sizeof(buf));
// handle EOF
if (cp == NULL) {
val = -1;
break;
}
// decode the number
val = strtoll(buf,&cp,10);
if (*cp == 0)
break;
printf("invalid number syntax -- '%s'\n",cp);
}
return val;
}
int
main(void)
{
struct stud s[SMAX];
int i;
for (i = 0; i < SMAX; i++) {
while (1) {
prompt_string("name",i,s[i].nam,sizeof(s[i].nam));
if (! chkone(s,i))
break;
}
s[i].num = prompt_number("number grade",i);
}
// recheck all entries
// this will _never_ report a duplicate because of the chkone above
chkall(s,SMAX);
for (i = 0; i < SMAX; i++) {
for (struct letter *let = letters; let->letter != NULL; ++let) {
if (s[i].num >= let->num) {
strcpy(s[i].letter,let->letter);
break;
}
}
}
for (i = 0; i < SMAX; i++)
printf("%s has a %s\n", s[i].nam, s[i].letter);
return 0;
}
I was assigned to make an user input a 5 digit number, validate it and print it back in a reversed order, I have the reverse part figured out yet I am struggling to figure out how to validate the variable in order to force the user to use 5 digits
#include <stdio.h>
int main() {
int n, inv = 0, inverso;
printf("Introduce un numero de 5 digitos: ");
scanf("%d", &n);
while (n != 0) {
inverso = n % 10;
inv = inv * 10 + inverso;
n /= 10;
}
printf("El numero invertido es = %d", inv);
return 0;
}
I would read into a character string, validate that the length is 5, and all characters are digits. Prompt to repeat if validation failed.
Then simply print that string in reverse order.
For example:
int n, inv = 0, inverso;
char buff[100];
int repeat = 1;
while (repeat) {
printf("Introduce un numero de 5 digitos: ");
scanf("%s", buff);
repeat = 0;
if (strlen(buff) != 5) {
printf("You must type 5 digits\n");
repeat = 1;
continue;
}
for (int i = 0; i < 5; ++i) {
if (!isdigit(buff[i])) {
printf("%c is not a digit\n");
repeat = 1;
continue;
}
}
}
printf("\nEl numero invertido es = ");
for (int i = 4; i >= 0; --i) {
printf("%c", buff[i]);
}
You could reject the input if it is greater than 5 and signal it to the user, or you could truncate the input (after validating it) to prevent overflowing the maximum size. For instance :
if (strlen(string) > 5) string[5] = '\0';
Then continue normally (possibly signaling to the user that its input has been truncated) (the advantage being not to force the user to input again).
Since you're already processing the user input number digit-by-digit in a loop, it makes sense to just add a count variable initialized at 0 that increments each loop iteration:
int count = 0;
while (n != 0) {
inverso = n % 10;
inv = inv * 10 + inverso;
n /= 10;
count++;
}
There are count digits in your user's input.
That said, in this case, unless your homework assignment or exercise prohibit processing the user input as a string instead of an integer, that really is the way to go because it's a lot simpler.
Use fgets() to read the input as a string. Later validate that the input is no longer that 5 digits and parse it with sscanf(). Even better you can avoid converting it and just print it in reverse.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_LENGTH 500
int main()
{
char buff[500] = {0};
char *numbers = "0123456789";
if (!fgets(buff,MAX_LENGTH,stdin))
{
fprintf(stderr,"input error...\n");
exit(EXIT_FAILURE);
}
buff[strcspn(buff, "\n")] = 0; /* removing newline character from the buffer */
if (strspn(buff,numbers) != strlen(buff)) /* making sure input is only numbers */
{
printf("only numbers allowed\n");
exit(EXIT_FAILURE);
}
for (size_t i = 0, len = strlen(buff); i <= len; i++)
{
putchar(buff[len-i]);
}
return 0;
}
sscanf() alternative :
if (!fgets(buff,MAX_LENGTH,stdin))
{
fprintf(stderr,"input error...\n");
exit(EXIT_FAILURE);
}
if (sscanf(buff,"%ld",&number) != 1) /* always check the result of scanf() */
{
fprintf(stderr,"input failure\n");
exit(EXIT_FAILURE);
}
/* reverse the number */
Here is a simple test to check that n has 5 digits:
if (n >= 10000 && n <= 99999) {
// n has 5 digits
} else {
// try again
}
Here is a modified version:
#include <stdio.h>
int main() {
int c, n, inv;
for (;;) {
printf("Introduce un numero de 5 digitos: ");
if (scanf("%d", &n) == 1 && n >= 10000 && n <= 99999)
break;
while ((c = getchar()) != '\n') {
if (c == EOF) // unexpected end of file
return 1;
}
printf("Invalid input, try again\n");
}
inv = 0;
while (n != 0) {
inv = inv * 10 + n % 10;
n /= 10;
}
printf("El numero invertido es = %d\n", inv);
return 0;
}
I would try a more unorthodox way since your input is already a String(char).
#include <stdio.h>
int len;
char buff[100];
int main()
{
scanf("%s", buff);
len = strlen(buff);
}
From there you have the length of the string stored in an int form in variable len.
You will continue by coding the if that handles what happens when len is greater than 5.
Hope i helped!
I have an university project in C programming. I ran into a problem with the following task. My program should order numbers in two arrays. In the first array i must save (the biggest of every fifth element) and that is my problem. I am not sure how to make the loop which reads five elements compare them, take the biggest one, and then continue doing this with the other elements. I am hoping someone to help because I blocked.
#define A 100
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
void show(int x[], int nx); //функция за показване на масивите
float vavedi(int x[], int nx); //функция, чрез която ръчно въвеждаме числата и ги обработваме
FILE* readFile(char* fname); //функция която чете файл и представя съдържанието му като масиви
int main()
{
int call, a = 0, b = 0, mode = 0, i = 0;
int check = 0;
char fail[A];
char* menu[] = {
"PROGRAM STARTED!",
"Enter an option:",
"1 : Write the numbers.",
"2 : Choose from a file.",
"0 : Exit."
};
do {
for (i = 0; i < 5; i++)
printf("%s\n", menu[i]);
check=scanf("%d", &mode);
if (check != 1)
printf("ERROR! Try again!");
switch (mode)
{
case 1: {
//в случай 1 числата се въведждат от потребителя
call = vavedi(a, b);
break;
}
case 2: {
//в случай 2 потребителя използва съществуващ файл
printf("Enter the path of the file you want to open:\n");
scanf("%s", fail);
call = readFile(fail);
if (call == NULL) {
printf("The file doesn't exist! Try again!\n");
}
break;
}
case 0:
break;
default:
{
printf("ERROR! Try again!\n");
}
}
} while (mode != 0);
printf("\nThe program ended!\n");
return 0;
}
void show(int x[], int nx)
{
int k;
for (k = 0; k < nx; k++)
{
printf("\n Element[%d]= %d", k, x[k]);
}
}
float vavedi(int x[], int nx)
{
int call=0;
int enter=0;
int imin, max;
int b[A], c[A];
int i, j, j1, count;
j = 0;
do {
printf("\nCount of the elements:");
scanf("%d", &count);
if (count <= 0 || count > 100)
printf("Invalid input! Try again!\n");
} while (count <= 0 || count > 100);
for (i = 0; i < count; i++)
{
printf("\nEnter an element:");
scanf("%d", &c[i]);
}
printf("\n");
return enter;
}
Update : Including header file mentioned by #pmg.
If i understand correctly, your problem is to find largest element for every five elements:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <limits.h> // Put this after your header files
..
.. //Rest of the code.
..
for (i = 0; i < count; i++)
{
printf("\nEnter an element:");
scanf("%d", &c[i]);
}
size_t size = ceil(count/5.0);
memset(b , INT_MIN , size);
for(int i = 0 ; i < count ; i++)
{
b[i/5] = b[i/5] > c[i] ? b[i/5] : c[i];
}
// Print b like this:
for(i = 0 ; i < size; i++)
printf("%d" , b[i] );
Let's see how this works for array of size 9. array's indices will go from 0 to 8 . If I divide each of them by 5, I get 0 for i = 0 to 4 and 1 for i = 5 to 8. Now we can take max over elements in buckets of 5. You seem to be a beginner, hope this helps you in creating better understanding.
The other problem is the following:
{
int c[A], b[A];
int i = 0, j = 0, k = 0, num;
FILE* fp= NULL; //указател за файл
fp = fopen(fname, "r");
if (fp)
{
while (fscanf(fp, "%d", &c[i]) != EOF)
i++;
num=i;
size_t size = ceil(num/5.0);
memset(b , INT_MIN , size);
for(i = 0 ; i < num ; i++)
{
b[i/5] = b[i/5] > c[i] ? b[i/5] : c[i];
}
printf("\nARRAY1:\n");
for(i = 0 ; i < size; i++)
{
buble(b, i);
printf(" Element[%d]=%1d\n", i, b[i]);
}
printf("\nARRAY2:");
buble(c, num);
show(c, num);
printf("\n");
}
fclose(fp);
return fp;
}
It doesn't calculate the rigth way.
As stated in the title I am trying to find all lower-case letters that are not in a series of words. There are no upper-case letters, digits, punctuation, or special symbols.
I need help fixing my code. I am stuck and do not know where to go from here.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void){
int letters[26];
char words[50];
int i = 0, b = 0;
printf("Enter your input : ");
scanf("%s", words);
for(i = 0; i < 26; i++){
letters[i] = 0;
}
while(!feof(stdin)){
for(b = 0; b < strlen(words) - 1; b++){
letters[ words[b] - 'a']++;
scanf("%s", words);
}
}
printf("\nMissing letters : %c ", b + 97);
return 0;
}
My output is giving me some random letter that I do not know where it is coming from.
Here is a working first implementation.
As well as the comments that have already been made, you should use functions wherever possible to separate out the functionality of the program into logical steps. Your main function should then just call the appropriate functions in order to solve the problem. Each function should be something that is self contained and testable.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_INPUT 20 /* Max input to read from user. */
char *readinput(void);
void find_missing_lower_case(char *, int);
int main()
{
char *user_input = readinput();
int len_input = strlen(user_input);
printf("user input: %s\n", user_input);
printf("len input: %d\n", len_input);
find_missing_lower_case(user_input, len_input);
/* Free the memory allocated for 'user_input'. */
free(user_input);
return 0;
}
char *readinput()
{
char a;
char *result = (char *) malloc(MAX_INPUT);
int i;
for(i = 0; i < MAX_INPUT; ++i)
{
scanf("%c", &a);
if( a == '\n')
{
break;
}
*(result + i) = a;
}
*(result + i) = '\0';
return result;
}
void find_missing_lower_case(char *input, int len_input)
{
int a = 97; /* ASCII value of 'a' */
int z = 122; /* ASCII value of 'z' */
int lower_case_chars[26] = {0}; /* Initialise all to value of 0 */
/* Scan through input and if a lower case char is found, set the
* corresponding index of lower_case_chars to 1
*/
for(int i = 0; i < len_input; i++)
{
char c = *(input + i);
if(c >= a && c <= z)
{
lower_case_chars[c - a] = 1;
}
}
/* Iterate through lower_case_chars and print any values that were not set
* to 1 in the above for loop.
*/
printf("Missing lower case characters:\n");
for(int i = 0; i < 26; i++)
{
if(!lower_case_chars[i])
{
printf("%c ", i + a);
}
}
printf("\n");
}
I figured it out and this is the code I used.
int main(void)
{
int array[26];
char w;
int i=0;
for(i=0; i<26; i++) {
array[i]=0; }
printf("Enter your input: ");
scanf("%c", &w);
while(!feof(stdin)) {
array[w-97] = 1;
scanf("%c", &w); }
printf("Missing letters: ");
for(i=0; i<26; i++) {
if(array[i] == 0) {
printf("%c ", i+97); }
}
printf("\n");
return 0;
}
So basically I want my program to display the following:
(memory address) (16 bytes of hex values) (those hex values in characters)
Now, I have the format correctly, except the following line always returns '0' and thus no characters are being displayed at all:
printf("%c", isgraph(*startPtr)? *startPtr:'.');
Finally, I think I'm using srand and rand correctly, but my array isn't being filled up with random stuff. It's always the same.
Anyway, here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
void DumpMem(void *arrayPtr, int numBytes);
void FillMem(void *base, int numBytes);
int main(void)
{
auto int numBytes;
auto double *doublePtr;
auto char *charPtr;
auto int *intPtr;
srand(time(NULL));
// Doubles
printf("How many doubles? ");
scanf("%d", &numBytes);
doublePtr = malloc(numBytes * sizeof(*doublePtr));
if (NULL == doublePtr)
{
printf("Malloc failed!");
}
printf("Here's a dynamic array of doubles... \n");
FillMem(doublePtr, numBytes * sizeof(*doublePtr));
DumpMem(doublePtr, numBytes * sizeof(*doublePtr));
// Chars
printf("\nHow many chars? \n");
scanf("%d", &numBytes);
charPtr = malloc(numBytes * sizeof(*charPtr));
if (NULL == charPtr)
{
printf("Malloc failed!");
}
printf("Here's a dynamic array of chars... \n");
FillMem(charPtr, numBytes * sizeof(*charPtr));
DumpMem(charPtr, numBytes * sizeof(*charPtr));
// Ints
printf("\nHow many ints? \n");
scanf("%d", &numBytes);
intPtr = malloc(numBytes * sizeof(*intPtr));
if (NULL == intPtr)
{
printf("Malloc failed!");
}
printf("Here's a dynamic array of ints... \n");
FillMem(intPtr, numBytes * sizeof(*intPtr));
DumpMem(intPtr, numBytes * sizeof(*intPtr));
// Free memory used
free(doublePtr);
free(charPtr);
free(intPtr);
}
void DumpMem(void *arrayPtr, int numBytes)
{
auto unsigned char *startPtr = arrayPtr;
auto int counter = 0;
auto int asciiBytes = numBytes;
while (numBytes > 0)
{
printf("%p ", startPtr);
for (counter = 0; counter < 8; counter++)
{
if (numBytes > 0)
{
printf("%02x ", *startPtr);
startPtr++;
numBytes--;
}
else
{
printf(" ");
}
}
printf(" ");
for (counter = 0; counter < 8; counter++)
{
if (numBytes > 0)
{
printf("%02x ", *startPtr);
startPtr++;
numBytes--;
}
else
{
printf(" ");
}
}
printf(" |");
// 'Rewind' where it's pointing to
startPtr -= 16;
for (counter = 0; counter < 16; counter++)
{
if (asciiBytes > 0)
{
printf("%c", isgraph(*startPtr)? *startPtr:'.');
asciiBytes--;
}
else
{
printf(" ");
}
}
puts("| ");
}
}
void FillMem(void *base, int numBytes)
{
auto unsigned char *startingPtr = base;
while (numBytes > 0)
{
*startingPtr = (unsigned char)rand;
numBytes--;
startingPtr++;
}
}
Why am I not getting random values inside the array? And why is my conditional statement always 'false'?
You're filling your array with the low-order byte of the function pointer to rand, not with a random number. You need to call the function:
*startingPtr = (unsigned char)rand();
You also aren't incrementing startPtr while printing out the character data. You need a startPtr++ in there:
if (asciiBytes > 0)
{
printf("%c", isgraph(*startPtr)? *startPtr:'.');
startPtr++;
asciiBytes--;
}
As it stands, your program just prints the first byte over and over again, and then goes on to the next line and prints an identical one to the previous line.