Structure Arranging in C - c

I am trying to make a program that will arrange my structures by people ids.
I used long double because IDs are 20 digits long.
For example if I introduce 3 persons:
1.Alex Alex / id = 219(...)
2.John John / id = 200(...)
3.Robert Robert / id = 199(...)
I want my program to rearrange so Robert comes first, John second and Alex third.
I am having a problem on "for" structure - I don't exactly know how to swap two structures since I have chars and ints combined.
typedef struct
{
char name;
char prename;
long double id;
int j;
} PERSON;
int main()
{
int n,i,j;
printf ("How many people = ");
scanf("%d", &n);
PERSON v[n];
for(i=1;i<=n;i++)
{
printf("For person number nr. %d\n", i);
printf("name = ");
scanf("%s", &v[i].name);
printf("Prename = ");
scanf("%s", &v[i].prename);
printf("id = ");
scanf("%d", &v[i].id);
}
for(int i=0; i<n; i++)
{
for(int j=0; j<n-1; j++)
{
if( v[i].id > v[j+1].id )
{
int temp = v[j].id;
char temp2[100];
char temp3[100];
strcpy(v[j].prename,temp3);
strcpy(v[j].name,temp2);
v[j].id = v[j+1].id;
v[j+1].id = temp;
}
}
}
return;
}

Ok, because there is so many things you've done here that are not obvious to a new C developer, I want to point them out to help you learn:
typedef struct
{
// These need to be arrays if they are to be strings
// Preferably using a constant for size or dynamic
// if you want them to be variable sized to match input.
char name;
char prename;
// Do not use floating point numbers to represent integer values.
// IRL, you'd use a library, but here, you may want to use an array of
// some sort of integer type instead.
long double id;
// This is a really poor name for a struct variable and probably shouldn't be here.
int j;
} PERSON;
int main()
{
int n,i,j;
printf ("How many people = ");
// Dropping raw output from scanf into a memory allocation is crazy dangerous.
// At least error check the results to be sure it is meaningful.
scanf("%d", &n);
// This is a variable length array and often is not supported directly.
// You probably want to use a malloc()/free() pair to handle this.
// (Update: VLA is now part of newer standards, but are not safe because they cannot fail gracefully on out of memory.)
PERSON v[n];
// Anytime in C I see an array start at 1 and use <= for condition, I get very nervous because
// it tends to lead to indexing errors.
for(i=1;i<=n;i++)
{
printf("For person number nr. %d\n", i);
printf("name = ");
// Oops - and this is why. You just skipped the first entry at 0
// and will overwrite memory on the last loop.
// Also, scanf into a string without a length can blow up memory...
scanf("%s", &v[i].name);
printf("Prename = ");
// Ditto
scanf("%s", &v[i].prename);
printf("id = ");
// Ditto - worse because you've crossed your types - %d doesn't go into a long double.
scanf("%d", &v[i].id);
}
// Should be its own function to make it easier to swap later to a better sort.
for(int i=0; i<n; i++)
{
// Bubble sort usually wants j=i here.
for(int j=0; j<n-1; j++)
{
if( v[i].id > v[j+1].id )
{
// Make a swap function here. Makes it clearer what you want to do.
int temp = v[j].id;
// What is 100? How do you know that is enough?
// These are called magic numbers and lead to code death.
char temp2[100];
char temp3[100];
// Ah, strcpy - 3 things wrong here.
// 1 - You have the parameters backwards - you are copying temp3 to your struct.
// 2 - You have no way to know if the destination will fit the source because it copies until it finds a '\0' - very dangerous.
// 3 - Because your parameters are backwards and temp123 is not initialized, this very well could copy forever.
// strncpy (also crazy dangerous) at the least should be used and consider using better means like strlcpy() and such.
strcpy(v[j].prename,temp3);
strcpy(v[j].name,temp2);
v[j].id = v[j+1].id;
v[j+1].id = temp;
// You kinda forgot to swap the strings, but the program is already dead so no worries.
}
}
}
// Please enable compiler warnings - you must return a value here.
return;
}
In seriousness, I'm sure I missed a few other things, but this is good enough for a free internet code review and learning session. :)
Info on strcpy() and strncpy()
Why is strncpy insecure?
Info on scanf() safety
How to prevent scanf causing a buffer overflow in C?
Info on Variable Length Array safety:
Is it safe to use variable-length arrays?

The main problem with your code is here:
typedef struct
char name; <--- just a char! no room for a string
char prename; <--- just a char! no room for a string
long double id;
int j;
} PERSON;
You need to make these arrays so that they can hold the names.
Like:
#define MAX_NAME_LEN 100
typedef struct {
char name[MAX_NAME_LEN];
char prename[MAX_NAME_LEN];
long double id;
int j;
} PERSON;
Besides that, you should always check the value returned by scanf and never do scanf("%s", &v[i].prename); as it may lead to buffer overflow. Instead do scanf("%99s", v[i].prename); but even better use fgets
And for sorting... just use qsort - see http://man7.org/linux/man-pages/man3/qsort.3.html
BTW: A long double can't be scan'ed using scanf("%d", &v[i].id); - %d is for integers. That said, I doubt you want to use long double. You probably want some interger type - maybe long long unsigned or uint64_t

Related

How to find the total number of a certain element in an array(C)

I'm trying to create a complete C program to read ten alphabets and display them on the screen. I shall also have to find the number of a certain element and print it on the screen.
#include <stdio.h>
#include <conio.h>
void listAlpha( char ch)
{
printf(" %c", ch);
}
int readAlpha(){
char arr[10];
int count = 1, iterator = 0;
for(int iterator=0; iterator<10; iterator++){
printf("\nAlphabet %d:", count);
scanf(" %c", &arr[iterator]);
count++;
}
printf("-----------------------------------------");
printf("List of alphabets: ");
for (int x=0; x<10; x++)
{
/* I’m passing each element one by one using subscript*/
listAlpha(arr[x]);
}
printf("%c",arr);
return 0;
}
int findTotal(){
}
int main(){
readAlpha();
}
The code should be added in the findTotal() element. The output is expected as below.
Output:
List of alphabets : C C C A B C B A C C //I've worked out this part.
Total alphabet A: 2
Total alphabet B: 2
Total alphabet C: 6
Alphabet with highest hit is C
I use an array to count the number of the existence of each character,
I did this code but the display of number of each character is repeated in the loop
int main()
{
char arr[100];
printf("Give a text :");
gets(arr);
int k=strlen(arr);
for(int iterator=0; iterator<k; iterator++)
{
printf("[%c]",arr[iterator]);
}
int T[k];
for(int i=0;i<k;i++)
{
T[i]=arr[i];
}
int cpt1=0;
char d;
for(int i=0;i<k;i++)
{int cpt=0;
for(int j=0;j<k;j++)
{
if(T[i]==T[j])
{
cpt++;
}
}
if(cpt>cpt1)
{
cpt1=cpt;
d=T[i];
}
printf("\nTotal alphabet %c : %d \n",T[i],cpt);
}
printf("\nAlphabet with highest hit is : %c\n",d,cpt1);
}
There is no way to get the number of elements You write in an array.
Array in C is just a space in the memory.
C does not know what elements are actual data.
But there are common ways to solve this problem in C:
as mentioned above, create an array with one extra element and, fill the element after the last actual element with zero ('\0'). Zero means the end of the actual data. It is right if you do not wish to use '\0' among characters to be processed. It is similar to null-terminated strings in C.
add the variable to store the number of elements in an array. It is similar to Pascal-strings.
#include <stdio.h>
#include <string.h>
#define ARRAY_SIZE 10
char array[ARRAY_SIZE + 1];
int array_len(char * inp_arr) {
int ret_val = 0;
while (inp_arr[ret_val] != '\0')
++ret_val;
return ret_val;
}
float array_with_level[ARRAY_SIZE];
int array_with_level_level;
int main() {
array[0] = '\0';
memcpy(array, "hello!\0", 7); // 7'th element is 0
printf("array with 0 at the end\n");
printf("%s, length is %d\n", array, array_len(array));
array_with_level_level = 0;
const int fill_level = 5;
int iter;
for (iter = 0; iter < fill_level; ++iter) {
array_with_level[iter] = iter*iter/2.0;
}
array_with_level_level = iter;
printf("array with length in the dedicated variable\n");
for (int i1 = 0; i1 < array_with_level_level; ++i1)
printf("%02d:%02.2f ", i1, array_with_level[i1]);
printf(", length is %d", array_with_level_level);
return 0;
}
<conio.h> is a non-standard header. I assume you're using Turbo C/C++ because it's part of your course. Turbo C/C++ is a terrible implementation (in 2020) and the only known reason to use it is because your lecturer made you!
However everything you actually use here is standard. I believe you can remove it.
printf("%c",arr); doesn't make sense. arr will be passed as a pointer (to the first character in the array) but %c expects a character value. I'm not sure what you want that line to do but it doesn't look useful - you've listed the array in the for-loop.
I suggest you remove it. If you do don't worry about a \0. You only need that if you want to treat arr as a string but in the code you're handling it quite validly as an array of 10 characters without calling any functions that expect a string. That's when it needs to contain a 0 terminator.
Also add return 0; to the end of main(). It means 'execution successful' and is required to be conformant.
With those 3 changes an input of ABCDEFGHIJ produces:
Alphabet 1:
Alphabet 2:
Alphabet 3:
Alphabet 4:
Alphabet 5:
Alphabet 6:
Alphabet 7:
Alphabet 8:
Alphabet 9:
Alphabet 10:-----------------------------------------List of alphabets: A B C D E F G H I J
It's not pretty but that's what you asked for and it at least shows you've successfully read in the letters. You may want to tidy it up...
Remove printf("\nAlphabet %d:", count); and insert printf("\nAlphabet %d: %c", count,arr[iterator]); after scanf(" %c", &arr[iterator]);.
Put a newline before and after the line of minus signs (printf("\n-----------------------------------------\n"); and it looks better to me.
But that's just cosmetics. It's up to you.
There's a number of ways to find the most frequent character. But at this level I recommend a simple nested loop.
Here's a function that finds the most common character (rather than the count of the most common character) and if there's a tie (two characters with the same count) it returns the one that appears first.
char findCommonest(const char* arr){
char commonest='#'; //Arbitrary Bad value!
int high_count=0;
for(int ch=0;ch<10;++ch){
const char counting=arr[ch];
int count=0;
for(int c=0;c<10;++c){
if(arr[c]==counting){
++count;
}
}
if(count>high_count){
high_count=count;
commonest=counting;
}
}
return commonest;
}
It's not very efficient and you might like to put some printfs in to see why!
But I think it's at your level of expertise to understand. Eventually.
Here's a version that unit-tests that function. Never write code without a unit test battery of some kind. It might look like chore but it'll help debug your code.
https://ideone.com/DVy7Cn
Footnote: I've made minimal changes to your code. There's comments with some good advice that you shouldn't hardcode the array size as 10 and certainly not litter the code with that value (e.g. #define ALPHABET_LIST_SIZE (10) at the top).
I have used const but that may be something you haven't yet met. If you don't understand it and don't want to learn it, remove it.
The terms of your course will forbid plagiarism. You may not cut and paste my code into yours. You are obliged to understand the ideas and implement it yourself. My code is very inefficient. You might want to do something about that!
The only run-time problem I see in your code is this statement:
printf("%c",arr);
Is wrong. At this point in your program, arr is an array of char, not a single char as expected by the format specifier %c. For this to work, the printf() needs to be expanded to:
printf("%c%c%c%c%c%c%c%c%c%c\n",
arr[0],arr[1],arr[2],arr[3],arr[4],
arr[5],arr[6],arr[7],arr[8],arr[9]);
Or: treat arr as a string rather than just a char array. Declare arr as `char arr[11] = {0};//extra space for null termination
printf("%s\n", arr);//to print the string
Regarding this part of your stated objective:
"I shall also have to find the number of a certain element and print it on the screen. I'm new to this. Please help me out."
The steps below are offered to modify the following work
int findTotal(){
}
Change prototype to:
int FindTotal(char *arr);
count each occurrence of unique element in array (How to reference)
Adapt above reference to use printf and formatting to match your stated output. (How to reference)

Is there any tip to conclude different types in the same table?

How can I add different types in a table? Firstly I have to create a function in order to add the food that I ate (char), the calories (int) and the hour that I ate it (float) in a table with maximum size [100][4].
The only knowledge that I have and I can use for this project for my university is pointers and tables, NOT structures (which is the solution I was also thinking)
I've tried many things and the only thing that I did is to fill only the first column with the name of the food.
for (j=0;j<4;j++){
if (j==0){
printf ("Add your food:\n");
//char
scanf("%s",&table[n][j]);
}else if (j==1){
printf ("Add calories:\n");
//int
scanf("%d",&table[n][j]);
}else if (j==2){
printf ("Add the time you ate:\n");
//float
scanf("%.2f",&table[n][j]);
}else if (j==3){
printf ("Kati\n");
}
}
I expected my code to show all the data I filled but of course that doesn't work. So is there any solution to add different types in a table?
add different types in a table? ... pointers and tables, NOT structures ..
... as char *table[100][4] ...
Save all data as strings. Convert the type/value into a string with enough information to reconstruct the type/value later.
#include <float.h>
#include <stdlib.h>
void table_add(char *table[100][4], size_t index, const char *food, int calories, float hour) {
table[index][0] = strdup(food);
char buf[42]; // large enough for a 128 bit `int`
sprintf(buf, "%d", index);
table[index][1] = strdup(buf);
sprintf(buf, "%.*e", FLT_DECIMAL_DIG - 1, index);
table[index][2] = strdup(buf);
table[index][3] = NULL; // Unclear what OP needs a 4th element for
}
Usage
#define FOOD_SIZE 50
char *table[100][4] = { 0 };
for (index = 0; index < 100; index++) {
char food[FOOD_SIZE];
printf ("Add your food:\n");
scanf("%49s",food);
int calories
printf ("Add calories:\n");
scanf("%d",&calories);
float hour;
printf ("Add the time you ate:\n"); // Unclear why OP is using float for `time`
scanf("%f", &hour);
printf ("Kati\n");
table_add(table, index, food, calories, hour);
}
// now use the data somehow
index = ...
food = table[index][0];
calories = atoi(table[index][1]);
hour = atof(table[index][2]);
printf("Food:%s Calories:%d Time:%.2f\n", food, calories, hour);
// When done, free all allocations
for (index = 0; index < 100; index++) {
for (j = 0; j < 4; j++) {
free(table[index][j]);
}
}
For details on FLT_DECIMAL_DIG - 1 in sprintf(buf, "%.*e", FLT_DECIMAL_DIG - 1, index); see Printf width specifier to maintain precision of floating-point value.
Disclaimer This is not a general purpose solution, in nearly all cases there are better ways of doing such an exercise. However this is what your assignment restrictions call for
In C a pointer is allowed to alias any other type of pointer (though there are special restrictions when it comes to dereferencing such pointers), so you have to type pun your other types into your array (which is generally risky as you give up type safety). Modifying your code sample this would look like this (I have removed the loop and branching as I found them to hinder readability):
printf ("Add your food:\n");
// 50 is just to showcase, replace with actual value in your code
table[n][0] = malloc(50 * sizeof(char));
scanf("%s",table[n][0]);
printf ("Add calories:\n");
table[n][1] = malloc(sizeof(int));
scanf("%d",(int*)table[n][1]);
printf ("Add the time you ate:\n");
table[n][2] = malloc(sizeof(float));
scanf("%f",(float*)table[n][2]);
printf ("Kati\n");
Also make note of the changes I made to the scanf lines to make sure that the pointers passed in are of the actual correct types. Since you malloc all elements of the array you also need to remember to free them all at the end of your program to avoid memory leaks.
EDIT As commented by OP, table is defined as char* table[100][4]

The scanf is not reading the values

Hey this is the entire code.
I m a beginner in C, I was trying to make a code which has a structure mall and takes into input the name, number of items shopped , the name of each item and the cost of each item.
* For small programs like this i fix the max size of the structure object
But the program cant take the input in the manner desired.
#include<stdio.h>
struct mall
{
char name[50];
char obj[10][30];
float price[10];
int numb;
}b[50];
void main()
{
int m; // number of persons who shopped at the mall
scanf("%d",&m);
for(int i=0;i<m;i++)
{
num=0;
scanf("%s",&b[i].name);
scanf("%d",&b[i].numb);
printf("%s\n%d",b[i].name,b[i].numb);
for(int j=0;j<num;j++)
{
scanf("%s",&b[i].obj);
scanf("%f",&b[i].price);
}
}
}
For the input :
1
Ram 2 bread 50.00 jam 25.00
I m getting the output as
2500
Your code has many small mistakes:
scanf("%s", &b[i].name); does not need &
num=0; is not necessary; remove it
Nested loop condition should use j < b[i].numb as its condition
Nested loop is not using j. It needs to add [j] to both obj and price.
Once you fix these problems, your code runs as expected as long as the input is correct (demo).
However, this is not enough to make your code robust: you need to add error checking to ensure that invalid input does not cause undefined behavior:
Add limits to string format specifiers in scanf to avoid buffer overflows (e.g. %49s to read name[50]),
Add a limit to the outer loop in case m is above 50,
Add a limit to the nested loop in case b[i].numb is above 10,
Add checks of return values for scanf.
num is undeclared. The program does not compile to executable. See below.
Don't pass &b[i].name to scanf(3), as the %s format requires a char * and the &b[i].name is not that type (it is, indeed, char *[50], while compatible, it is not the same type, while b[i].name is)
change num by b[i].numb in the inner for loop. This is the proper number of items. You have to check for array sizes also, as you have hardwired them in the code. If you don't you can overrun
Use inside the inner loop b[i].obj[j] for the object name as the reference to scanf(3)
Use inside the inner loop &b[i].price[j] for the j-esim price. You have forgotten it here and in the point above.
you have to add code to print the individual objects (the inner loop has no printf(3) at all.
A valid (but not completely correct, as array sizes are not checked) could be:
#include<stdio.h>
struct mall {
char name[50];
char obj[10][30];
float price[10];
int numb;
}b[50];
int main()
{
int m; // number of persons who shopped at the mall
scanf("%d",&m);
for(int i=0; i<m; i++) {
scanf("%s",b[i].name);
scanf("%d",&b[i].numb);
printf("* name = %s\n"
" numb = %d\n",b[i].name,b[i].numb);
for(int j=0; j < b[i].numb; j++) {
scanf("%s",b[i].obj[j]);
scanf("%f",&b[i].price[j]);
printf(" * obj = %s\n", b[i].obj[j]);
printf(" price = %.2f\n", b[i].price[j]);
}
}
return 0;
}

( C ) Cannot find reason for Core Dump Error with Dynamic Allocating Structs

Disclaimer: This is my first question on StackOverflow and I'm a novice programmer, so my apologies ahead of time if you are appalled by my code or if I don't post my question appropriately.
Anyways, I'm working on a gradebook with dynamically allocated structures. I've divided the gradebook into three structues, a student structure (student name, student id), a course structure (course name, course id), and an enroll structure(student id, course id, grade).
Problem: I'm able to input as many grade as I need without errors for the first student. When I try to input a grade for the second student my program core dumps. I've checked all the variables involved to see if they are passed to my function appropriately and they are. The following is my Enroll structure and my add grade function.
typedef struct {
int Student_ID;
int Course_ID;
int *Grade;
int GradeCount;
} Enroll_Database;
Function...
void addGrade(Enroll_Database *Enroll)
{
int i = 0, j = 0, b, Course_Num, Student_Num, Grade;
printf("Enter Course Number: ");
scanf("%d", &Course_Num);
printf("Enter Student ID: ");
scanf("%d", &Student_Num);
/* For loop that traverses through the Enroll array until until it encounters
nothing in the Course ID */
for(i = 0; Enroll[i].Course_ID != 0; i++)
{
/* if the Student Number and the Course Number are equal to their
appropriate Enroll element, then ask user to input Grade */
if(Enroll[i].Student_ID == Student_Num && Enroll[i].Course_ID == Course_Num)
{
printf("Enter Grade: ");
scanf("%d", &Grade);
if(Enroll[i].GradeCount == 0)
{
Enroll->Grade = (int *) malloc(sizeof(int));
Enroll[i].Grade[Enroll[i].GradeCount] = Grade; //core dumps
Enroll[i].GradeCount++;
}
else
{
Enroll->Grade = (int *) realloc(Enroll->Grade, sizeof(int));
Enroll[i].Grade[Enroll[i].GradeCount] = Grade; //core dumps
Enroll[i].GradeCount++;
}
}
}
}
I've ran multiple checks and the core dump occurs after I malloc/realloc and assign user input to the grade value in the enroll structure.
I'd greatly appreciate any help, and I'm sorry again if my code is unreadable or I formatted wrong.
Thanks!
This only allocates space for one element,. and also it reallocates the wrong pointer:
Enroll->Grade = (int *) realloc(Enroll->Grade, sizeof(int));
It could be fixed like this:
Enroll[i].Grade = realloc(Enroll[i].Grade, sizeof(int) * (Enroll[i].GradeCount + 1));
Remembering that X->Y, (*X).Y, and X[0].Y all mean the same thing: your original version actually reallocated Enroll[0].Grade, instead of Enroll[i].Grade.
(The rest of this answer is some possible style improvement suggestions:)
To avoid this sort of error, I'd personally write just after the scanf:
Enroll_Database *found = &Enroll[i];
and then use found-> everywhere instead of Enroll[i]. Alternatively I'd consider having a separate function to actually add the grade (which is called once the database entry has been found).
Now, if you initialize Enroll[i].Grade to NULL when setting up the database, you actually wouldn't need this if...else statement. Since the behaviour of realloc(NULL, X) is the same as malloc(X), the same code would handle both situations.
Note that in C you should not cast the value returned by malloc and friends.
Another thing to bear in mind is that the style X = realloc(X, ... does not allow you to recover from allocation failure. To write robust code you need to take some sensible action when a malloc-family function returns NULL; this could be as simple as printing a message and calling exit.

How can i add numbers to an array using scan f

I want to add numbers to an array using scanf
What did i do wrong? it says expected an expression on the first bracket { in front of i inside the scanf...
void addScores(int a[],int *counter){
int i=0;
printf("please enter your score..");
scanf_s("%i", a[*c] = {i});
}//end add scores
I suggest:
void addScores(int *a, int count){
int i;
for(i = 0; i < count; i++) {
printf("please enter your score..");
scanf("%d", a+i);
}
}
Usage:
int main() {
int scores[6];
addScores(scores, 6);
}
a+i is not friendly to newcomer.
I suggest
scanf("%d", &a[i]);
Your code suggests that you expect that your array will be dynamically resized; but that's not what happens in C. You have to create an array of the right size upfront. Assuming that you allocated enough memory in your array for all the scores you might want to collect, the following would work:
#include <stdio.h>
int addScores(int *a, int *count) {
return scanf("%d", &a[(*count)++]);
}
int main(void) {
int scores[100];
int sCount = 0;
int sumScore = 0;
printf("enter scores followed by <return>. To finish, type Q\n");
while(addScores(scores, &sCount)>0 && sCount < 100);
printf("total number of scores entered: %d\n", --sCount);
while(sCount >= 0) sumScore += scores[sCount--];
printf("The total score is %d\n", sumScore);
}
A few things to note:
The function addScores doesn't keep track of the total count: that variable is kept in the main program
A simple mechanism for end-of-input: if a letter is entered, scanf will not find a number and return a value of 0
Simple prompts to tell the user what to do are always an essential part of any program - even a simple five-liner.
There are more compact ways of writing certain expressions in the above - but in my experience, clarity ALWAYS trumps cleverness - and the compiler will typically optimize out any apparent redundancy. Thus - don't be afraid of extra parentheses to make sure you will get what you intended.
If you do need to dynamically increase the size of your array, look at realloc. It can be used in conjunction with malloc to create arrays of variable size. But it won't work if your initial array is declared as in the above code snippet.
Testing for a return value (of addScores, and thus effectively of scanf) >0 rather than !=0 catches the case where someone types ctrl-D ("EOF") to terminate input. Thanks #chux for the suggestion!

Resources