ADD elements of an array with a given condition - c

There is an array of n students( stu[n]).If gender is boy then my code adds
for boy b, 2nd,4th,6th,........even position elements of array and
for girl g, 1st,3rd,5th....odd position elements of array.
1> Gender of boys denoted by b.
2> Gender of girls denoted by g.
Input info>>
The 1st line contains n, denoting the number of students in the class, hence the number of elements in the array.
Each of the subsequent lines contains the marks of n students .
The last line contains gender b/g;
Output info>>
The output should contain the sum of all the alternate elements of marks as explained above.
#include <stdio.h>
int main() {
int n,i;
scanf("%d",&n);//n denotes number of students.
int stu[n],sum=0;
for(i=1;i<=n;++i)
scanf("%d",&stu[i]);//for accepting input in array.
char gen;
scanf("%s",&gen);//for accepting input gender b/g.
for(i=1;i<=n;i++){
if(gen=='g' && i%2!=0){ //girl g adds alternate odd position elements.
sum=sum+stu[i];
printf("%d",sum);
}
else if(gen=='b' && i%2==0){ //boy b adds alternate even position elements.
sum=sum+stu[i];
printf("%d",sum);
}
}
//code
return 0;
}
Sample Input
3
3
2
5
b
Sample Output
8
explanation>>
marks=[3,2,5] and gender = b so it will add 3+5(even position 0,2 alternate elements). If gender in place of b is g then it will produce output = 2.
My code is shows output of 0 in all test cases.

You have the major problem in
int n,i;
int stu[n],sum=0;
here, n being a uninitialized local scoped variable with automatic storage, the initial value is indeterminate.
Now, since the address of the variable was never taken and it has a type that can have trap representation, attempt to use the value (int stu[n]) will invoke undefined behavior.
You need to scan in the value into n first, then use that to define the VLA stu. Something like
int n,i;
scanf("%d",&n);//n denotes number of students.
// **Note: please check for errors in input with scanf return value.**
int stu[n],sum=0; // here, n has the scanned value.
That said,
char gen;
scanf("%s",&gen);
is also horribly wrong, you want to scan in a char, not a string, and with the address of a plain char variable, %s conversion specification would be UB, again. You should use %c and discard any whitespaces which is present in buffer altogether.

You're making things more complicated than they need to be. Here is how you can possibly do:
#include <stdio.h>
int main(void)
{
int mark;
int b = 0;
int g = 0;
char students_marks[5];
for (int i=0; i<5; i++) {
scanf("%d", &mark);
students_marks[i] = mark;
}
for (int i=0; i<5; i++) {
if (i%2 == 0) b += students_marks[i];
if (i%2 == 1) g += students_marks[i];
}
printf("Boys: %d\nGirls: %d\n", b, g);
return 0;
}

You should probably not use an array, and just ignore the first data point. It is (probably) easier to use a linked list. Or maybe just use two lists, alternating the inputs between them. And I would definitely not use scanf. If you are new to C, do NOT waste your time learning the foibles of the scanf format string language. The only time scanf is ever useful is in introductory courses where instructors incorrectly believe that you will be able to get input more quickly than if you spend time learning other methods. But in fact you will end up burning more time learning when to use spaces in the format string that you saved by not learning fread. After your introduction to C, you will (almost) never use scanf. Also, it seems like a horrible design to put the discriminant at the end of the input. The values to be summed (the gender) should be given as a command line argument. That said, you could just do:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
FILE *
xfopen(const char *path, const char *mode)
{
FILE *rv;
if( (rv = fopen(path, mode)) == NULL ) {
perror(path);
exit(EXIT_FAILURE);
}
return rv;
}
int
main(int argc, char **argv)
{
int i;
int size; /* Should probably be size_t. Skipping that for now. */
FILE *in = argc > 1 ? xfopen(argv[1], "r") : stdin;
int sum = 0;
if( fscanf(in, "%d", &size) != 1 || size <= 0 ) {
fprintf( stderr, "invalid input\n" );
exit(EXIT_FAILURE);
}
int grades[size];
for( i = 0; i < size; i++ ) {
if(fscanf(in, "%d", grades + i) != 1) {
fprintf( stderr, "invalid input on line %d\n", i );
exit(EXIT_FAILURE);
}
}
char gender;
if(fscanf(in, " %c", &gender) != 1) {
fprintf( stderr, "invalid input on line %d\n", i );
exit(EXIT_FAILURE);
}
if(strchr("bBgG", gender) == NULL) {
fprintf( stderr, "invalid gender: %c\n", gender);
exit(EXIT_FAILURE);
}
for( i = tolower(gender) == 'b'; i < size; i += 2 ) {
sum += grades[i];
}
printf("sum: %d\n", sum);
}

Hmm… i changed your code a Little bit and hope this runs as described.
int main() {
int n, index, sum=0;
int* stu;
scanf("%d", &n); // input number of studens
if(n>0)
stu = malloc(n*sizeof(int)); // allocate memory
else
return 0;
for(index=0;index<n;index++)
scanf("%d", &stu[index]);
char gen;
scanf("%c", &gen);
for(index=0; index<n; index++){
if(gen=='g' && index%2!=0) {
sum += stu[index];
printf("%d", sum);
} else if(gen=='b' && index%2==0) {
sum += stu[index];
printf("%d", sum);
}
}
return 0;
}

Related

Storing an array in C

Context: I need to write a program that will accept inputs which will be stored into the array. Before storing in to the array, the inputted number must be checked if it already exists in the array or not. If it does not exist, it is stored into the array. If it exists, another input will be asked.
Now, my code will get inputs from the user, but the code will only work for the first input. It won't work for the second until the last input. Any pointers?
This is my code:
#include<stdio.h>
#define size 5
main()
{
int i;
arr[size];
input;
printf("This program will accept ");
printf("unique inputted numbers that will be stored");
printf(" in an array\n");
for(i = 0;i < size;i++)
{
printf("Enter input: ");
scanf("%d",&input);
if (unique(arr,input,i))
arr[i] = input;
else
i--;
//decrement i because ask for input again
}
for(i = 0;i < size;i++)
printf("%d ",arr[i]);
}
int unique(int arr[],int input,int i)
{
int n, z;
n = 0;
z = 1;
while(i > n)
{
if(arr[n] == input)
{
scanf("%d",&n);
z = 0;
break;
}
else
n=1;
break;
}
return z;
}
Your code is wrong at multiple levels:
The logic in the unique function is wrong.
Doing the scanf in the unique function is extremely bad design. The only thing unique should do is return 0 if input is already in the array.
You have used implicit variable declarations here: arr[size]; input;, it should be int arr[size]; int input;.
You should use descriptive variable names which makes your code easier to understand.
This is a working example (explanations in comments).
#include <stdio.h>
#define SIZE 5 // use capitals for macros (this is a convention)
int unique(int arr[], int value, int arrsize)
{
for (int i = 0; i < arrsize; i++)
{
if (arr[i] == value)
{
return 0; // value found in array
}
}
return 1; // value not found in array
}
void Test(int arr[], int arrsize, int value, int expected)
{
if (unique(arr, arrsize, value) != expected)
printf("Test failed for value %d\n", value);
}
void runtests()
{
int arr[] = { 1,2,3 };
Test(arr, 4, sizeof(arr) / sizeof(*arr), 1);
Test(arr, 1, sizeof(arr) / sizeof(*arr), 0);
Test(arr, 3, sizeof(arr) / sizeof(*arr), 0);
}
#define size 5
int main()
{
int i;
int arr[size]; // declare int variable
int input; // declare int variable
printf("This program will accept unique inputted numbers that will be stored in an array\n");
for (i = 0; i < size; i++)
{
printf("Enter input %d: ", i + 1);
scanf("%d", &input);
if (unique(arr, input, i)) // value already in the array?
arr[i] = input; // no => put it there
else
{ // yes => ask again
printf(" >> %d is already in the array\n");
i--;
}
}
for (i = 0; i < size; i++)
printf("%d ", arr[i]);
}
There are two more functions Test and runtests in this code. They are not called by this code, but they can be very useful for debugging. As an exercise try to understand why they can be useful during the debug phase of your code.
You're close, but overcomplicating it slightly.
Let's take a step back and think about this at a high level. You want to store unique inputs in the array, up to the size of the array. In pseudocode:
while array not full
prompt for and read next input
if input not already in array
store input
else
write a message
end if
end while
What's really key is that you only need one input statement - your unique function should only check for the presence of the input value in the array and return true or false. It shouldn't do any input of its own.
So your main loop is more like
while ( i < size )
{
fputs( "Gimme a number: ", stdout );
/**
* Break out of the loop if there's an error
* on input.
*/
if ( scanf( "%d", &input ) != 1 )
break;
if ( unique( arr, i, input ) )
arr[i++] = input;
else
printf( "%d already exists in the array, try again.\n", input );
}
All your unique function needs to do is cycle through the elements of the array. By calling unique with i instead of size it will only check array elements that have been written to so far and not bother with unassigned elements. This way you don't have to make sure that all of the array elements have been initialized to some known, out-of-band value that's guaranteed to compare unequal to any valid input.
You'll need to compile against C99 or later and include stdbool.h to use the bool type and the true and false constants.
#include <stdbool.h>
...
bool unique( int *arr, size_t size, int input )
{
bool result = true;
for( size_t i = 0; i < size && result; i++ )
if ( arr[i] == input )
result = false;
return result;
}
If you want to get really terse, you could directly assign the result of the Boolean expression to result:
for ( size_t i = 0; i < size && result; i++ )
result = (arr[i] == input);
but people will hit you. It's perfectly valid code, but a little eye-stabby, and most programmers aren't used to seeing Boolean expressions outside of an if, for, while, or switch statement control expression.
Finally, some suggestions:
Fix your formatting. The compiler doesn't care, but it makes it easier for other people to understand what you're trying to do and to spot mistakes.
The presence of main() in your code suggests you're using C89 or K&R C. C99 did away with implicit int declarations. You really should define main as either int main( void ) or int main( int argc, char **argv ). Furthermore, you should move to a compiler that supports later versions of C (C11 or C18).

I have problem with reading input with blank spaces

I made this function to get input:
void entrada_dados(Time* time, int i){
scanf("%s %d %d", time[i].nome, &time[i].gols_marcados, &time[i].gols_sofridos);
};
The input is in this form:
2
Campinense
23
12
ABC
30
13
The main is:
int main(void) {
int n = 0;
scanf("%d", &n);
for(int i = 0; i < n; i++){
entrada_dados(time, i);
}
....
My problem is when the team name have some space like to "São Paulo". I have tried some forms to solve, but no one solved my problem.
I tried:
void entrada_dados(Time* time, int i){
fscanf(stdin, "%[^\n] %d %d", time[i].nome, &time[i].gols_marcados, &time[i].gols_sofridos);
};
and:
void entrada_dados(Time* time, int i){
fgets(time[i].nome, 100, stdin);
scanf("%d", &time[i].gols_marcados);
scanf("%d", &time[i].gols_sofridos);
}
but in the first case the output have nothing, and second case the output miss some cases. Someone can help me to understand this problem?
Edit 1:
The definition of .name is:
typedef struct Time{
char nome[100];
int gols_marcados;
int gols_sofridos;
} Time;
Edit 2:
Solution:
One way to solve it:
Try two fscanfs fscanf(stdin, " %[^\n]", time[i].nome);
fscanf(stdin, "%d %d", &time[i].gols_marcados, &time[i].gols_sofridos);
Thank you guys.
Because you have to handle strings with spaces, it's better to use fgets for those.
But mixing fgets and scanf doesn't work too well. We can replace scanf with fgets followed by sscanf.
To decode numbers, we can use strtol or sscanf
We take advantage of the fact that each element/member of Time appears on a separate line in the input file, so we can do fgets for every line. This simplifies the code and makes error checking easier.
Here is the refactored code. It is annotated.
I didn't do this, but, if these sequences are done a lot, we can combine some of these sequences in helper functions to reduce some code replication (e.g. a function that combines the fgets followed by the sscanf)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Time {
char nome[100];
int gols_marcados;
int gols_sofridos;
} Time;
// RETURNS: 1=valid, 0=syntax error
int
entrada_dados(Time *timelist, int i)
{
char buf[100];
char *cp;
Time *tim = &timelist[i];
int valid = 0;
do {
// get name
if (fgets(tim->nome,sizeof(tim->nome),stdin) == NULL)
break;
// strip newline
tim->nome[strcspn(tim->nome,"\n")] = 0;
// get number using strtol
if (fgets(buf,sizeof(buf),stdin) == NULL)
break;
tim->gols_marcados = strtol(buf,&cp,10);
if (*cp != '\n')
break;
// get number using sscanf
if (fgets(buf,sizeof(buf),stdin) == NULL)
break;
if (sscanf(buf,"%d",&tim->gols_sofridos) != 1)
break;
// all input is okay
valid = 1;
} while (0);
return valid;
};
int
main(void)
{
int n = 0;
#if 0
scanf("%d", &n);
#else
char buf[100];
if (fgets(buf,sizeof(buf),stdin) == NULL)
exit(1);
sscanf(buf,"%d",&n);
#endif
// allocate sufficient space
Time *timelist = malloc(sizeof(*timelist) * n);
// read in data
int valid = 0;
for (int i = 0; i < n; i++) {
valid = entrada_dados(timelist, i);
if (! valid)
break;
}
// show the data
if (valid) {
for (int i = 0; i < n; i++) {
Time *tim = &timelist[i];
printf("nome='%s' gols_marcados=%d gols_sofridos=%d\n",
tim->nome,tim->gols_marcados,tim->gols_sofridos);
}
}
return 0;
}
Here is the program input:
3
Campinense
23
12
ABC
30
13
São Paulo
17
82
Here is the program output:
nome='Campinense' gols_marcados=23 gols_sofridos=12
nome='ABC' gols_marcados=30 gols_sofridos=13
nome='São Paulo' gols_marcados=17 gols_sofridos=82

Struct and Pointers Stuck

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.

Reading and storing values from csv file in an array using C

#include <stdio.h>
#include <stdlib.h>
int main() {
int c1[100];
char c2[150];
char c3[100];
float c4[100];
float c5[100];
float c6[100];
float c7[100];
char c8[100];
char c9[100];
float c10[100];
char string[10][100];
int i, j;
char c;
FILE *fp1;
fp1 = fopen("sample.csv", "r");
while (1) {
c = fgetc(fp1);
if (c == EOF)
break;
else
printf("%c", c);
}
for (i = 1; i <= 10; i++) {
fscanf(fp1, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d",
&c1[i], &c2[i], &c3[i], &c4[i], &c5[i],
&c6[i], &c7[i], &c8[i], &c9[i], &c10[i]);
}
for (j = 0; j <= 10; j++) {
printf("\n");
printf("%d", c3); //Here i am trying to read the column3 values but getting random integer values.
//This problem continues to every column
}
return 0;
}
I have to read file sample.csv and store values into the array so that I can perform operation on that values.
I am not getting the exact value from the csv file that I have read.
I am getting some random integer value on running the program.
There are many problems in your code:
you do not check if fopen() succeeded.
c must be defined as int for proper end of file testing
you must rewind the file with rewind(fp1); or fseek(fp1, 0L, SEEK_SET); before reparsing the contents with fscanf()
the loop index i must start at 0 instead of 1, because arrays are 0 based.
it is idiomatic in C to use for (i = 0; i < 10; i++) ... to handle 10 lines of input. i <= 10 would iterate 11 times.
you must check the return value of fscanf() to ensure the input stream has the expected format. The format string does not handle empty text fields.
the fscanf() format string is incompatible with the arguments provided
the printf format string "%d\n" in incompatible with the type of the argument: the argument is the array c3 which is passed as a pointer to its first member, not an int as expected.
Simply read a line in a loop until there are no more lines
#include <stdio.h>
#include <string.h>
#define MAX_ITEMS 10000
#define LARGEST_LINE 1000
#define LARGEST_ELEMENT 100
int main(void) {
int c1[MAX_ITEMS];
char c2[MAX_ITEMS][LARGEST_ELEMENT+1]; // large enough for each `c2`
char c3[MAX_ITEMS][LARGEST_ELEMENT+1];
char c4[MAX_ITEMS][LARGEST_ELEMENT+1];
char c5[MAX_ITEMS][LARGEST_ELEMENT+1];
int c6[MAX_ITEMS];
int c7[MAX_ITEMS];
int tmpc1;
char tmpc2[LARGEST_ELEMENT+1];
char tmpc3[LARGEST_ELEMENT+1];
char tmpc4[LARGEST_ELEMENT+1];
char tmpc5[LARGEST_ELEMENT+1];
int tmpc6;
int tmpc7;
int lineno = 0;
char buf[LARGEST_LINE]; // large enough for the largest line
while (fgets(buf, sizeof buf, fp1)) {
++lineno;
// no error, process line
if (sscanf(buf, "%d,"
"%" LARGEST_ELEMENT "[^,],"
"%" LARGEST_ELEMENT "[^,],"
"%" LARGEST_ELEMENT "[^,],"
"%" LARGEST_ELEMENT "[^,],"
"%d,%d",
&tmpd1, tmpc2, tmpc3, tmpc4, tmpc5, &tmpd6, &tmpd7) == 7) {
// line ok, copy tmp variables and update indexes
c1[i] = tmpd1;
strcpy(c2[i], tmpc2);
strcpy(c3[i], tmpc3);
strcpy(c4[i], tmpc4);
strcpy(c5[i], tmpc5);
c6[i] = tmpd6;
c7[i] = tmpd7;
i++;
} else {
// line with error, you may want to report to the user
fprintf(stderr, "line %d with error.\n", lineno);
}
}
// read "error", probably EOF; close file and report
fclose(fp1);
for (int j = 0; j < i; j++) {
printf("item #%d: %d, %s-%s-%s-%s, %d %d\n",
c1[j], c2[j], c3[j], c4[j], c5[j], c6[j], c7[j]);
}
return 0;
}
Also consider putting all those c arrays inside a struct and make 1 single array of that structure.

How to sort array of strings in ascending order in C

Problem
I have made sorting program which is similiar to other found at
https://beginnersbook.com/2015/02/c-program-to-sort-set-of-strings-in-alphabetical-order/
but program which i made is not working.
I think both are same but my program giving me waste output.
Also i want to know in other program count is set to 5 for example and it should take 6 input starting from 0 but it is getting only 5,How?
My Program
#include <string.h>
#include <stdio.h>
int main() {
char str[4][10],temp[10];
int i,j;
printf("Enter strings one by one : \n");
for(i=0;i<5;i++)
scanf("%s",str[i]);
for(i=0;i<5;i++)
for(j=i+1;j<5;j++)
if(strcmp(str[i],str[j])>0){
strcpy(temp,str[i]);
strcpy(str[i],str[j]);
strcpy(str[j],temp);
}
printf("\nSorted List : ");
for(i=0;i<5;i++)
printf("\n%s",str[i]);
printf("\n\n");
return 0;
}
Use qsort().
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int pstrcmp( const void* a, const void* b )
{
return strcmp( *(const char**)a, *(const char**)b );
}
int main()
{
const char* xs[] =
{
"Korra",
"Zhu Li",
"Asami",
"Mako",
"Bolin",
"Tenzin",
"Varrick",
};
const size_t N = sizeof(xs) / sizeof(xs[0]);
puts( "(unsorted)" );
for (int n = 0; n < N; n++)
puts( xs[ n ] );
// Do the thing!
qsort( xs, N, sizeof(xs[0]), pstrcmp );
puts( "\n(sorted)" );
for (int n = 0; n < N; n++)
puts( xs[ n ] );
}
Please don’t use bubble sort. In C, you really do not have to write your own sorting algorithm outside of specialized needs.
Here is a program which will sort and print your inputted strings. Answering a little late, but just in case others have a similar question.
// This program will sort strings into either ascending or descending order
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SIZE 1000
#define EQUAL 0
#define ASCENDING 0
#define DESCENDING 1
// Function prototypes
void swap_str(char str1[], char str2[]);
void sort_strings(char str[MAX_SIZE][MAX_SIZE], int len, int order_type);
void print_2d_array(char str[MAX_SIZE][MAX_SIZE], int len);
int main(void) {
int order_type;
char str[MAX_SIZE][MAX_SIZE];
// User selecting the order type
printf("-----------[Order Type]-----------\n");
printf("Sort your strings in ascending or descending order?\n");
printf("0 = Ascending | 1 = descending\n");
scanf("%d", &order_type);
if (order_type != ASCENDING && order_type != DESCENDING) {
printf("Please enter 0 or 1\n");
exit(1);
}
// User inputting their strings
printf("---------[Enter Strings]----------\n");
printf("Enter Strings one by one.\n");
printf("Max Strings: %d | Max String Len: %d\n", MAX_SIZE, MAX_SIZE);
int i = 0;
while ((i < MAX_SIZE) && (scanf("%s", str[i]) == 1)) i++;
if (i == MAX_SIZE) printf("You reached the maximum strings allowed\n");
// Program output of the sorted strings
printf("---------[Sorted Strings]---------\n");
sort_strings(str, i, ASCENDING);
print_2d_array(str, i);
return 0;
}
// Swaps two strings (assuming memory allocation is already correct)
void swap_str(char str1[], char str2[]) {
char temp[MAX_SIZE];
strcpy(temp, str1);
strcpy(str1, str2);
strcpy(str2, temp);
}
// Will sort a 2D array in either descending or ascending order,
// depending on the flag you give it
void sort_strings(char str[MAX_SIZE][MAX_SIZE], int len, int order_type) {
int i = 0;
while (i < len) {
int j = 0;
while (j < len) {
if ((order_type == ASCENDING) &&
(strcmp(str[i], str[j]) < EQUAL)) {
swap_str(str[i], str[j]);
} else if ((order_type == DESCENDING) &&
(strcmp(str[i], str[j]) > EQUAL)) {
swap_str(str[i], str[j]);
}
j++;
}
i++;
}
}
// Will print out all the strings 2d array
void print_2d_array(char str[MAX_SIZE][MAX_SIZE], int len) {
int i = 0;
while (i < len) {
printf("%s\n", str[i]);
i++;
}
}
Example (Ascending order):
-----------[Order Type]-----------
Sort your strings in ascending or descending order?
0 = Ascending | 1 = descending
0
---------[Enter Strings]----------
Enter Strings one by one.
Max Strings: 1000 | Max String Len: 1000
Mango
Watermelon
Apple
Banana
Orange
// I pressed CTRL+D here (Linux) or CTRL+Z then enter (on Windows). Essentially triggering EOF. If you typed the MAX_SIZE it would automatically stop.
---------[Sorted Strings]---------
Apple
Banana
Mango
Orange
Watermelon
it should take 6 input starting from 0 but it is getting only 5,How?
This loop
for(i=0;i<5;i++)
scanf("%s",str[i]);
execute for i being 0, 1, 2, 3, 4 so it loops 5 times.
If you want 6 loops do
for(i=0;i<=5;i++)
^
Notice
or
for(i=0;i<6;i++)
^
Notice
Also notice this line
char str[6][10],temp[10];
^
Notice
so that you reserve memory for 6 strings
This is not an answer, but some criticism of the code you refer to:
#include<stdio.h>
#include<string.h>
int main(){
int i,j,count;
char str[25][25],temp[25];
puts("How many strings u are going to enter?: ");
scanf("%d",&count); // (1)
puts("Enter Strings one by one: ");
for(i=0;i<=count;i++) // (2)
gets(str[i]);
for(i=0;i<=count;i++)
for(j=i+1;j<=count;j++){
if(strcmp(str[i],str[j])>0){
strcpy(temp,str[i]);
strcpy(str[i],str[j]);
strcpy(str[j],temp);
}
}
printf("Order of Sorted Strings:"); // (3)
for(i=0;i<=count;i++)
puts(str[i]);
return 0;
}
And the criticism:
(1) scanf("%d",&count); reads a number into count, and returns after that. It does not consume the line break(!)
(2) this loop does not print anything, just reads. However if you put
for(i=0;i<=count;i++){
printf("%d:",i);
gets(str[i]);
}
in its place, you will suddenly see that it asks for names 0...5, just skips the 0 automatically. That is where the line break is consumed, it reads an empty string. You can also make it appear, if instead of putting 5 into the initial question, you put 5 anmoloo7.
(3) in the printout the names appear below the title Order of Sorted Strings. But there is no linebreak in that printf. The thing is that the empty string is "smaller" than any other string, so it gets to the front of the list, and that is printed there first. If you do the 'trick' of appending a name after the initial number, the output will look different, there will be 6 names, and one of them appended directly to the title.
Plus there is the thing what you probably get from your compiler too: gets is a deadly function, forget its existence and use fgets with stdin as appears in other answers.
I have this sample that I made:
#include <stdio.h>
#include <string.h>
void main()
{
char str[100],ch;
int i,j,l;
printf("\n\nSort a string array in ascending order :\n");
printf("--------------------------------------------\n");
printf("Input the string : ");
fgets(str, sizeof str, stdin);
l=strlen(str);
/* sorting process */
for(i=1;i<l;i++)
for(j=0;j<l-i;j++)
if(str[j]>str[j+1])
{
ch=str[j];
str[j] = str[j+1];
str[j+1]=ch;
}
printf("After sorting the string appears like : \n");
printf("%s\n\n",str);
}
#include <stdio.h>
#include <string.h>
#define STRING_MAX_WIDTH 255
void sort_strings(char str_arr[][STRING_MAX_WIDTH],int len){
char temp[STRING_MAX_WIDTH];
for(int i=0;i<len-1;i++){
if(strcmp(&str_arr[i][0],&str_arr[i+1][0])>0){
strcpy(temp, str_arr[i+1]);
strcpy(str_arr[i+1],str_arr[i]);
strcpy(str_arr[i],temp);
sort_strings(str_arr, len);
}
}
}
int main(){
char str_arr[][STRING_MAX_WIDTH] = {"Test", "Fine", "Verb", "Ven", "Zoo Keeper", "Annie"};
int len = sizeof(str_arr)/STRING_MAX_WIDTH;
sort_strings(str_arr, len);
for(int i=0;i<len;i++){
printf("%s\r\n", str_arr[i]);
}
}

Resources