Here is the task i'm working on:
I am given a txt file containing the list of student names, id numbers, schools, majors, and test scores.
Read this contents and copy to the structure in C.
Sort this list using insertion sort.
Print sorted list on the screen.
I checked my coding by muting some parts, there is an error with my insertion sorting function.
I have no idea which part is incorrect. It all makes sense to me. I need help :( here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 1000
#define BUF_SIZE 80
typedef struct {
char name[20];
char studentID[10];
char department[20];
char major[20];
int mid;
int final;
} student;
FILE *fp;
void operation(student t, student list[], int j);
void insertion_sort(student list[], int n);
void printing(student list[], int n);
int main(int argc, char *argv[])
{
char filename[20] = "studentlist.txt";
int n = 1; /* (number of students) + 1 */
student list[MAX];
char buffer[BUF_SIZE];
int i;
fp = fopen(filename, "r");
while (1) {
if (fgets(buffer, BUF_SIZE, fp) == NULL)
break;
strncpy(list[n].name, buffer, strlen(buffer) - 1);
fgets(buffer, BUF_SIZE, fp);
strncpy(list[n].studentID, buffer, strlen(buffer) - 1);
fgets(buffer, BUF_SIZE, fp);
strncpy(list[n].department, buffer, strlen(buffer) - 1);
fgets(buffer, BUF_SIZE, fp);
strncpy(list[n].major, buffer, strlen(buffer) - 1);
fgets(buffer, BUF_SIZE, fp);
list[n].mid = atoi(buffer);
fgets(buffer, BUF_SIZE, fp);
list[n].final = atoi(buffer);
n++;
}
fclose(fp);
insertion_sort(list, n);
printing(list, n);
return 0;
}
void insertion_sort(student list[], int n)
{
int i;
student temp;
for (i = 2; i < n; i++) {
temp = list[i];
operation(temp, list, i - 1);
}
}
void operation(student t, student list[], int j)
{
list[0] = t;
while (t.studentID < list[j].studentID) {
list[j + 1] = list[j];
j--;
}
list[j + 1] = t;
}
void printing(student list[], int n)
{
int i;
for (i = 1; i < n; i++) {
printf(" %s ", list[i].name);
printf(" %s ", list[i].studentID);
printf(" %s ", list[i].department);
printf(" %s ", list[i].major);
printf(" %6d ", list[i].mid);
printf(" %6d ", list[i].final);
putchar('\n');
}
}
Continuing from the comments and the first answer, you are making tracking index error between the split insertion_sort and operation functions. There is no need for two functions when one will work (and arguably be smaller). There is little benefit to be obtained from the split (except for increased confusion).
Putting a simple insertion sort together in one function that will fit your needs (and using pointers to sort as opposed to repeatedly using the function copy-constructor to accomplish the swaps), you could do something similar to the following:
typedef struct {
int id, g;
} student;
void insertion_sort (student **list, int nmemb)
{
for (int i = 0; i < nmemb; i++)
for (int j = i; j > 0 && list[j]->id < list[j-1]->id; j--)
{
student *tmp = list[j];
list[j] = list[j-1];
list[j-1] = tmp;
}
}
Putting together a short (and limited struct member example), you can do something like the following to sort the student data by ID (or id below)
#include <stdio.h>
typedef struct {
int id, g;
} student;
void insertion_sort (student **list, int nmemb)
{
for (int i = 0; i < nmemb; i++)
for (int j = i; j > 0 && list[j]->id < list[j-1]->id; j--)
{
student *tmp = list[j];
list[j] = list[j-1];
list[j-1] = tmp;
}
}
void printing(student *list[], int n)
{
int i;
for (i = 0; i < n; i++) {
printf(" %d ", list[i]->id);
printf(" %d \n", list[i]->g);
}
}
int main (void) {
student s[] = { {.id = 5, .g = 72}, /* minimal student test data */
{.id = 2, .g = 91},
{.id = 4, .g = 77},
{.id = 1, .g = 96},
{.id = 3, .g = 85}};
int n = sizeof s / sizeof *s;
student *l[n];
for (int i = 0; i < n; i++) /* initialize pointers in l */
l[i] = &s[i];
insertion_sort (l, n); /* sort pointers in l */
printing (l, n); /* output sorted pointers */
return 0;
}
Example Use/Output
Sorted by student.id
$ ./bin/inssort_so
1 96
2 91
3 85
4 77
5 72
If you did want to avoid using the additional level of indirection involved in passing an array of pointers (actually a pointer-to-pointer-to-type), you can make your original approach work as follows:
void insertion_sort (student *list, int nmemb)
{
for (int i = 0; i < nmemb; i++)
for (int j = i; j > 0 && list[j].id < list[j-1].id; j--)
{
student tmp = list[j];
list[j] = list[j-1];
list[j-1] = tmp;
}
}
void printing (student *list, int n)
{
for (int i = 0; i < n; i++)
printf (" %d %d \n", list[i].id, list[i].g);
}
int main (void) {
student s[] = { {.id = 5, .g = 72},
{.id = 2, .g = 91},
{.id = 4, .g = 77},
{.id = 1, .g = 96},
{.id = 3, .g = 85}};
int n = sizeof s / sizeof *s;
insertion_sort (s, n);
printing (s, n);
return 0;
}
As you go forward, also consider passing a compare callback function to your sort routines. That allows you to sort many different types of data using your same sort routine -- just by changing the compare function (as is done for the C-library qsort routine.
Related
I need to sort students according to their surname or if their surname is the same according to their name in lexicographical order.
#include <stdio.h>
struct Student {
char name[20], surname[20];
};
void sort(struct Student students[], int n) {
int i, j, temp;
for (i = 0; i < n; i++)
for (j = i + 1; j < n; j++)
if (students[j].surname > students[i].surname ||
students[j].name > students[i].name) {
temp = i;
students[i] = students[j];
students[j] = students[temp];
}
}
void main() {
struct Student students[6] = {
{"Mujo", "Mujic"},
{"Meho", "Mujic"},
{"Pero", "Peric"},
{"Beba", "Bebic"},
{"Mujo", "Mujic"},
{"Fata", "Fatic"},
};
sort(students, 6);
int i;
for (i = 0; i < 6; i++)
printf("%s %s\n", students[i].surname, students[i].name);
}
This prints only one student six times. Could you help me to fix this?
Note: using auxiliary arrays is not allowed
Your swap code is dubious — broken, I believe. A big warning bell is the type of temp — it needs to be a struct Student.
You have:
temp = i;
students[i] = students[j];
students[j] = students[temp];
You need:
struct Student temp = students[i];
students[i] = students[j];
students[j] = temp;
Obviously, remove the definition int temp; as well.
However, there are other problems too (as ever). You can't usefully compare strings using relational operators — use strcmp(). And your ordering test is wrong too, even when revised to use strcmp().
This code does the job, sorting names in descending order:
#include <stdio.h>
#include <string.h>
struct Student
{
char name[20];
char surname[20];
};
static void sort(struct Student students[], int n)
{
for (int i = 0; i < n; i++)
{
for (int j = i + 1; j < n; j++)
{
int rc = strcmp(students[j].surname, students[i].surname);
if (rc > 0 ||
(rc == 0 && strcmp(students[j].name, students[i].name) > 0))
{
struct Student temp = students[i];
students[i] = students[j];
students[j] = temp;
}
}
}
}
static void dump_array(const char *tag, size_t size, const struct Student *students)
{
printf("%s (%zu):\n", tag, size);
for (size_t i = 0; i < size; i++)
printf("%s %s\n", students[i].surname, students[i].name);
}
int main(void)
{
struct Student students[] =
{
{"Mujo", "Mujic"},
{"Meho", "Mujic"},
{"Pero", "Peric"},
{"Zebra", "Elephant"},
{"Beba", "Bebic"},
{"Mujo", "Mujic"},
{"Abelone", "Shells"},
{"Fata", "Fatic"},
};
enum { NUM_STUDENTS = sizeof(students) / sizeof(students[0]) };
dump_array("Before", NUM_STUDENTS, students);
sort(students, NUM_STUDENTS);
dump_array("After", NUM_STUDENTS, students);
return 0;
}
Output:
Before (8):
Mujic Mujo
Mujic Meho
Peric Pero
Elephant Zebra
Bebic Beba
Mujic Mujo
Shells Abelone
Fatic Fata
After (8):
Shells Abelone
Peric Pero
Mujic Mujo
Mujic Mujo
Mujic Meho
Fatic Fata
Elephant Zebra
Bebic Beba
I'm working with grap.
My graph is a structure like this
typedef struct{
int order; // number of node
int **mat; // matrix graphe
}graphe;
I'm working on school project and I need to build a set of binary's number from 0 to N (where is the order of the graph)
Actually I did this, it's working. When I'm printing the final variable, it displays all declinaison of binary number (000, 001, 010, 011, etc...)
char** construireSousEnsemble(graphe g){
int size = pow(2, g.order);
char** D = (char**)malloc(sizeof(char*)*g.order-1);
for (int i = 0; i < size; i++){
D[i] = (char*)malloc(g.order-1);
char buffer[g.order-1];
char tmp[g.order-1];
char final[g.order-1];
for (int j = g.order-1; j >= 0; j--){
int bin = (i >> j)&1;
sprintf(buffer, "%d", bin);
snprintf(tmp, sizeof(tmp), "%s", buffer);
strcat(final, tmp);
if (j == 0){
strcpy(D[i], final);
//printf("%s\n", D[i]);
//printf("%d | %s\n", i, D[i]);
memset(final, 0, sizeof(final)); // reset the zone
}
}
//printf("\n");
}
return D;
}
But in the main function, when I'm calling the function like this:
char** zones = construireSousEnsemble(g);
But when I'm printing the content with zones, I have this:
So I'm a bit lost.
This example is for a 3 nodes graph. If I have a 4 nodes, the weird symbol increase and I won't have 0001 or 0010 etc.., same with 5 or 6 nodes.
So my question is, why is this happening?
By the way, I'm not confortable with C so maybe I made some mistakes.
Thank you all :)
Here is the solution (posting if for the future)
char ** construireSousEnsemble(graphe g){
int size = pow(2, g.order);
char **D = (char **) malloc (sizeof (char *) * size);
char buffer[g.order];
char final[g.order];
for (int i = 0; i < size; i++) {
D[i] = (char *)malloc(g.order + 1);
final[0] = 0;
for (int j = g.order - 1; j >= 0; j--) {
int bin = (i >> j) & 1;
sprintf (buffer, "%d", bin);
strcat (final, buffer);
}
strcpy (D[i], final);
}
return D;
}
This code does not give any errors but displays the array with old
values without sorting by name field, it works up to reading the txt file storing data in a struct array student an instance of struct person, then when used the code to sort it and print the results of sorted array . but it does not work
#include < stdio.h >
#include < string.h >
int main() {
// read data from txt file and stores
// in a struct array
storeInarraySort();
}
int storeInarraySort() {
// struct person with 4 fields
struct person {
char name[100];
char address[100];
char IDnumber[20];
int age;
};
FILE * file = fopen("personout.txt", "r");
// declares an struct array to store data
struct person student[10];
int k = 0;
if (file != NULL)
{
char line[128]; /* or other suitable maximum line size */
/* read a line */
while (fgets(line, sizeof line, file) != NULL)
{
// stores values in struct array
sscanf(line, " %99[^,], %99[^,], %19[^,], %d", student[k].name,
student[k].address, student[k].IDnumber, & student[k].age);
k++;
}
printf("%d\n", k); // no of records in array
fclose(file);
// number of records k r=k first for loop
// inner for loop s=r+1
//char temp;
//struct person temp;
for (int r = 0; r < k - 1; r++) {
for (int s = r + 1; r < k; r++) {
if (strcmp(student[r].name, student[s].name) > 0) {
struct person temp = student[r];
//strcpy(temp,student[r]);
student[r] = student[s];
//strcpy(student[r],student[s]);
student[s] = temp;
//strcpy(student[s],temp);
}
}
}
// prints struct array to check
for (int t = 0; t < k; t++) {
printf("%s\n %s\n %s\n %d\n ", student[t].name,
student[t].address, student[t].IDnumber, student[t].age);
}
}
}
Use selection sort for sorting.
void swap(int *xp, int *yp)
{
int temp = *xp;
*xp = *yp;
*yp = temp;
}
void selectionSort(int arr[], int n)
{
int i, j, min_idx;
// One by one move boundary of unsorted subarray
for (i = 0; i < n-1; i++)
{
// Find the minimum element in unsorted array
min_idx = i;
for (j = i+1; j < n; j++)
if (strcmp(arr[j].name , arr[min_idx].name) < 0)
min_idx = j;
// Swap the found minimum element with the first element
swap(&arr[min_idx], &arr[i]);
}
}
Is there any way to undo the actions or get the original array after I changed the array as shown below.
#include <stdio.h>
void function(int array[]){
array[2] = 20;
//do some extra work
return;
}
int main(void){
int array[5] = {1,2,3,4,5};
function(array);
// code which has to use original array
return 0;
}
You can pack two 32 bit integers (old / new) into a 64 bit integer, example:
#include <stdio.h>
#include <stdint.h>
void function(int64_t array[])
{
array[2] = (array[2] << 32) | 20;
}
void printarr(int64_t array[], size_t n)
{
size_t i;
for (i = 0; i < n; i++) {
printf("%d ", (int32_t)(array[i]));
}
printf("\n");
}
int main(void)
{
int64_t array[] = {1, 2, 3, 4, 5};
size_t i, n = sizeof(array) / sizeof(array[0]);
function(array);
puts("After function:");
printarr(array, n);
for (i = 0; i < n; i++) {
if (array[i] >> 32 != 0) /* Changed */
array[i] = array[i] >> 32; /* Undo */
}
puts("Original values:");
printarr(array, n);
return 0;
}
Output:
After function:
1 2 20 4 5
Original values:
1 2 3 4 5
Note:
Of course you can pack two 16 bit integers in a 32 bit integer if you are using short values in order to save some space.
To be portable use PRId32 format (defined in <inttyes.h>) for printf and int32_t:
printf("%"PRId32" ", (int32_t)x);
Another method:
If those changes are made sequentially over positive integers you can change the sign (to identify a change) and store only the changes using realloc:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int *value;
size_t length;
} t_undo;
void function(t_undo *undo, int array[], int index, int value)
{
undo->value = realloc(undo->value, sizeof(int) * (undo->length + 1));
/* check realloc */
undo->value[undo->length++] = array[index];
array[index] = -value;
}
void printarr(int array[], size_t n)
{
size_t i;
for (i = 0; i < n; i++) {
printf("%d ", abs(array[i]));
}
printf("\n");
}
int main(void)
{
t_undo *undo;
int array[] = {1, 2, 3, 4, 5};
size_t i, j = 0, n = sizeof(array) / sizeof(array[0]);
undo = malloc(sizeof(*undo));
/* check malloc */
undo->value = NULL;
undo->length = 0;
function(undo, array, 2, 20);
puts("After function:");
printarr(array, n);
for (i = 0; i < n; i++) {
if (array[i] < 0) /* Changed */
array[i] = undo->value[j++]; /* Undo */
}
puts("Original values:");
printarr(array, n);
free(undo->value);
free(undo);
return 0;
}
Making it short: No. You cannot have your operations reverted - Not in a simple way anyway. What you probably want is to have a local copy of your data in your function(). You could do this by creating a new array and copy it to your first one:
int array[5] = whatever;
int array_copy[5];
memcpy(array_copy, array, 5*sizeof(int));
function(array_copy);
//You have your array now unchanged
If you really don't wanna do that, there's another way of enclosing your array into a struct and let the compiler synthesize a copy operation for you. However there's a serious downside for this one since for big such struct you may end up wasting your stack.
struct MyArray {
int array[5];
};
void function(struct MyArray m) {
//This will create a local copy of m
int i;
for (i = 0; i < 5; i++) {
m.array[i] = i + 1;
}
}
void initialize(struct MyArray* m) {
int i;
assert(m != NULL);
for (i = 0; i < 5; i++) {
m->array[i] = i;
}
}
int main(int argc, char *argv[]) {
struct MyArray m;
int i;
initialize(&m);
function(m);
//Your m here will be untouched
return 0;
}
#include <stdio.h>
void function(int array[]){
array[2] = 20;
}
void save(const char *filename, const void *data, size_t size);
void undo(const char *filename);
int main(void){
int array[5] = {1,2,3,4,5};
save("save_2", &array[2], sizeof(int));//array[2] save to file
function(array);
undo("save_2");//restore
save("save_all", array, sizeof(array));
function(array);
undo("save_all");
// code which has to use original array
for(int i=0;i<5;++i)
printf("%d\n", array[i]);
remove("save_2");
remove("save_all");
return 0;
}
void save(const char *filename, const void *data, size_t size){
FILE *fp = fopen(filename, "wb");
fwrite(&data, sizeof(void *), 1, fp);
fwrite(&size, sizeof(size_t), 1, fp);
fwrite(data, size, 1, fp);
fclose(fp);、
}
void undo(const char *filename){
FILE *fp = fopen(filename, "rb");
void *data;
size_t size;
fread(&data, sizeof(void*), 1, fp);
fread(&size, sizeof(size_t), 1, fp);
fread(data, size, 1, fp);
fclose(fp);
}
gcc compiles the following code without error. I'm creating a bubble sort function that can be used with arrays of any data type (hence the function pointer).
It sorts the array of character strings (arr2) without a problem, however, I can't figure out why it won't properly sort the array of integers (arr). I added a printf statement in the compare_long function to see what is going on. It doesn't look like the integers are being passed to it properly. Any help will be greatly appreciated.
#include <stdio.h>
#include <string.h>
#define MAX_BUF 256
long arr[10] = { 3,6,1,2,3,8,4,1,7,2};
char arr2[5][20] = { "Mickey Mouse",
"Donald Duck",
"Minnie Mouse",
"Goofy",
"Pluto" };
void bubble(void *p, int width, int N, int(*fptr)(const void *, const void *));
int compare_string(const void *m, const void *n);
int compare_long(const void *m, const void *n);
int main(void) {
int i;
puts("\nBefore Sorting:\n");
for(i = 0; i < 10; i++) { /* show the long ints */
printf("%ld ",arr[i]);
}
puts("\n");
for(i = 0; i < 5; i++) { /* show the strings */
printf("%s\n", arr2[i]);
}
bubble(arr, 4, 10, compare_long); /* sort the longs */
bubble(arr2, 20, 5, compare_string); /* sort the strings */
puts("\n\nAfter Sorting:\n");
for(i = 0; i < 10; i++) { /* show the sorted longs */
printf("%d ",arr[i]);
}
puts("\n");
for(i = 0; i < 5; i++) { /* show the sorted strings */
printf("%s\n", arr2[i]);
}
return 0;
}
void bubble(void *p, int width, int N, int(*fptr)(const void *, const void *)) {
int i, j, k;
unsigned char buf[MAX_BUF];
unsigned char *bp = p;
for(i = N - 1; i >= 0; i--) {
for(j = 1; j <= i; j++) {
k = fptr((void *)(bp + width*(j-1)), (void *)(bp + j*width));
if(k > 0) {
memcpy(buf, bp + width*(j-1), width);
memcpy(bp + width*(j-1), bp + j*width , width);
memcpy(bp + j*width, buf, width);
}
}
}
}
int compare_string(const void *m, const void *n) {
char *m1 = (char *)m;
char *n1 = (char *)n;
return (strcmp(m1,n1));
}
int compare_long(const void *m, const void *n) {
long *m1, *n1;
m1 = (long *)m;
n1 = (long *)n;
printf("m1 = %l and n1 = %l\n", *m1, *n1);
return (*m1 > *n1);
}
The ANSI C spec defines long as a MINIMUM of 4 bytes (32 bits) but GCC is defining long as 8 bytes in your case. It is architecture-specific so you need to use sizeof(long) or one of the C99 types like uint32_t or int32_t if you want a specific size.