Producers and Consumers with semaphores and threads - arrays

I am currently having issues this code. The only part of the code that is having error is "io_production_report(thread_args[0].Produced, pointer);".
The terminal mentions the error as follows"
driver.cpp: In function ‘int main(int, char**)’:
driver.cpp:83:51: error: cannot convert ‘int (*)[2]’ to ‘int**’
83 | io_production_report(thread_args[0].Produced, pointer);
| ^~~~~~~
| |
| int (*)[2]
In file included from driver.cpp:2:
io.h:49:48: note: initializing argument 2 of ‘void io_production_report(int*, int**)’
49 | void io_production_report(int produced[], int *consumed[]);
| ~~~~~^~~~~~~~~~
make: *** [Makefile:8: driver.o] Error 1
I am having problems implementing an array of pointers.
If anyone can help me that would be great!! :
driver.cpp
// Print Production Report
int pointer[2][2];
pointer[0][0]=thread_args[0].Consumed[0]; //Ethel
pointer[0][1]=thread_args[0].Consumed[1]; //Lucy
pointer[1][0]=thread_args[1].Consumed[0];
pointer[1][1]=thread_args[1].Consumed[1];
io_production_report(thread_args[0].Produced, pointer); //NEED HELP ON THIS LINE
driver.h
struct args {
sem_t *mutexOnBelt;
sem_t *frogMax;
sem_t *beltMax;
sem_t *candiesOnBelt;
sem_t *produceMax;
sem_t *consumeMax;
string *belt;
int *head;
int *tail;
int wait_time;
int produced;
string *name;
int Consumed[2];
int onBelt[2];
int Produced[2];
};
int option = 0;
int flagValues[4] = {0, 0, 0, 0};
io.c
/*
* void io_production_report(int produced[], int *consumed[])
* Show how many candies of each type produced. Show how many candies consumed by
* each consumer.
* produced[] - count for each ProductType
* *consumed[] - array of pointers to consumed arrays
* e.g. consumed[Lucy] points to an array that is indexed by product name
*/
void io_production_report(int produced[], int *consumed[]) {
int p, c; /* array indices */
int total;
printf("\nPRODUCTION REPORT\n----------------------------------------\n");
/* show number produced for each producer / candy type */
for (p=0; p < ProductTypeN; p++) {
printf("%s producer generated %d candies\n",
ProducerNames[p], produced[p]);
}
/* show number consumed by each consumer */
for (c=0; c < ConsumerTypeN; c++) {
printf("%s consumed ", ConsumerNames[c]);
total = 0;
for (p=0; p < ProductTypeN; p++) {
if (p > 0)
printf(" + ");
total += consumed[c][p];
printf("%d %s", consumed[c][p], ProducerAbbrevs[p]);
}
printf(" = %d total\n", total);
}
printf("Elapsed time %.3f s\n", elapsed_s());
}

As explained in the comments you need to make a pointer to the first element of your array, id est: you specify you array[N][M] as int (*)[M], working example:
#include <stdio.h>
void io_production_report(int (*)[2]);
void io_production_report(int (*consumed)[2]) {
for (int i = 0; i <4; i++)
printf("%d\n", consumed[i/2][i%2]);
}
int main(void) {
int pointer[2][2];
pointer[0][0]=1; //Ethel
pointer[0][1]=2; //Lucy
pointer[1][0]=3;
pointer[1][1]=4;
io_production_report(pointer);
}

Related

Round Robin Scheduler in C

I need a bit of help with my round robin scheduler. It is meant to take external C files and place them in a queue while displaying their current state, and execute them through round robin scheduling. Here is what I have so far:
#include<pthread.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
enum states {running, ready, waiting, idle};
struct process{
//command to run process
enum states state;
int id;
};
struct process procs[5];
int procSize = 5;
void *runProcess(struct process *p)
{
while(1)
{
printf("Process %i is running", p->id);
sleep(1);
}
}
void *takeInput(void *vargp)
{
//print process statuses when 'procs' is entered
while(1)
{
char *str[64];
fgets(str, 64, stdin);
if (strcmp(str, "procs"))
{
for (int i = 0; i < procSize; i++)
{
printf("Process %i: %i\n", procs[i].id, procs[i].state);
}
}
}
}
void *schedule(void *vargp)
{
struct process p = procs[0];
if (p.state == idle)
{
p.state = ready;
}
if (p.state == ready)
{
p.state = running;
pthread_t run;
pthread_create(&run, NULL, runProcess, &p);
//pthread_join(run, NULL);
sleep(5);
pthread_cancel(run);
p.state = ready;
for(int i = procSize - 1; i > 0; i--)
{
procs[i-1] = procs[i];
}
procs[procSize] = p;
}
else if (p.state == waiting)
{
for(int i = procSize - 1; i > 0; i--)
{
procs[i-1] = procs[i];
}
procs[procSize] = p;
}
}
int main()
{
for (int i = 0; i < 5; i++)
{
struct process p;
p.state = idle;
p.id = i;
procs[i] = p;
}
pthread_t schedulerid;
pthread_t inputid;
pthread_create(&schedulerid, NULL, schedule, NULL);
//pthread_join(schedulerid, NULL);
pthread_create(&inputid, NULL, takeInput, NULL);
//pthread_join(inputid, NULL);
}
When I attempt to run this, I get no errors, only warnings, and nothing happens. What do I need to improve on? Is there an issue with trying to call functions using threads? Any help is appreciated.
UPDATED WITH WARNINGS:
Sched.c:35:20: warning: passing argument 1 of ‘strcmp’ from incompatible pointer type [-Wincompatible-pointer-types]
35 | if (strcmp(str, "procs"))
| ^~~
| |
| char **
In file included from Sched.c:3:
/usr/include/string.h:137:32: note: expected ‘const char *’ but argument is of type ‘char **’
137 | extern int strcmp (const char *__s1, const char *__s2)
| ~~~~~~~~~~~~^~~~
Sched.c: In function ‘schedule’:
Sched.c:56:36: warning: passing argument 3 of ‘pthread_create’ from incompatible pointer type [-Wincompatible-pointer-types]
56 | pthread_create(&run, NULL, runProcess, &p);
| ^~~~~~~~~~
| |
| void * (*)(struct process *)
In file included from Sched.c:1:
/usr/include/pthread.h:200:15: note: expected ‘void * (*)(void *)’ but argument is of type ‘void * (*)(struct process *)’
200 | void *(*__start_routine) (void *),
| ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
Lets fix those warnings
char *str[64];
fgets(str, 64, stdin);
if (strcmp(str, "procs"))
should be
char str[64];
fgets(str, 64, stdin);
if (strcmp(str, "procs"))
then
pthread_create(&run, NULL, runProcess, &p);
The function must be a void* func (void*)
So change run process to
void *runProcess(void *v)
{
struct process *p =(struct process*)v;
while(1)
{
printf("Process %i is running", p->id);
sleep(1);
}
}
ie pass a void * and cast it to what you need
this compiles with no warning now.
Finally put your joins back in
int pt1 = pthread_create(&schedulerid, NULL, schedule, NULL);
int pt2 = pthread_create(&inputid, NULL, takeInput, NULL);
printf("%d %d \n", pt1, pt2);
pthread_join(schedulerid, NULL);
pthread_join(inputid, NULL);
as a final note - you really must test the returns from all your system calls to make sure they worked
here it is running
pm100#paul-think:~/ut$ gcc ggg.c -g -lpthread
pm100#paul-think:~/ut$ ./a.out
Process 0 is running

How to create new threads per function call in c

I'm trying to implement a shuffling technique based on a random time so that it is possible for one message to overtake the next message and becomes shuffled. I have an original message that is separated into chunks per thread. Each thread sleeps and prints out the chunk afterwards.
original_message > grab a slice of original_message > deal with it in another thread > grab another slice > deal with it in another NEW thread while the other thread may/may not be running > and repeat until all slice is taken.
However, sometimes the output is the same and that they do not output the after the sleep one by one e.g. the one with 0 seconds sleep comes out first.... How can I create a new thread with its own stack so that it does not share those variables. I'm not sure mutex lock will suffice because it may affect the sleep timings. What can I do to fix it?
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
struct argsparam {
int sleep;
char split_message[5];
};
void* sleep_shuffle(void *arguments){
struct argsparam *args = arguments;
sleep(args->sleep);
printf("shuffled message: %s\n", args->split_message);
}
int main(){
struct argsparam args;
pthread_t sleeppt[4];
time_t t;
srand((unsigned) time(&t));
char original_message[20] = "1234567890abcdefghij";
for(int i=0; i<4; i++){
memcpy(args.split_message , original_message+(i*5), 5);
args.split_message[5] = '\0'; //null terminator
args.sleep = rand() % 5;
printf("message: %s and time: %d\n", args.split_message, args.sleep);
if(pthread_create( &sleeppt[i], NULL, sleep_shuffle, (void *)&args) != 0)
printf("Error in creating thread!");
}
for(int i=0; i<4; i++){
pthread_join(sleeppt[i], NULL);
}
}
You're passing the same copy of args to each thread, overwriting the previous value before calling the next thread, so what each thread reads is dependent on the timing of each thread.
Create an array of struct argsparam, one for each thread, then pass in the pointer to the appropriate instance. That way they won't interfere with each other.
int main(){
// an array of args
struct argsparam args[4];
pthread_t sleeppt[4];
time_t t;
srand((unsigned) time(&t));
char original_message[20] = "1234567890abcdefghij";
for(int i=0; i<4; i++){
// assign to a specific array instance for each thread
memcpy(args[i].split_message , original_message+(i*5), 5);
args[i].split_message[5] = '\0'; //null terminator
args[i].sleep = rand() % 5;
printf("message: %s and time: %d\n", args[i].split_message, args[i].sleep);
if(pthread_create( &sleeppt[i], NULL, sleep_shuffle, (void *)&args[i]) != 0)
printf("Error in creating thread!");
}
for(int i=0; i<4; i++){
pthread_join(sleeppt[i], NULL);
}
}
Sample output (yours may vary):
message: 12345 and time: 0
message: 67890 and time: 3
message: abcde and time: 2
message: fghij and time: 1
shuffled message: 12345
shuffled message: fghij
shuffled message: abcde
shuffled message: 67890
Also, your split_message array isn't big enough to hold the string you're copying in. You need a total of 6 bytes, for for each of 5 characters and one for the null terminator.
struct argsparam {
int sleep;
char split_message[6];
};
You're "lucky" that you're not stepping on memory you shouldn't due to structure padding.
First, the size of the "split_message[5] of struct argsparam" array is incorrect.
Second, why not use "args" as an array?
--- orig.c 2017-06-14 03:18:25.569775282 +0900
+++ dest.c 2017-06-14 03:16:52.989777492 +0900
## -6,7 +6,7 ##
struct argsparam {
int sleep;
- char split_message[5];
+ char split_message[6];
};
void* sleep_shuffle(void *arguments){
## -16,7 +16,7 ## void* sleep_shuffle(void *arguments){
}
int main(){
- struct argsparam args;
+ struct argsparam args[4];
pthread_t sleeppt[4];
time_t t;
int i;
## -25,12 +25,12 ## int main(){
char original_message[20] = "1234567890abcdefghij";
for(i=0; i<4; i++){
- memcpy(args.split_message , original_message+(i*5), 5);
- args.split_message[5] = '\0'; //null terminator
- args.sleep = rand() % 5;
+ memcpy(args[i].split_message , original_message+(i*5), 5);
+ args[i].split_message[5] = '\0'; //null terminator
+ args[i].sleep = rand() % 5;
- printf("message: %s and time: %d\n", args.split_message, args.sleep);
- if(pthread_create( &sleeppt[i], NULL, sleep_shuffle, (void *)&args) != 0)
+ printf("message: %s and time: %d\n", args[i].split_message, args[i].sleep);
+ if(pthread_create( &sleeppt[i], NULL, sleep_shuffle, (void *)(args + i)) != 0)
printf("Error in creating thread!");
}

Unexpected Segmentation Fault in pthread Program

So a foreword, I am new to C, so please forgive any horrifying mistakes my program has.
I am trying to write a program that will take a list of numbers passed in as arguments to the program or contained in a file whose path is passed in.
It stores the numbers into an array, and stores how many numbers there are into the first element of the array. It will only store up to 100 numbers.
It then creates a pthread and passes the pointer to the array to the thread.
The thread is then suppose to sum up the numbers and return the sum back to the main function.
I am experiencing the following issues:
1. It doesn't always happen, but sometimes I get a segmentation fault right before the line of code that says:
printf("Begining to create the thread");
2. My attempts to return the sum isn't working, and after hours of research online, I can't figure out why.
3. When I compile the program, I get the following errors:
gcc -g -o assn3 assn3.c -pthread
assn3.c: In function ‘AddUpNumbers’:
assn3.c:34:9: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
return (void*)total;
^
assn3.c: In function ‘main’:
assn3.c:96:3: warning: passing argument 1 of ‘pthread_join’ makes integer from pointer without a cast [enabled by default]
pthread_join(&functionThread, &total);
^
In file included from assn3.c:12:0:
/usr/include/pthread.h:261:12: note: expected ‘pthread_t’ but argument is of type ‘pthread_t *’
extern int pthread_join (pthread_t __th, void **__thread_return);
^
assn3.c:97:3: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘void *’ [-Wformat=]
printf("The total returned by the thread is %d", total);
^
Here's my code:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NO_ARGS 1
#define ONLY_SINGLE_ARG 2
#define PATH_TO_READ_FROM 1
#define MAX_NUMBERS 101
#define MAX_CHAR_INPUT 255
#define COUNT_LOCATION 0
void* AddUpNumbers(void* arrayPointer) {
printf("Created the pthread!");
int* numbersArray = (int*)arrayPointer;
int count = numbersArray[COUNT_LOCATION];
int total = 0;
int i = 1;
while (i < count) {
total = total + numbersArray[i];
}
printf("The total to be returned is %d", total);
return (void*)total;
}
int main(int argc, char* argv[]) {
FILE * numbersFile = NULL;
int count = 0;
int numberArray[MAX_NUMBERS];
//Initialize the Array
int i = 0;
while (i < MAX_NUMBERS) {
numberArray[i] = 0;
i = i + 1;
}
if (argc == NO_ARGS) {
printf("Usage: # or file path, #, #, ..., #\n");
} else if (argc == ONLY_SINGLE_ARG) {
numbersFile = fopen(argv[PATH_TO_READ_FROM], "r");
if (numbersFile != NULL) {
char buff[MAX_CHAR_INPUT];
i = 1;
count = 0;
while (i < MAX_NUMBERS) {
if (fscanf(numbersFile, "%s", buff) != EOF) {
numberArray[i] = atoi(buff);
printf("%d\n", numberArray[i]);
i = i + 1;
count = count + 1;
} else {
break;
}
}
numberArray[COUNT_LOCATION] = count;
printf("Count Total: %d\n", numberArray[COUNT_LOCATION]);
} else {
printf("Error: Could not open file!\n");
return -1;
}
} else if (argc < MAX_NUMBERS + 1) {
i = 1;
count = 0;
while (i < argc) {
numberArray[i] = atoi(argv[i]);
printf("%d\n", numberArray[i]);
i = i + 1;
count = count + 1;
}
printf("See if error happens after this");
numberArray[COUNT_LOCATION] = count;
printf("Count Total: %d\n", numberArray[COUNT_LOCATION]);
} else {
printf("Too many numbers! This program can only add up to: %d numbers.\n", MAX_NUMBERS);
return -1;
}
printf("Begining to create the thread");
pthread_t functionThread;
int creationSuccess = 0;
void* total;
creationSuccess = pthread_create(&functionThread, NULL, AddUpNumbers, (void*)numberArray);
if (creationSuccess == 0) {
pthread_join(&functionThread, total);
printf("The total returned by the thread is %d", *((int)total));
} else {
printf("Something went wrong.\n");
}
if (numbersFile != NULL) {
fclose(numbersFile);
}
return 0;
}
My Makefile looks like this:
assn3: assn3.c
gcc -g -o assn3 assn3.c -pthread
You should be very wary of compiler warnings. Either clean them up or understand very well why they are ok. Pay special attention to warnings about data type mismatches.
In this case this warning probably explains the main problem:
In file included from assn3.c:12:0:
/usr/include/pthread.h:261:12: note: expected ‘pthread_t’ but argument is of type ‘pthread_t *’
extern int pthread_join (pthread_t __th, void **__thread_return);
^
You are (creating and) passing a pointer to your pthread_t object as the first argument of pthread_join(), but unlike pthread_create(), pthread_join() expects you to pass the pthread_t itself, not a pointer to it. All manner of havoc (technically, "undefined behavior") will ensue.
UPDATE: Additionally, the second argument you are passing to pthread_join() is an uninitialized pointer to void. If pthread_create() tries to write anything where it points then who knows what happens (undefined behavior again). You are expected to pass a valid pointer to the location where the result is to be written. In this case, that would be &total.
The syntax of pthread_create is:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, *(*start_routine) (void *), void *arg);
pthread_t - is id of thread. So create variable pthread_t id, or array of values if you have a lot of threads, like pthread_t id[THREAD_NUM];
Then your func will looks like:
pthread_create(&id[i], NULL, &functionThread, (void*)numberArray);
With pthread_join(&functionThread, total);
The same thing.
int pthread_join(pthread_t thread, void **value_ptr);
So you join must look like:
pthread_join(&id[i], total);

Structure pointer operator

Code is meant to create an array of pointers to student structure in order to use the array of pointers in other functions. I'm not sure how to use the arrow operator in binary function. It doesn't return a value for the index where id is found.
typedef struct{
int IDno;
char name[20];
int project;
int exam;
double final;
} student;
student **create_class_list(char*filename, int *sizePtr);
void print_list(student**list,int *sizePtr);
int find_binsrch(int idNo, student **list, int size,int low, int high);
int main(void){
int i, n;
student **listPtr;
listPtr = create_class_list("student.txt", &n);
print_list(listPtr,&n);
index2 = find_binsrch(searchID, listPtr, n, 1200, 4580);
}
student **create_class_list(char *filename, int *sizeptr){
int n,i;
FILE *fptr;
fptr=fopen(filename,"r");
if(fptr==NULL)
printf("The file could not be opened.\n");
else
fscanf(fptr, "%d",sizeptr);
n=*sizeptr;
student **list;
list = (student**)calloc(1, sizeof(student*));
for(i=0;i<n;i++){
list[i]=(student*)calloc(n,sizeof(student));
fscanf(fptr,"%d %[^\n]s", &(list[i]->IDno),(list[i]->name));
}
return list;
}
void print_list(student**list,int *sizePtr){
int i;
for(i=0; i<*sizePtr; i++){
printf("%d %s\n",&(list[i]->IDno),(list[i]->name));
}
}
int find_binsrch(int idNo, student **list, int size, int low, int high){
int middle, i;
while(low<=high){
middle =(low+high)/2;
printf("%d\n", middle);
if(idNo==list[middle]->IDno)
return list[i]->IDno;
if(idNo<list[middle]->IDno)
high = middle -1;
else
low = middle +1;
return -1;
}
}
What you must learn to do is enable Warnings every time you compile. This allows the compiler to identify many areas in your code that need attention. You should not accept code that compiles with warnings. There are only very, very rare circumstances where it is acceptable to rely on code that compiles with warnings (none that you will likely encounter in your first year of programming) So always enable -Wall -Wextra as part of your compile string. (you can also enable -pedantic to see additional warnings as well as some specific warning requests, but for general use -Wall -Wextra will do)
Had you compiled with warnings you would have seen:
students3.c: In function ‘main’:
students3.c:23:5: error: ‘index2’ undeclared (first use in this function)
index2 = find_binsrch(searchID, listPtr, n, 1200, 4580);
^
students3.c:23:5: note: each undeclared identifier is reported only once for each function it appears in
students3.c:23:27: error: ‘searchID’ undeclared (first use in this function)
index2 = find_binsrch(searchID, listPtr, n, 1200, 4580);
^
students3.c:19:9: warning: unused variable ‘i’ [-Wunused-variable]
int i, n;
^
students3.c: In function ‘print_list’:
students3.c:53:9: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat=]
printf("%d %s\n",&(list[i]->IDno),(list[i]->name));
^
students3.c: In function ‘find_binsrch’:
students3.c:57:48: warning: unused parameter ‘size’ [-Wunused-parameter]
int find_binsrch(int idNo, student **list, int size, int low, int high){
^
students3.c: In function ‘main’:
students3.c:24:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
students3.c: In function ‘find_binsrch’:
students3.c:74:1: warning: control reaches end of non-void function [-Wreturn-type]
}
<snip>
Simply addressing the warnings/errors and recompiling (and addressing new warnings/errors disclosed from fixing the first list) will allow you to systematically correct your code. Taking these basic steps will allow you to correct your code to the point it will compile without warnings:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
int IDno;
char name[20];
int project;
int exam;
double final;
} student;
student **create_class_list(char*filename, int *sizePtr);
void print_list(student**list,int *sizePtr);
int find_binsrch(int idNo, student **list, int size,int low, int high);
int main(void){
int n, index2, searchID = 2;
student **listPtr = NULL;
listPtr = create_class_list("student.txt", &n);
if (!listPtr) {
fprintf (stderr, "error: create_class_list failed.\n");
return 1;
}
print_list(listPtr,&n);
index2 = find_binsrch(searchID, listPtr, n, 1200, 4580);
if (index2) {} /* stub to eliminate unused warning */
return 0;
}
student **create_class_list(char *filename, int *sizeptr){
int n,i;
FILE *fptr;
fptr=fopen(filename,"r");
if(fptr==NULL)
printf("The file could not be opened.\n");
else
fscanf(fptr, "%d",sizeptr);
n=*sizeptr;
student **list;
list = (student**)calloc(n, sizeof(student*));
for(i=0;i<n;i++){
list[i]=(student*)calloc(n,sizeof(student));
fscanf(fptr,"%d %[^\n]s", &(list[i]->IDno),(list[i]->name));
}
return list;
}
void print_list(student**list,int *sizePtr){
int i;
for(i=0; i<*sizePtr; i++){
printf("%d %s\n",list[i]->IDno, list[i]->name);
}
}
int find_binsrch(int idNo, student **list, int size, int low, int high)
{
int middle;
if (size) {} /* stub to eliminate unused warning */
while(low<=high){
middle =(low+high)/2;
printf("%d\n", middle);
if(idNo==list[middle]->IDno)
return list[middle]->IDno;
if(idNo<list[middle]->IDno)
high = middle -1;
else
low = middle +1;
}
return -1;
}
Note: whether it runs correctly is a different question indeed, that depends on your data and the elimination of any logic errors.
In your binary search routine, your if's are comparing idNo against list[middle] when they need to compare against list[middle].idNo
You could simplify a bit by using a 1D array that gets realloc'ed rather than a 2D array of pointers. The entire code will be simpler and you won't lose any functionality.
UPDATE
I've switched your code to use an array of structs rather than an array of pointers to structs. It simplifies things and the two level lookup was just adding complexity that was probably tripping you up. Also, cleaned up more style-wise--Sorry about that but it's how I was able to see enough of your logic in order to make the changes.
Note: I agree completely with David [and many others] about compiler warnings. They are your friends. They usually show bugs that are 10x harder to find with a running program. I've been doing C for many years, I [still] always use -Wall -Werror
If you'd like to learn more about pointers to structs, arrays of structs, see my recent answer Issue implementing dynamic array of structures It has a primer on the various ways to switch between arrays, pointers to arrays, indexing of pointers, etc. that may be useful.
Added a full diagnostic suite that proves the binsrch algorithm, including edge cases that might not appear for a given set of data before turning it loose on real/large data. A good technique to remember.
Note that I'm not sure why you passed low/high as arguments as they serve no purpose for binary search in general. They're useful if you wanted a specific subset of the data. If so, comment out my extra code resetting them.
// binsrch -- program to do binary search
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int IDno;
char name[20];
int project;
int exam;
double final;
} student;
student *
create_class_list(char *filename,int *sizeptr)
{
int n;
int i;
FILE *fptr;
student *cur;
student *list;
fptr = fopen(filename,"r");
if (fptr == NULL)
printf("The file could not be opened.\n");
else
fscanf(fptr,"%d",sizeptr);
n = *sizeptr;
list = calloc(n,sizeof(student));
for (i = 0; i < n; i++) {
cur = &list[i];
fscanf(fptr,"%d %[^\n]s",&cur->IDno,cur->name);
}
fclose(fptr);
return list;
}
void
print_list(student *list,int count)
{
int i;
student *cur;
for (i = 0; i < count; i++) {
cur = &list[i];
printf("%d %s\n",cur->IDno,cur->name);
}
}
student *
find_binsrch(int idNo,student *list,int count,int low,int high)
{
student *cur;
int middle;
student *match;
match = NULL;
// what is the purpose of the limited range? -- ignore for now
low = 0;
high = count - 1;
while (low <= high) {
middle = (low + high) / 2;
cur = &list[middle];
//printf("find_binsrch: TRACE middle=%d\n",middle);
if (idNo == cur->IDno) {
match = cur;
break;
}
if (idNo < cur->IDno)
high = middle - 1;
else
low = middle + 1;
}
return match;
}
#define RAND0(_lim) \
(rand() % _lim)
#define RAND1(_lim) \
(RAND0(_lim) + 1)
// diag_binsrch -- run diagnostic on single array size
void
diag_binsrch(int count)
{
student *list;
student *cur;
int searchidx;
student *match;
int err;
list = calloc(count,sizeof(student));
searchidx = 0;
cur = &list[searchidx];
cur->IDno = RAND1(30);
// create interesting data
++searchidx;
for (; searchidx < count; ++searchidx)
list[searchidx].IDno = list[searchidx - 1].IDno + RAND1(137);
err = 0;
// search for something lower that the lowest -- we _want_ it to fail
searchidx = 0;
cur = &list[searchidx];
match = find_binsrch(cur->IDno - 1,list,count,1200,4580);
if (match != NULL) {
printf("DIAG: expected failure -- searchidx=%d cur=%d match=%d\n",
searchidx,cur->IDno - 1,match->IDno);
++err;
}
// search for something higher that the highest -- we _want_ it to fail
searchidx = count - 1;
cur = &list[searchidx];
match = find_binsrch(cur->IDno + 1,list,count,0,count - 1);
if (match != NULL) {
printf("DIAG: expected failure -- searchidx=%d cur=%d match=%d\n",
searchidx,cur->IDno + 1,match->IDno);
++err;
}
// search for all remaining entries -- they should all match
cur = list;
for (searchidx = 0; searchidx < count; ++searchidx, ++cur) {
match = find_binsrch(cur->IDno,list,count,0,count - 1);
if (match == NULL) {
printf("DIAG: null return -- searchidx=%d IDno=%d\n",
searchidx,cur->IDno);
++err;
continue;
}
if (match->IDno != cur->IDno) {
printf("DIAG: mismatch -- searchidx=%d cur=%d match=%d\n",
searchidx,cur->IDno,match->IDno);
++err;
continue;
}
}
free(list);
if (err)
exit(1);
}
// diag_binsrch_full -- run full diagnostic
void
diag_binsrch_full(void)
{
int count;
printf("diag_binsrch_full: start ...\n");
for (count = 1; count < 1000; ++count)
diag_binsrch(count);
for (count = 1000; count <= 10000000; count *= 10)
diag_binsrch(count);
printf("diag_binsrch_full: complete\n");
}
int
main(void)
{
int listCount;
student *listPtr;
//student *cur;
//student *match;
// run diagnostic
diag_binsrch_full();
exit(0);
listPtr = create_class_list("student.txt",&listCount);
print_list(listPtr,listCount);
#if 0
match = find_binsrch(searchID,listPtr,n,1200,4580);
if (match != NULL)
printf("main: MATCH IDno=%d name='%s'\n",match->IDno,match->name);
#endif
return 0;
}

Loss of values in array in struct after function execution

I am working on a c code that holds a structure that hosts some values which I call range.
My purpose is to use this so called range dynamically (holding different amount of data at every execution). I am now provisionally using the # define comp instead. This so called range gets updated every time I call my update_range though the use of s1 structure (and memory allocations).
What I found weird is that when I introduced a "show_range" function to output the actual values inside/outside the update function I realized that I loose the first two values.
Here is the code.
Any suggestions on that?
Thanks in advance!
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <string.h>
#include <complex.h>
#define comp 1024
// struct holding a complex-valued range
struct range {
int dimensions; /* number of dimensions */
int* size; /* array holding number of points per dimension */
complex double* values; /* array holding complex valued */
int components; /* number of components that will change on any execution*/
};
// parameters to use in function
struct s1 {
int tag;
struct range* range;
};
int update_range(struct s1* arg);
int show_range(struct range* argrange, char* message);
int copy_range(struct range* in, struct range* out);
int main(void) {
int ret = 0;
struct s1 s1;
s1.tag = 0;
s1.range = malloc(sizeof(struct range));
update_range(&s1);
show_range(s1.range, "s1.range inside main function");
return ret;
}
////////////////////////////////////////////
int update_range(struct s1* arg) {
int ret = 0;
int i;
struct range range;
range.dimensions = 1;
range.size = malloc(range.dimensions * sizeof(int));
range.components = comp;
range.size[0] = range.components; // unidimensional case
range.values = malloc(range.components * sizeof(complex double));
for (i = 0; i < range.components; i++) {
range.values[i] = (i + 1) + I * (i + 1);
}
show_range(&range, "range inside update_range function");
arg->range->size =
malloc(range.dimensions * sizeof(int)); // size was unknown before
arg->range->values =
malloc(comp * sizeof(complex double)); // amount of values was unknown
copy_range(&range, arg->range);
show_range(arg->range, "arg->range inside update_range function");
if (range.size)
free(range.size);
range.size = NULL;
if (range.values)
free(range.values);
range.values = NULL;
return ret;
}
////////////////////////////////////////////
// Show parameters (10 first values)
int show_range(struct range* argrange, char* message) {
int ret = 0;
vint i;
printf(" ******************************\n");
printf(" range in %s \n", message);
printf(" arg.dimensions=%d \n", argrange->dimensions);
printf(" arg.size[0]=%d \n", argrange->size[0]);
printf(" argrange.components=%d \n", argrange->components);
printf(" first 10 {Re} values: \n");
for (i = 0; i < 10; i++) {
printf(" argrange.values[%d]=%f\n", i, creal(argrange->values[i]));
}
printf("\n");
return ret;
}
////////////////////////////////////////////
// copy range
int copy_range(struct range* in, struct range* out) {
int ret = 0;
if (in == NULL) {
fprintf(stderr, "error: in points to NULL (%s:%d)\n", __FILE__,
__LINE__);
ret = -1;
goto cleanup;
}
if (out == NULL) {
fprintf(stderr, "error: out points to NULL (%s:%d)\n", __FILE__,
__LINE__);
ret = -1;
goto cleanup;
}
out->dimensions = in->dimensions;
out->size = in->size;
out->values = in->values;
out->components = in->components;
cleanup:
return ret;
}
Your copy_range function is broken, because it copy only pointer to size and values and not the memory. After you call free(range.size); and free(range.values); you are deleting mamory also from original object but without setting its pointers back to NULL.
After calling update_range, s1.range has non NULL pointers in size and values, but they are pointing to deleted memory.
You are experiencing undefined behaviour (UB) due to accessing freed memory. Your copy_range() function only does a shallow copy of the two pointer fields so when you run free(range->size) you make arg->range->size invalid.
You should make copy_range() a deep copy by allocating and copying the pointer contents like:
out->size = malloc(in->dimensions * sizeof(int));
memcpy(out->size, in->size, in->dimensions * sizeof(int));
out->values = malloc(in->components * sizeof(complex double));
memcpy(out->values , in->values, in->components * sizeof(complex double));
There are not 10 items to print, so the lines:
printf(" first 10 {Re} values: \n");
for (i = 0; i < 10; i++) {
printf(" argrange.values[%d]=%f\n", i, creal(argrange->values[i]));
}
Will be printing from random memory.
a much better method would be:
printf(" first %d {Re} values: \n", min(argrange.components,10));
for (i = 0; i < argrange.components; i++) {
printf(" argrange.values[%d]=%f\n", i, creal(argrange->values[i]));
}
The above is just one of many problems with the code.
I would suggest executing the code using a debugger to get the full story.
as it is, the code has some massive memory leaks due mostly
to overlaying malloc'd memory pointers.
for instance as in the following:
arg->range->size =
malloc(range.dimensions * sizeof(int)); // size was unknown before
arg->range->values =
malloc(comp * sizeof(complex double)); // amount of values was unknown

Resources