#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;
}
This is my code for solving the problem "Students Marks Sum" in Hackerrank:
(Link: https://www.hackerrank.com/challenges/students-marks-sum/problem?)
#include <string.h>
#include <math.h>
#include <stdlib.h>
//Complete the following function.
int marks_summation(int* marks, int number_of_students, char gender) {
int result = 0;
switch (gender){
case 'b':
while(marks != NULL){
result += (*marks);
marks += 2;
}
case 'g':
++marks;
while(marks != NULL){
result += (*marks);
marks += 2;
}
break;
}
return result;
}
int main() {
int number_of_students;
char gender;
int sum;
scanf("%d", &number_of_students);
int *marks = (int *) malloc(number_of_students * sizeof (int));
for (int student = 0; student < number_of_students; student++) {
scanf("%d", (marks + student));
}
scanf(" %c", &gender);
sum = marks_summation(marks, number_of_students, gender);
printf("%d", sum);
free(marks);
return 0;
}
When I move this code into an IDE (in my situation, it's DEV C/C++), it was crashed when my debug ran into my function marks_summation.
Error in line:
result += (*marks);
The problem is in your assumption that if you move out of the range of dynamically allocated memory the pointer value will change to NULL:
while(marks != NULL){
result += (*marks);
marks += 2;
}
There are a number of approaches you can pick to iterate over such array.
You can calculate the memory address that will no longer be valid to read and check at each iteration if your pointer is smaller than it:
int marks_summation(int* marks, int number_of_students, char gender) {
int result = 0;
int* marksEnd = marks + number_of_students;
switch (gender){
case 'b':
while(marks < marksEnd){
result += (*marks);
marks += 2;
}
case 'g':
++marks;
while(marks < marksEnd){
result += (*marks);
marks += 2;
}
break;
}
return result;
}
I am learning from C Programming Absolute Beginner's Guide. I had to do the following exercise and I took it upon myself to try to add a:
"Would you like to sort these numbers in ascending order? (Y/N): "
after the numbers are randomly generated. However, I am struggling with this. I have tried if, else if, do while, etc. But I am struggling with it. How would you add that to this code?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int ctr, inner, outer, didSwap, temp, choice;
int nums[10];
time_t t;
srand(time(&t));
//First step is to fill the array with random numbers (from 1 to 10)
for (ctr = 0; ctr < 10; ctr++)
{
nums[ctr] = (rand() % 99 + 1);
}
//Now list the array as it currently is before sorting
puts("\nHere is the list before the sort:");
for (ctr = 0; ctr < 10; ctr++)
{
printf("%d\n", nums[ctr]);
}
//Now sort the array
for (outer = 0; outer < 10; outer++)
{
didSwap = 0; //Becomes 1 (true) if list is not yet ordered
for (inner = outer; inner < 10; inner++)
{
if (nums[inner] < nums [outer])
{
temp = nums[inner];
nums[inner] = nums [outer];
nums[outer] = temp;
didSwap = 1;
}
}
if (didSwap == 0)
{
break;
}
}
//Now list the array as it currently is after sorting
puts("\nHere is the list after the sort:");
for (ctr = 0; ctr <10; ctr++)
{
printf("%d\n", nums[ctr]);
}
return 0;
}
EDITED CODE:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int ctr, inner, outer, didSwap, temp, choice;
int nums[10];
time_t t;
srand(time(&t));
//First step is to fill the array with random numbers (from 1 to 10)
for (ctr = 0; ctr < 10; ctr++)
{
nums[ctr] = (rand() % 99 + 1);
}
//Now list the array as it currently is before sorting
puts("\nHere is the list before the sort:");
for (ctr = 0; ctr < 10; ctr++)
{
printf("%d\n", nums[ctr]);
}
puts("Would you like to sort in ascending order? ");
puts(("(Reply Y or N): "));
scanf(" %d", choice);
choice = toupper(choice);
if (choice = 'Y')
{
//Now sort the array
for (outer = 0; outer < 10; outer++)
{
didSwap = 0; //Becomes 1 (true) if list is not yet ordered
for (inner = outer; inner < 10; inner++)
{
if (nums[inner] < nums [outer])
{
temp = nums[inner];
nums[inner] = nums [outer];
nums[outer] = temp;
didSwap = 1;
}
}
if (didSwap == 0)
{
break;
}
}
//Now list the array as it currently is after sorting
puts("\nHere is the list after the sort:");
for (ctr = 0; ctr <10; ctr++)
{
printf("%d\n", nums[ctr]);
}
}
else if (choice = 'N')
{
puts("Have a good day!");
}
return 0;
}
You should use %c format specifier with char variable to read one character.
You will have to use unary & operator to get a pointer to a variable for scanf().
= is an assignment operator in C. You should use == operator to check equality.
/* remove choice from int variable declaration */
char choice; /* use char */
puts(("(Reply Y or N): "));
scanf(" %c", &choice); /* use %c and & */
choice = toupper(choice);
if (choice == 'Y') /* use == */
{
//Now sort the array
/* omit */
}
else if (choice == 'N') /* use == */
{
puts("Have a good day!");
}
The code will not produce a compiler error, but i am trying to update the memory allocation after every loop if the user types Y, and increase the size of the memory. But after the first while loop, when i try to print the list of numbers using the first for loop, it will just show one number which will be 0.00000. I can't get it to update (which is what I am trying to do in the second if loop). Any help is appreciated.
#include<stdio.h>
#include<stdlib.h>
int main()
{
double *userNum = (double*)malloc(sizeof(double));
double sum = 0;
char userChar = 'Y';
int i = 0;
int j = 0;
if (userNum == NULL) {
printf("Error with memory");
return 1;
}
while (userChar == 'Y' || userChar == 'y') {
printf("Enter a number\n");
scanf("%lf", userNum);
printf("List of numbers:\n");
for (j; j < (i + 1); j++) {
printf( "%lf\n", userNum[j]);
}
printf("More numbers (Y/N)? \n");
getchar();
scanf("%c", &userChar);
if (userChar == 'Y' || userChar == 'y') {
userNum = realloc(userNum, (i + 2) * sizeof(double));
i++;
}
}
return 0;
)
You need to reinitialize your loop counter to 0 when printing out the numbers :
for (j = 0; j < (i + 1); j++) {
printf("%lf\n", userNum[j]);
}
And you need to increment the position in userNum that you scan the numbers into :
scanf("%lf", &userNum[i]);
This is my C Programming assignment. We're required to build a simple game that uses array. Our game is like the popular minesweeper game. At first, we initialise the 20*50 array area. Then we put some bombs randomly in the map. In the game, the player is required to travel from the starting point to the ending point to win the game. When the player moves, the movement will make the arrays hidden so that the user knows where did he start. However, in my case, the system doesn't update and make the array empty after the player moves. Can anyone help me with my 's' code? What is wrong?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define iMAX 20
#define jMAX 50
char array[20][50];
int i; //row
int j; //column
int z; //bomb
int n; //steps counter
int o; //x
int p; //y
o = 0;
p = 0;
int level;
int bomb;
char move;
int steps;
int main() {
printf("Welcome to the BombArray Game!\n");
printf("\nLevel 1 Begineer : 50 bombs\nLevel 2 Intermediate : 100 bombs\nLevel 3 Advance : 200 bombs\n");
printf("\nI want to challenge level ");
scanf_s("%d", &level);
printf("\n");
srand(time(NULL));
for (i = 0; i < 20; i++) {
for (j = 0; j < 50; j++) {
array[i][j] = '*';
}
}
array[0][0] = 'S';
array[19][49] = 'E';
if (level == 1) {
bomb = 50;
}
else if (level == 2) {
bomb = 100;
}
else if (level == 3) {
bomb = 200;
}
for (z = 0; z < bomb; z++) {
i = rand() % 20;
j = rand() % 50;
array[i][j] = '1';
}
do {
system("cls");
for (i = 0; i < iMAX; i++) {
for (j = 0; j < jMAX; j++) {
if (array[i][j] == 'S') {
printf("S");
}
else if (array[i][j] == '*') {
printf("*");
}
else if (array[i][j] == '1') {
printf("*");
}
else if (array[i][j] == 'E') {
printf("E");
}
else if (array[i][j] == '2') {
printf(" ");
}
}
printf("\n");
}
printf("\n\nMoving direction (w:up s:down a:left d:right e:exit): ");
scanf_s(" %c", &move);
printf("Steps? ");
scanf_s("%d", &steps);
if (move == 's') {
for (n = 0; n < steps; n++) {
i = o;
j = p;
i++;
array[i][j] = '2';
o = i;
p = j;
}
}
} while (array[19][49] != 2);
return 0;
}
if (move == 's') {
array[o][p] = '2';
for (n = 0; n < steps; n++) {
i = o;
j = p;
i++;
array[i][j] = '2';
o = i;
p = j;
}
array[o][p] = 'S';
}
You have to delete the S at the Start position and write it at the end position when you move
Some additional things: You don't need that much variables. You can remove i and j (or o and p).
If you enter something others than 1-3 for the level you will have an undefined number of bombs (if you declare the bomb variable as a local variable), therefore you should make a default case.
You never look if you are hitting a bomb, you just overwrite array[i][j] without prove if there's a bomb.
better:
if (move == 's') {
array[i][j] = '2';
for (n = 0; n < steps; n++) {
i++;
if (array[i][j] == '1') {
printf("bomb\n");
return 0;
}
array[i][j] = '2';
}
array[i][j] = 'S';
}