Remove an element from an array of structures in C? - c

I'm new to C, and honestly have no idea where to start with removing a particular element from an array of structures.
If you wish, you can view and copy my code in its entirety here: http://pastebin.com/Zbrm2xyL
Mostly I'm concerned with the function 'rmv_student', which is supposed to remove the struct with a matching id number from the array 'st_array' without messing with the other elements of that array after prompting the user for confirmation. Function 'rmv_student' is as follows:
void rmv_student(long id) // BROKEN
{
int i; // iterator
char response; // used to confirm deletion
for( i = 0; i < MAX; i++){
if ( st_array[i].id == id){
printf("Are you sure you want to delete %s %s, %d?\n", st_array[i].first_name, st_array[i].last_name, st_array[i].id);
puts("You will not be able to undo the deletion.");
puts("Enter 'y' to delete or 'n' to return to the main menu.");
response = getchar();
switch (response){
case 'y':
// delete
case 'Y':
// delete
case 'n':
main();
case 'N':
main();
default:
puts("Please enter 'y' or 'n'.");
rmv_student(id);
}
}
}
if ( i == MAX ){
printf("\nThere are no students with ID %d.\n\n", id);
main();
}
}
I have two questions.
Are my switch cases correct? Will this test the user's input character correctly?
How do I go about deleting the struct?
Before you ask. Yes, this is homework. As such, I'm not looking for a handout, just a point in the right direction. Any other suggestions are welcome.
Note: I am aware that I don't really need the function 'menu_test_input', but I'm leaving it for now.

Use loops and return statements instead of recursive calling! Remember that when the called function returns the code will continue after the call.
Instead do something like the following pseudo-code
do
{
print_prompt()
get_response()
} while (response is not legal)
if (response is yes)
do_the_actual_deletion
If you want to remove element X of array A, then move the element X + 1 to X, move element X + 2 to X + 1, etc. When done then decrease the size by one. No actual "removing" involved.

There are two possible solutions to your problem, which one you should use depends on whether the order of the array elements is important to you.
The fast solution: Copy the last element in the array to the position of the element you want to delete, then simply decrement your count of elements in the array.
int* array = ...;
int elementCount = ...;
...
int deletionIndex = ...;
array[deletionIndex] = array[--elementCount]; //the deletion is actually a one liner :-)
This solution is the preferred one whenever you are operating with an unsorted array, it takes only a constant amount of time, regardless of where you do the deletion.
The long solution: Move all elements behind the deleted element one position to the front.
//setup is the same as for the fast solution
elementCount--;
for(int i = deletionIndex; i < elementCount; i++) array[i] = array[i+1];
Not exactly difficult, but considerably more complex than the fast solution.
You need to use this whenever you need to preserve the relative order of the array elements. The price for the ordering is that the runtime depends on the amount of elements that need to be moved.

you have to use break;
case 'y':
//your code
break;
case 'Y':
//your code
break;
case 'n':
break;
...
......
or the code will run all your cases.
proper use - http://www.tutorialspoint.com/cprogramming/switch_statement_in_c.htm

Related

Why is binary search function not working when values on pointers are compared instead of the pointers themselves?

I was making this binary search function in c, but have encountered a problem. I have passed a pointer of a sorted array, its (size-1) and the number to be searched. When I try to compare values at the front position and last position in the while loop, its not working for the values on the right of middle element. For eg, if I pass a array={1,2,3,4,5} for {1,2,3}, the function works fine, however for {4,5} the loop just runs once and exits.
There's one more issue, this problem only happens if we compare the values on the address of the pointers, but if I compare the pointers instead, the function works perfectly. Better explained in the code given below.
int binarysearch(int*p,int r,int num){
//p is pointer of an array, r is the (sizeof(array)-1), num is the number to be searched
int *mid;
while(*p<=*(p+r)){//if we replace the condition with(p<=(p+r)) the function works
mid=(p+(r/2));
printf("1 ");
if(*mid==num)
return *mid;
if(*mid<num)
p=mid+1;
else
r=((r/2)-1);
}
return -1;
}
Check out the way you calculate the mid value.
In the first iteration, mid=(p+(r/2)) will give you the middle term correctly. Let say r=5, and that in the first iteration we got *mid<num so now p=mid+1 or p=p+3.
The problem now is that r is still 5 so the array pointer just got shifted away from its values without marking the new end. This can easily lead to segmentation fault if you will try to use your result address later.
the solution is simple: don't calculate the mid position with a pointer and int. Use two pointers to mark the search bounders or two integers to mark their indexes.
You can also use your way, but be sure to update r size every iteration.
First option - using indexes (my choice if I had to make one):
int binarysearch(int*p,int r,int num){
int mid;
int a=0;
while(a<=r) {
mid=(a+r)/2;
printf("1 ");
if(p[mid]==num)
return *mid;
else if(p[mid]<num)
a=mid+1;
else if(a==r)
break;
else
r=mid;
}
return -1;
}
Second option - your fixed version:
int binarysearch(int*p,int r,int num){
int *mid;
while(r>=0) {
mid=p+(r/2);
printf("1 ");
if(*mid==num)
return *mid;
else if(*mid<num) {
p=mid+1;
r-=(r/2)+1;
}
else if (r==0)
break;
else
r/=2;
}
return -1;
}

How is the check function working in this program of Priority Queue

This is the program for priority queue data structures. Can someone explain me the check function in this program? I understand that it is used to check the priority of the inserted elements but i am a little confused how is it doing that and what was the need of nested loops in check function.
Also please explain the for loop initialization and condition part for j why did we do rear+1 and why is j>i.
#include <stdio.h>
#include <stdlib.h>
#define max 3
int q[max],front=0,rear=-1;
void insert_by_p()
{
if(rear==max-1)
{
printf("overflow\n");return;
}
printf("please enter the element\n");
int a;
scanf("%d",&a);
check(a);
rear++;
}
void check(int a)
{
int i,j;
for(i=front;i<=rear;i++)
{
if(a<=q[i])
{
for(j=rear+1;j>i;j--)
q[j]=q[j-1];
q[i]=a;
return;
}
}
q[i]=a;
}
void display()
{
if(rear==-1||front>rear)
{
printf("underflow\n");return;
}
printf("Q items:");
for(int i=front;i<=rear;i++)
{
printf("%d,",q[i]);
}
printf("\n");
}
void delete_by_p()
{
if(rear==-1||front>rear)
{
printf("underflow\n");return;
}
printf("the deleted element is %d\n",q[front++]);
}
int main()
{
int a;
while(1)
{
printf("please choose one option:\n1.insert\n2.delete\n3.display\n4.exit\n");
scanf("%d",&a);
switch(a)
{
case 1: insert_by_p();
break;
case 2: delete_by_p();
break;
case 3: display();
break;
case 4: exit(0);
break;
default:printf("Wrong choice\n");
break;
}
}
return 0;
}
EDIT: So i got comments regarding if the code was right or who provided the code. Dont worry about that the code is working perfectly fine and it was given to me by my professor. Unlike linear queue, priority queue will arrange the elements according to their priority(here max element highest priority) the dequeue operation would happen as per the priority
Considering:
int q[max],front=0,rear=-1;
Then in insert_by_p():
check(a);
rear++;
When check() is called a has not yet been inserted and rear refers to the "old" end (or -1 on the first insertion). So in check, rear is one less than the insertion location. Hence rear + 1 in the for loop, and j > i because the loop is iterating in reverse toward i.
With respect to how check() actually works, well to start with it does not "check the priority of the inserted elements" - it actually performs the insertion - it is misleadingly named. The outer loop iterates through the non-deleted elements of q[]. The inner loop moves elements of q[] to make space for the insertion of a.
To be honest the code is not great;
An array is an inappropriate data structure for this, requiring data to be moved non-deterministically for each insertion. A doubly-linked list would be more appropriate.
front and rear are only ever incremented. Everytime delete_by_p() is called, the queue capacity is effectively reduced. It is a "leaky" algorithm.
check() is used in insert_by_p() before it is declared.
since a is inserted by check(), then the rear++ should be done there too (and check() is a bad name for a function that actually modifies the data).
check() has two exit points with one deeply nested - that is nasty, and can lead to errors. For example if you were to move the rear++ to check() as I suggest; you have to add it in two places.
I am sure there are other issues - those are just the ones immediately obvious.

How to printf a vector in C

I have three diferent vectors with the name of diferents towns inside:
V_NombrePueblos listTown1={"Abrera","Granollers","Cardedeu","Manresa","Martorell"};
V_NombrePueblos listTown2={"Astorga","Benavente","Bembibre","Camarzana","Ferrol"};
V_NombrePueblos listTown3={"Arteijo","Betanzos","Cariño","Cedeira","Cerdido"};
The user tell me the number of vector and the position for print the town. I think in use a function with a switch inside for do this:
typedef char nameList[8];
void returnTown(int listTown, int position){
nameList numList;
if (listTown==0){
strcpy(numList, "listTown1");
}
if (listTown==1){
strcpy(numList, "listTown2");
}
if (listTown==2){
strcpy(numList, "listTown3");
}
switch (position){
case 1:
printf("%s", numList[0]);
break;
case 2:
printf("%s", numList[1]);
break;
case 3:
printf("%s", numList[2]);
break;
case 4:
printf("%s", numList[3]);
break;
case 5:
printf("%s", numList[4]);
break;
But when I try to print example:
returnTown(0,1)
The console doesn't show nothing, with the previus code the console should show "Abrera"
The problem is in the printf insede the switch,
If I put:
printf("%s",listTown1[0] )
The code show "Abrera" fine, but I need pass the name of the vector like a varName, because sometimes will be listTown1, other times listTown2 or listTown3...
Any idea?
Thanks
Copying names of variables doesn't mean refering variables.
To refer variables, you should use pointers.
You will want something like this:
void returnTown(int listTown, int position){
V_NombrePueblos* numList;
switch (listTown){
case 0: numList = &listTown1; break;
case 1: numList = &listTown2; break;
case 2: numList = &listTown3; break;
default: return;
}
if (1 <= position && position <= 5){
printf("%s", (*numList)[position - 1]);
(rest part of this function isn't shown because I respect the original code snippet)
What you are trying to do won't work in C - you can't build variable names dynamically like that.
Any time you find yourself defining a bunch of variables with the same type and with ordinal names (var1, var2, etc.), that's a real strong hint you want to use an array. In this case, you could do something like
/**
* I am *assuming* that vNombrePueblos is a typedef name for char *[5],
* based on the declarations in your code.
*
* The size of the listTowns array is taken from the number of initializers;
* in this case, 3.
*/
vNombrePueblos listTowns[] = {
{"Abrera","Granollers","Cardedeu","Manresa","Martorell"},
{"Astorga","Benavente","Bembibre","Camarzana","Ferrol"},
{"Arteijo","Betanzos","Cariño","Cedeira","Cerdido"}
};
This way instead of trying to figure out which listTownN variable you want, you just index into this array. To print out the correct town, all you need is the two indices:
/**
* You need to pass the list of towns as an argument to your function;
* since arrays lose their "array-ness" under most circumstances, you also
* have to pass the array size to make sure you don't try to access something
* past the end of it.
*/
void returnTown( vNombrePueblos listTowns[], int numTowns, int town, int position )
{
if ( town < numTowns )
printf( "%s\n", listTowns[town][position] );
else
fprintf( stderr, "No such entry\n" );
}
You'll need to keep track of the number of entries in listTowns yourself - arrays in C don't carry any metadata about their size, and under most circumstances (such as when you pass it as an argument to a function) an expression of type "array of T" will "decay" into an expression of type "pointer to T", so the sizeof arr / sizeof arr[0] trick won't work to get the number of elements.

look for only the first common element between two arrays

I have two arrays, namely a[] and b[]. I would like to search if any element of a is present in b. Note that I do not want to find all duplicate occurrences, if any one element from a[] exists in b[], I would like to report it and break out without checking the rest of a[] or b[]
I'm a bit confused about how 'break' operates. Can 'break' break out of any kind of loop - for/while, but it breaks out of only the 'innermost loop' around where it's placed in code ?
This is my solution and please suggest if there is a better implementation for this O(n^2) approach
#include <stdio.h>
int main(void)
{
int a[] = {1,2,3,4,5,6};
int b[] = {11,2,3,7,8,9,6,10,11};
int i = 0, j = 0;
int duplicate = 0;
for(i=0;i<sizeof(a)/sizeof(int);i++)
{
for(j=0;j<sizeof(b)/sizeof(int);j++)
{
if(a[i] == b[j])
{
printf("Duplicates found for [%d]\n",a[i]);
duplicate = 1;
break;
}
}
if(duplicate == 1)
{
break;
}
}
return 0;
}
break only breaks the innermost loop in which it is used. If you want to avoid the ugly syntax then you have multiple solutions:
ignore this optimization until you prove it's relevant
move the code inside a function that accepts the two arrays as arguments so that you can directly return from the function without having to break.
adjust indices i and j after that you found a duplicate to make both loops return gracefully
use a goto instruction (which is not advisable in any case)
Other solutions, with complexity lower than O(n^2) could require some additional data structure, like a hashset or sorting of data.
breaks generally break out of the most inner loop in c. You have used it correctly.
if u want to just report a duplicate, then u can put this code into a fuction and whenever a duplicate is found, you just return. The function would look like this:
//Here a and b are pointers to array
//n1 and n2 are number of elements in array a,b
chk_dup(int *a,int *b,int n1,,int n2){
for(i=0;i<n1;i++)
{
for(j=0;j<n2;j++)
{
if(a[i] == b[j])
{
printf("Duplicates found for [%d]\n",a[i]);
//duplicate = 1;
//break;
return;
}
}
}
}
You cannot use sizeof(a) here becoz its just a pointer to array.Same goes for sizeof(b).
You can make this algorithm more efficient by sorting the arrays using quicksort(that would take O(nlgn)) and then for each element in a do binary search in b.
pseudo code for that is something like this:
//Here a and b are pointers to sorted arrays
//n1 and n2 are number of elements in array a,b
chk_dup(int *a,int *b,int n1,,int n2){
for(i=0;i<n1;i++)
{
//find a[i] in b using binary search.
int found=binary_search(a[i],b);
if(found){
printf("Found Duplicate");
return;
}
}
printf("No duplicate found");
}
So, the whole algorithm works in O(nlgn). While the algorithm you are using can take O(n^2).
Otherwise your code is perfectly fine and the use of break is correct
You could use goto as noted here How to break out of nested loops? it's the last remaining valid use of this...
And maybe there is a better solution using xor, not sure if you can reduce the
complexity though...

How to create array of random strings in C?

I tried creating an array of ten random strings that would print directions randomly. Such as first time "up down right ... rot_x" and second time "forward rot_y up ... down" etc. I tried using a char* pc and allocating memory for it with memset but that didn't work so I tried the following code but I'm getting weird output. How can I fix this?
int main()
{
int r_num;
char r_arr[10][10];
for (int i = 0; i < 10; i++)
{
r_num = rand() % 10;
switch(r_num)
{
case 0: strcpy(r_arr[0], "up");
break;
case 1: strcpy(r_arr[1], "down");
break;
case 2: strcpy(r_arr[2], "left");
break;
case 3: strcpy(r_arr[3], "right");
break;
case 4: strcpy(r_arr[4], "rot_x");
break;
case 5: strcpy(r_arr[5], "rot_y");
break;
case 6: strcpy(r_arr[6], "rot_z");
break;
case 7: strcpy(r_arr[7], "forward");
break;
case 8: strcpy(r_arr[8], "back");
break;
case 9: strcpy(r_arr[9], "reset");
break;
default:
fprintf(stderr, "Cannot process input/n");
}
}
for (int i = 0; i < 10; i++)
{
printf("%s ", r_arr[i]);
}
return 0;
}
here's my output:
up ?V? left right rot_x ?V? forward back reset
A few problems with your code are:
You aren't seeding rand(), so every run of your program will generate identical output. You need to use srand() first with a seed. Traditionally one uses time().
Secondly despite the randomness you are unrandomly (is that a word?) filling r_arr. "up" will always be first, "down" will always be second etc.... Instead you should do something like
for (int = 0; i< 10; i++) {
r_num = rand() % 10;
strcpy(r_arr[i], getDirection(r_num));
}
where getDirection() will return a string based on an integer input (e.g.: via a case statement that associates 0 with "up").
Your r_arr needs to be initialized. There is no guarantee in your current code that each entry in the array will be populated with chars before being accessed. If you implement suggestion 2 then you wont have a problem. Today however your code is accessing potentially uninitialized memory.
As noted by others above, your issue is that you're not indexing your array with the iteration number of your loop. You had:
case 0: strcpy(r_arr[0], "up");
Whereas you should have had:
case 0: strcpy(r_arr[i], "up");
The additional thing that I wanted to point out is that rand() uses a linear equation (at least on many systems) so it will be impossible for you to ever get two even numbers in a row or two odd numbers in a row, which is not very random. Hence I suggest something like:
r_num = (rand() >> 8) % 10;
As the commenters pointed out, you are randomizing not what value you put in each position but which positions get filled with their preset value. Also, your use of a switch statement here is just odd. Try something like:
char value_arr[10][10]={"up", "down", "left", "right", "rot_x", "rot_y", "rot_z", "forward", "back", "reset"}
for (int i = 0; i < 10; i++)
{
r_num = rand() % 10;
strcpy(r_arr[i], value_arr[r_num]);
}
Print the strings inside the switch instead of the for-loop at the end.
Maybe you'll also need something like:
srand (time(NULL));
here is a code that fits exactly to your need :
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// we use this instruction to get a random result each time we run the program
srand(time(NULL));
int r_num;
char r_arr[10][10];
// the dictionary array will be used to take from it the possible strings
char dictionary[10][10]={"up","down","left","right","rot_x","rot_x","rot_x","forward","back","reset"};
int i=0;
for(i=0; i<10; i++)
{
// r_num will be assigned randomly an index from dictionary tab
// the general equation is (rand()%(max-min+1))+min to get a random value
// between max and min inclusive
r_num=(rand()%(9-0+1))+0;
// we will put the random string chosen in the array r_num each time
strcpy(r_arr[i],dictionary[r_num]);
}
// this loop will print the result
for(i=0; i<10; i++)
{
printf("r_arr[%d]=%s \n",i,r_arr[i]);
}
return 0;
}
by looking at your output i noticed some strange values like ?v?,
the problem is that not all numbers between 0 and 9 will be generated by
the rand() function which mean that the corresponding array element(to those numbers) will not be initialized and therefor it contain garbage values from what ever was stored in that memory address.
i hope that explain why you are getting those strange values.

Resources