I have been trying to free() the memory at the end however my instructor stated that I have created 3 malloc'd pointers (with the current settings).
NOTE: I would like as detailed explanation possible regarding the malloc/what's actually going on in memory.
I would appreciate guidance on what I can do to make sure there is no memory leak.
I have written the following.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LUNCH_ITEMS 5
#define REMAINING 2
#define CHAR_SIZE 256
int main(void)
{
struct Food
{
char *name; //name attribute of food
int weight, calories;
} lunch[LUNCH_ITEMS] = {{"apple", 4, 100}, {"salad", 2, 80},};
int loopCount;
//INPUT
char *namePtr = NULL;
for (loopCount = REMAINING; loopCount < LUNCH_ITEMS; ++loopCount)
{
char tempBuffer[CHAR_SIZE];
printf("Enter name of item,the weight of item, and the calories in that item: \n");
// store name string in a tempBuffer. weight and calories directly into lunch structure array address
scanf("%255s %d %d", tempBuffer, &lunch[loopCount].weight, &lunch[loopCount].calories);
// get the exact size of (memory of) the input string including add one for null char
size_t exactMemory = strlen(tempBuffer) + 1;
//dynamically allocate the exact amount of memory determined in the previous step
namePtr = (char *)malloc(exactMemory * sizeof(char));
// check if no memory is allocated for foodPtr
if (namePtr == NULL)
{
fprintf(stderr, "Not enough memory available***\n Terminating Program");
exit(1);
}
//store the pointer to it in the name member of the structure in
//the current lunch array element.
(lunch + loopCount)->name = namePtr;
// Copy the food name (stored in tempbuffer) into the dynamically-allocated
// memory using the memcpy function
memcpy(namePtr, tempBuffer, exactMemory);
//(lunch + loopCount)->name = namePtr;
}
//DISPLAY
printf("Item Weight Cals\n---------------------------------------------\n");
for (loopCount = 0; loopCount < LUNCH_ITEMS; loopCount++)
{
int weight = lunch[loopCount].weight;
int cals = lunch[loopCount].calories;
printf("%-12.20s%22d%11d\n", (lunch + loopCount)->name, weight, cals);
if (loopCount > REMAINING)
{
//(lunch+loopCount)->name = NULL;
namePtr = NULL;
free(namePtr);
//free((lunch + loopCount)->name);
}
}
//De-allocate all malloc'd memory
return EXIT_SUCCESS;
}
My output -
Item Weight Cals
-----------------
apple 4 100
salad 2 80
hello 22 33
maybe 44 45
right 100 200
Look at the initialization loop:
for (loopCount = REMAINING; loopCount < LUNCH_ITEMS; ++loopCount)
{
// The code inside the loop will be executed for
// loopCount being equal to
// REMAINING
// REMAINING + 1
// ....
// LUNCH_ITEMS - 1
//
// So in your case, you execute this code for
// loopCount equal to 2, 3 and 4
}
In other words, the code inside the loop is executed 3 times, i,e, you call malloc 3 times.
In the same way, look at the loop where you release the memory.
for (loopCount = 0; loopCount < LUNCH_ITEMS; loopCount++)
{
// you execute this code for
// loopCount equal to 0, 1, 2, 3 and 4
// ....
if (loopCount > REMAINING)
{
// Since REMAINING is 2, you execute this code for
// loopCount equal 3 and 4
}
}
So the code in the body of the if is only executed 2 times. You really want to do it 3 times (i.e. for loopCount equal 2, 3 and 4). So you need to change the code to:
if (loopCount >= REMAINING) // Notice the = sign
{
// Since REMAINING is 2, you execute this code for
// loopCount equal 2, 3 and 4
}
Now regarding malloc and free. When you release memory, i.e. using free, you must pass exactly the value returned to you by malloc
In the initialization you saved the pointer like this:
namePtr = (char *)malloc(exactMemory * sizeof(char));
// ...
(lunch + loopCount)->name = namePtr; // Save pointer
So it is (lunch + loopCount)->name that shall be used for free. Like:
if (loopCount >= REMAINING) // Notice the = sign
{
free((lunch + loopCount)->name);
// Optional part but always after calling free
(lunch + loopCount)->name = NULL;
}
BTW: Notice that
(lunch + loopCount)->name
is the same as
lunch[loopCount].name
which many consider easier to read.
I think your instructor's comment about 3 malloc'ed strings is a pretty strong clue. I notice you have an array of 5 items, with 2 items pre-populated. 5 - 2 is 3.
Also, please be aware that indexes in C start from 0. The 2 items you pre-initialize your array with will have index 0 and index 1. Index 2 will be the first entered data. Comparing that index using > 2 will actually cause you to skip over the index of the first piece of user supplied data.
Related
I'm trying to do a program that get number of names from the user, then it get the names from the user and save them in array in strings. After it, it sort the names in the array by abc and then print the names ordered. The program work good, but the problem is when I try to free the dynamic memory I defined.
Here is the code:
#include <stdio.h>
#include <string.h>
#define STR_LEN 51
void myFgets(char str[], int n);
void sortString(char** arr, int numberOfStrings);
int main(void)
{
int i = 0, numberOfFriends = 0, sizeOfMemory = 0;
char name[STR_LEN] = { 0 };
char** arrOfNames = (char*)malloc(sizeof(int) * sizeOfMemory);
printf("Enter number of friends: ");
scanf("%d", &numberOfFriends);
getchar();
for (i = 0; i < numberOfFriends; i++) // In this loop we save the names into the array.
{
printf("Enter name of friend %d: ", i + 1);
myFgets(name, STR_LEN); // Get the name from the user.
sizeOfMemory += 1;
arrOfNames = (char*)realloc(arrOfNames, sizeof(int) * sizeOfMemory); // Change the size of the memory to more place to pointer from the last time.
arrOfNames[i] = (char*)malloc(sizeof(char) * strlen(name) + 1); // Set dynamic size to the name.
*(arrOfNames[i]) = '\0'; // We remove the string in the currnet name.
strncat(arrOfNames[i], name, strlen(name) + 1); // Then, we save the name of the user into the string.
}
sortString(arrOfNames, numberOfFriends); // We use this function to sort the array.
for (i = 0; i < numberOfFriends; i++)
{
printf("Friend %d: %s\n", i + 1, arrOfNames[i]);
}
for (i = 0; i < numberOfFriends; i++)
{
free(arrOfNames[i]);
}
free(arrOfNames);
getchar();
return 0;
}
/*
Function will perform the fgets command and also remove the newline
that might be at the end of the string - a known issue with fgets.
input: the buffer to read into, the number of chars to read
*/
void myFgets(char str[], int n)
{
fgets(str, n, stdin);
str[strcspn(str, "\n")] = 0;
}
/*In this function we get array of strings and sort the array by abc.
Input: The array and the long.
Output: None*/
void sortString(char** arr, int numberOfStrings)
{
int i = 0, x = 0;
char tmp[STR_LEN] = { 0 };
for (i = 0; i < numberOfStrings; i++) // In this loop we run on all the indexes of the array. From the first string to the last.
{
for (x = i + 1; x < numberOfStrings; x++) // In this loop we run on the next indexes and check if is there smaller string than the currnet.
{
if (strcmp(arr[i], arr[x]) > 0) // If the original string is bigger than the currnet string.
{
strncat(tmp, arr[i], strlen(arr[i])); // Save the original string to temp string.
// Switch between the orginal to the smaller string.
arr[i][0] = '\0';
strncat(arr[i], arr[x], strlen(arr[x]));
arr[x][0] = '\0';
strncat(arr[x], tmp, strlen(tmp));
tmp[0] = '\0';
}
}
}
}
After the print of the names, when I want to free the names and the array, in the first try to free, I get an error of: "HEAP CORRUPTION DETECTED: after normal block(#87)". By the way, I get this error only when I enter 4 or more players. If I enter 3 or less players, the program work properly.
Why does that happen and what I should do to fix it?
First of all remove the unnecessary (and partly wrong) casts of the return value of malloc and realloc. In other words: replace (char*)malloc(... with malloc(..., and the same for realloc.
Then there is a big problem here: realloc(arrOfNames, sizeof(int) * sizeOfMemory) : you want to allocate an array of pointers not an array of int and the size of a pointer may or may not be the same as the size of an int. You need sizeof(char**) or rather the less error prone sizeof(*arrOfNames) here.
Furthermore this in too convoluted (but not actually wrong):
*(arrOfNames[i]) = '\0';
strncat(arrOfNames[i], name, strlen(name) + 1);
instead you can simply use this:
strcpy(arrOfNames[i], name);
Same thing in the sort function.
Keep your code simple.
But actually there are more problems in your sort function. You naively swap the contents of the strings (which by the way is inefficient), but the real problem is that if you copy a longer string, say "Walter" into a shorter one, say "Joe", you'll write beyond the end of the allocated memory for "Joe".
Instead of swapping the content of the strings just swap the pointers.
I suggest you take a pencil and a piece of paper and draw the pointers and the memory they point to.
My program parses code, and as it parses code I need to keep track of how many instances of each keywords it goes through every x number of lines (probably every 10 lines or so), from this information I plan on later creating a histogram with gnuplot but that's not important.
So there's 13 keywords, I can easily keep count of them with an array of 0s where each index represents a keyword and whenever I find a keyword I increase it's index by 1. Ok simple
int keywordcount[13]={0};
The thing is that I need to create a new keywordcount every 10 lines, and I don't know how many code lines the file has. So this tells me thatI should have a dynamic array of keywordcount arrays.
How do I declare this dynamic array of arrays in C and how do I add to it keywordcount arrays? I'm still confused by multidimensional arrays in C. I don't know if I should declare it as an array of pointers or what, and I don't know how I would go about assigning it a new keywordcount array which would not disappear when the function that creates keywordcount returns. Let me know if something isn't clear.
You can use malloc() or calloc() to create a dynamic array of static arrays. For example, this defines keywordcount_arr as a pointer to an array of 13 ints (here enough memory is allocated for max_lines such arrays):
size_t max_lines = 10;
int (*keywordcount_arr)[13] = calloc(max_lines, sizeof *keywordcount_arr);
It may make the code a little easier to read and write if a typedef is used here:
typedef int KW_Count[13];
/* ... */
KW_Count *keywordcount_arr = calloc(max_lines, sizeof *keywordcount_arr);
You can index into the allocated memory using 2d array indexing:
for (size_t i = 0; i < 13; i++) {
keywordcount_arr[0][i] = i;
}
Or, if an existing array must be stored in the dynamic array, memcpy() can be used. If the dynamic array needs to grow, realloc() can be used. And realloc() can be used again to trim the dynamic allocation to final size:
max_lines *= 2;
KW_Count *temp = realloc(keywordcount_arr,
sizeof *keywordcount_arr * max_lines);
Here is an example program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM_KEYWORDS 13
typedef int KW_Count[NUM_KEYWORDS];
int main(void)
{
size_t max_lines = 10;
size_t num_lines = 0;
KW_Count *keywordcount_arr = calloc(max_lines, sizeof *keywordcount_arr);
if (keywordcount_arr == NULL) {
perror("Allocation failure");
/* Handle error... perhaps: */
exit(EXIT_FAILURE);
}
/* Store keyword counts directly in keywordcount_arr[] */
++num_lines;
for (size_t i = 0; i < NUM_KEYWORDS; i++) {
keywordcount_arr[0][i] = i;
}
/* Or use memcpy() if an existing array must be stored */
++num_lines;
KW_Count keywordcount = { 0, 2, 0, 0, 3, 1, 0, 0, 0, 1, 2, 0, 1 };
memcpy(keywordcount_arr[1],
keywordcount,
sizeof *keywordcount * NUM_KEYWORDS);
/* Use realloc() to grow the dynamic array as needed */
max_lines *= 2;
KW_Count *temp = realloc(keywordcount_arr,
sizeof *keywordcount_arr * max_lines);
if (temp == NULL) {
perror("Unable to reallocate");
/* Handle error */
} else {
keywordcount_arr = temp;
}
/* Use realloc() to trim the dynamic array to final size */
temp = realloc(keywordcount_arr, sizeof *keywordcount_arr * num_lines);
if (temp == NULL) {
perror("Unable to reallocate");
/* Handle error */
} else {
keywordcount_arr = temp;
}
/* Display array contents */
for (size_t i = 0; i < num_lines; i++) {
for (size_t j = 0; j < NUM_KEYWORDS; j++) {
printf("%5d", keywordcount_arr[i][j]);
}
putchar('\n');
}
/* Cleanup */
free(keywordcount_arr);
return 0;
}
Program output:
0 1 2 3 4 5 6 7 8 9 10 11 12
0 2 0 0 3 1 0 0 0 1 2 0 1
Make an assumption of how many keywordcounts will be created. Let's say that you are almost sure that 10 of them will be created.
You can dynamically declare a 2D array, of 10 rows and 13 columns.
Now keywordcount[0] will be the index of the first keywordcount array (which has size 13), and so on.
Now, if in action you see that you need more than 10 keywordcount arrays, you could use realloc() to dynamically increase the size of your 2D array.
PS: A good tip would be to double the size of your 2D array in rows every time you need to increase its size (instead of increasing by one row every time, so that you can avoid rellocations, which can be harm performance in some cases).
Don't touch 2D arrays, arrays of arrays, arrays of pointers, pointers to arrays or any such nonsense.
Wrap your statically-sized array in a struct.
typedef struct
{
int keyword_count[13];
} fragment_info;
Have a dynamic array of fragment_info like you would create any other dynamic array:
fragment_info* infos = malloc(initial_capacity * sizeof(*infos));
....
fragment_info* new_infos = realloc(infos, new_capacity * sizeof(*new_infos));
Now if you want to store additional information about your 10-line fragments, you have a natural place to keep it. Just add more fields to the struct.
● int vectorInsert(Vector * array, int index, Data value);
I am doing
If this can be corrected according to the given statement.
I am calling it using
Vector *vect = initVector();
Data data_array[20];
for(i = 0 ; i < 20 ; i++){
data_array[i].value = (rand() % 20) + 1;
vectorInsert(vect, i, data_array[i]);
}
There are a couple of errors in your code, but the most important one is in your initVector function, you actually need to allocate memory for the vector.
You also need to do the following things:
in initVector return v instead of v->data or &v
in vectorInsert print array->data[index].value instead of array->data[index]
in vectorInsert return 1 on success, add error checking in your allocation and return 0 on memory error.
All of these except the original malloc were warnings returned by the compiler.
First, according to your specifications, max_size should be an unsigned integer, so I changed Vector to reflect this, using size_t. I also changed the related format specifiers from %d to %zu to match this new type.
Your initVector() function needed to allocate memory for a Vector, so that has been added. Furthermore, there was no need to allocate memory for the dynamic array of Data structs here, so v->data is set to NULL. This function should also return the pointer to the newly allocated memory, v, instead of a pointer to the .data field of this Vector, as you originally had.
In the vectorInsert() function, you neglected to check for memory allocation errors, so I added a check after the attempted allocation which returns 0 if there is an error. After inserting the new Data struct, your check to increment .current_size is wrong. First, you need to increment if array->current_size <= index. Next, you need to add one to the .current_size, not set .current_size to one larger than the index value. Also, when printing the inserted value here, you forgot to access the .value field. I think that this may have been due to the confusing name that you used for the Data struct that you passed into vectorInsert(). You call this struct value, so in the previous line we have array->data[index] = value, where you are assigning the struct value to array->data[index]. But in the call to printf() you want to show the value held by the struct value. Choosing better names is always a win! Finally, this function returns 1 on a successful insertion.
I added to your test code to display the contents of vect and vect->data, and also added a Data struct, test_insert, to test insertion into an arbitrary index.
Finally, you need to free memory allocations after all of this, so I added a couple of calls to free().
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
int value;
}Data;
/* Specs say that max_size should be an unsigned integer */
typedef struct{
size_t max_size; //initialize to 0
size_t current_size; //initialize to 0
Data *data; // array of integers we're storing
} Vector;
/* Modified function */
Vector * initVector(void){
Vector *v = malloc(sizeof(*v));
v->max_size=0;
v->current_size=0;
v->data = NULL;
return v;
}
int vectorInsert(Vector * array, size_t index, Data value)
{
if(index >= array->max_size)
{
array->max_size = index * 2 + 1;
printf("Inside Vect max_size is : %zu\n", array->max_size);
Data *new_array = malloc(sizeof(Data) * array->max_size);
/* Added check for allocation error */
if (new_array == NULL)
return 0;
if(array->data != NULL)
{
memcpy(new_array, array->data, sizeof(Data)*array->current_size);
free(array->data);
array->data = NULL;
}
array->data = new_array;
}
array->data[index] = value;
printf("Main : %d\n", array->data[index].value);
/* Modified current_size increment logic */
if(array->current_size <= index)
{
array->current_size += 1;
}
/* Successful insertion */
return 1;
}
int main(void)
{
size_t i;
Vector *vect = initVector();
Data data_array[20];
Data test_insert = { -5 }; // to test index insertion
for(i = 0 ; i < 20 ; i++){
data_array[i].value = (rand() % 20) + 1;
vectorInsert(vect, i, data_array[i]);
}
/* Display results */
printf("vect->max_size = %zu\n", vect->max_size);
printf("vect->current_size = %zu\n", vect->current_size);
printf("vect->data contains:\n");
for (i = 0; i < vect->current_size; i++)
printf("%d ", vect->data[i].value);
putchar('\n');
/* Insert test_insert at index 5 */
vectorInsert(vect, 5, test_insert);
/* Display results */
printf("vect->max_size = %zu\n", vect->max_size);
printf("vect->current_size = %zu\n", vect->current_size);
printf("vect->data contains:\n");
for (i = 0; i < vect->current_size; i++)
printf("%d ", vect->data[i].value);
putchar('\n');
/* Free memory allocations */
free(vect->data);
free(vect);
return 0;
}
And here is a sample of the results:
vect->max_size = 31
vect->current_size = 20
vect->data contains:
4 7 18 16 14 16 7 13 10 2 3 8 11 20 4 7 1 7 13 17
vect->max_size = 31
vect->current_size = 20
vect->data contains:
4 7 18 16 14 -5 7 13 10 2 3 8 11 20 4 7 1 7 13 17
Enable all warnings and debug info in your compiler (e.g. compile with gcc -Wall -g). Then it should warn you about
Vector * initVector(){
Vector *v; /// UNINITALIZED
v->max_size=0;
v->current_size=0;
v->data = malloc(sizeof(int)*v->max_size);
return v->data;
// return (&v);
}
So you have an undefined behavior, and that is awfully bad.
(Of course the compiler will give a lot of other warnings, and you should improve your code till you got no warnings at all. Then you should use the gdb debugger)
You might want to read about flexible array members.
Consider perhaps having at least:
Vector* createVector(int maxsize) {
if (maxsize<=0)
{ fprintf(stderr, "bad maxsize=%d\n", maxsize); exit(EXIT_FAILURE); };
Vector* v = malloc(sizeof(Vector));
if (!v) { perror("malloc Vector"); exit(EXIT_FAILURE); };
v->data = malloc(sizeof(Data)*maxsize);
if (!v->data) { perror("malloc data"); exit(EXIT_FAILURE); };
v->max_size = maxsize;
v->current_size = 0;
memset(v->data, 0, sizeof(Data)*maxsize);
return v;
}
I’m wondering if anyone could provide any advice on storing “class slices” of values? I.e., for a table of values (see below for example), I want to be able to store, for each row, an array of the values that corresponds to each class. I have already sorted the table by class and determined the number of classes and the number of elements in each class. I’ve written out the basic conditions that I think will let me iterate over each row, over each class with the goal of copying the values from one struct into the new “class slice arrays”:
int x = 0;
for(int i = 0; i < total_rows - 1; i++)
{
for (int j = 0; j < n_classes; j++)
{
for (int k = 0; k < class_size[j]; k++)
{
data[x].value[i] = // value at the xth position of the “class slice” array for the jth class of values in the ith row //
x++;
}
}
}
where data[x].value[0] is the value I have stored for the xth sample in row i.
I guess my question is - how would you start mallocing space for arrays to store these class slices if you don’t know how many you’re even going to need (since that’s dependent on the number of rows and the number of classes)?
Example input, if this helps to make it clearer:
Class Case Case Case Case Case Case Case Case Case Case Case Case Case Case Control Control Control Control Control Control Control Control Control Control Control Control Control Control Control Control
Obs1 0.000741628 0.00308607 0.000267431 0.001418697 0.001237904 0.000761145 0.0008281 0.002426075 0.000236698 0.004924871 0.000722752 0.003758006 0.000104813 0.000986619 0.000121803 0.000666854 0 0.000171394 0.000877993 0.002717391 0.001336501 0.000812089 0.001448743 5.28E-05 0.001944298 0.000292529 0.000469631 0.001674047 0.000651526 0.000336615
Obs2 0.102002396 0.108035127 0.015052531 0.079923731 0.020643362 0.086480609 0.017907667 0.016279315 0.076263965 0.034876124 0.187481931 0.090615572 0.037460171 0.143326961 0.029628502 0.049487575 0.020175439 0.122975405 0.019754837 0.006702899 0.014033264 0.040024363 0.076610375 0.069287599 0.098896479 0.011813681 0.293331246 0.037558052 0.303052867 0.137591517
Obs3 0.218495065 0.242891829 0.23747851 0.101306336 0.309040188 0.237477347 0.293837554 0.34351816 0.217572429 0.168651691 0.179387106 0.166516699 0.099970652 0.181003474 0.076126675 0.10244981 0.449561404 0.139257863 0.127579104 0.355797101 0.354544105 0.262855651 0.10167146 0.186068602 0.316763006 0.187466247 0.05701315 0.123825467 0.064780343 0.069847682
Obs4 0.141137543 0.090948286 0.102502388 0.013063365 0.162060849 0.166292135 0.070215996 0.063535037 0.333743609 0.131011609 0.140936687 0.150108506 0.07812762 0.230704405 0.069792935 0.120770743 0.164473684 0.448110378 0.42599534 0.074094203 0.096525097 0.157661185 0.036737518 0.213931398 0.091119285 0.438073807 0.224921728 0.187034237 0.06611442 0.086005218
Obs5 0.003594044 0.003948354 0.008137536 0.001327901 0.002161974 0.003552012 0.002760334 0.001898667 0.001420186 0.003165988 0.001011853 0.001217382 0.000314439 0.004254794 0.000213155 0.003650147 0 0.002742309 0.002633978 0 0.002524503 0.002146234 0.001751465 0.006543536 0.003941146 0.00049505 0.00435191 0.001944054 0.001303053 0.004207692
Obs6 0.000285242 2.27E-05 0 1.13E-05 0.0002964 3.62E-05 0.000138017 0.000210963 0.000662753 0 0 0 0 4.11E-05 0 0 0 0 0.000101307 0 0 0 0 5.28E-05 0.00152391 0 0 0 0 0
Obs7 0.002624223 0.001134584 0.00095511 0.000419934 0.000401011 0.001739761 0.00272583 0.002566717 0.000520735 0.002311674 0.006287944 0 6.29E-05 0.000143882 3.05E-05 0.000491366 0 0 3.38E-05 0 0.001782002 0.000957104 0.002594763 0.000527704 0.000105097 0.001192619 3.13E-05 0 0.000744602 0.000252461
Obs8 0.392777683 0.383875286 0.451499522 0.684663315 0.387394299 0.357992026 0.488406597 0.423473155 0.27267563 0.47454646 0.331020526 0.484041709 0.735955056 0.338841956 0.781699147 0.625403622 0.313596491 0.270545891 0.379259109 0.498913043 0.372438372 0.446271644 0.606698813 0.305593668 0.360535996 0.29889739 0.328710081 0.521222594 0.419924299 0.584111756
I think what you are looking for is a struct containing a pointer to double and an int that keeps track of the number of values being stored:
struct data {
int numValues;
double *value;
};
If you don't know how many data structs you need, you declare a pointer to one, and allocate them as needed. Whenever you allocate a new data struct, you set the value to point to NULL. As you add values, you reallocate storage, reset the value pointer, and update the counter for that struct. Here is a little toy example to illustrate how it works:
#include <stdio.h>
#include <stdlib.h>
struct data {
int numValues;
double *value;
};
int main(void)
{
struct data *myData;
int numData = 0;
int i, last;
myData = NULL;
++numData;
i = numData - 1;
myData = realloc(myData, sizeof(*myData) * numData);
myData[i].numValues = 0;
myData[i].value = NULL;
last = myData[i].numValues;
myData[i].value = realloc(myData[i].value, sizeof(double) * (last + 1));
myData[i].numValues += 1;
myData[i].value[last] = 3.1415926536;
last = myData[i].numValues;
myData[i].value = realloc(myData[i].value, sizeof(double) * (last + 1));
myData[i].numValues += 1;
myData[i].value[last] = 2.7182818285;
printf("Value [0][0]: %lf\n", myData[i].value[0]);
printf("Value [0][1]: %lf\n", myData[i].value[1]);
++numData;
i = numData - 1;
myData = realloc(myData, sizeof(*myData) * numData);
myData[i].numValues = 0;
myData[i].value = NULL;
last = myData[i].numValues;
myData[i].value = realloc(myData[i].value, sizeof(double) * (last + 1));
myData[i].numValues += 1;
myData[i].value[last] = 1.6180340;
last = myData[i].numValues;
myData[i].value = realloc(myData[i].value, sizeof(double) * (last + 1));
myData[i].numValues += 1;
myData[i].value[last] = 2.99792458;
printf("Value [1][0]: %lf\n", myData[i].value[0]);
printf("Value [1][1]: %lf\n", myData[i].value[1]);
/* Let's free all of the memory we allocated! */
// first, free space allocated for values
for (int j = 0; j <= i; j++)
free(myData[j].value);
// next, free space allocated for structs
free(myData);
return 0;
}
Edit
I cleaned up the code a bit and stored a couple of numbers in a second struct in an attempt to make the example a little more illustrative. I also added a few lines to free the allocated memory.
I am not exactly sure what your data is representing, but it might be useful to add a second dynamic array to the struct:
struct data {
int numCase;
int numControl;
double *caseValue;
double *controlValue;
};
Some variation on this idea would allow you to keep your "case" and "control" values separate, and you could manage the addition of new values as before.
I have some very very basic idea what a struct is and how it works.
My goal is to create an attendance-program using structure:
input: worker, time he came to work, time he left - he can leave and come multiple times (lunch break for example)
output: how many hours did each the longest worker work and who was he.
For example:
+ 8:00:00 100
+ 8:50:00 105
- 9:30:00 100
- 18:20:00 105
- 19:00:00 100
- 17:00:00 100
+ 18:00:00 100
+ 9:00:00 200
+ 10:00:00 100
- 15:00:00 200
Output:
Longest work time: 9:30:00
Longest workers: 100, 105
My original idea was to use struct this way:
typedef struct work {
int *arrivals;
int *departures;
int *result;
} WORK;
WORK 66, 100, 105, 200;
Then I would dynamically allocate memories for arrivals and departures, sort them and compare elements of arrival[i] and departure[i].
The problem with this is that the number of elements (workers) may be varying and you cannot dynamically add new elements into structures.
So how can I change the structure to suit my needs?
If the number of workers is not known ahead of time, but once known will be fixed, dynamically allocate an array of WORKs. This would be done in exactly the same way you are doing arrivals, except you'd use you work struct instead of the timestruct.
If the number will change over time, you can either use realloc to do the above but change its size when needed, or use a more dynamic structure (like a linked list). realloc is similar to malloc, except that it takes a pointer & a new size, and modifies the thing it points to (possibly moving it) to make it the new size. For a linked list, you need to (at least) add a field to your struct which is a pointer to that same kind of struct, which is used to tell you where the next item in the list is.
As you can see, there are a number of ways to approach this problem. The primary consideration boils down to How do I want to handle my struct?, an array of structs? or an array of pointers to struct?. Both have advantages and disadvantages.
The biggest disadvantage of choosing an array of structs is you will bear the burden of keeping track of all of the indexes associated with the array, as well as the time array within each struct. Choosing an array of pointers to struct helps a bit by allowing you to initialize a block of pointers to NULL. As you add a worker, instead of NULL, you have an address in the pointer. This allows you to iterate through your array of pointers with a simple while (!NULL) {do stuff...}
The second little twist comes in using calloc instead of malloc. By using calloc, you initialize the memory allocated to zero/NULL. This can apply equally to an array of structs or an array of pointers to struct. This provides the ability to use the while (!NULL) {do stuff...} iteration. (the indexing is a bit more involved with an array of structs)
The remainder of the code should be fairly readable and straight forward. Here, we define an INITWKR value of 25 to allocate in initial block of pointers to struct. The struct is defined with several additional members useful in your worker with the most time calculations. In fact, aside from keeping track of the time in/time out, there is no reason to store an array of values within each worker's struct. In the code, while the (minutes worked for each shift) is stored in the time array in each struct, the actual date/time code is omitted. That would just clutter the logic for purposes of the example.
As each new worker is entered, a new struct is allocated for him and the pointer value is stored in the array of pointers to struct. The input loop checks whether that name already exists (has a struct), if so, then additional time is added and no new struct allocation is needed. During each input iteration, you will need to check whether you have exhausted your allocated pointers or allocated time array and realloc as needed. I have placed comments in the code and included the necessary test variables, but omitted the reallocation code to prevent cluttering the worker/time logic.
When the input loop is completed (just hit [enter] instead of providing input), all worker times worked are printed and the worker with the most time is shown. The program then takes care to free all dynamically allocated memory and then exits.
Take a look, the code is commented fairly well. If you get stuck, just drop a comment.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INITWKR 25
typedef struct {
char *name; /* worker name */
int *time; /* time array (min) */ /* optional */
unsigned entries; /* entries in time */
unsigned size; /* allocated size */ /* realloc test */
unsigned total; /* total time (min) */
} worker;
int main () {
char *name = NULL; /* name input */
int tio = 0; /* time input */
int idx = 0; /* workers index */
int idxmax = 0; /* index max time */
int tdx = 0; /* time index */
int tmax = 0; /* max worker time */
char exist = 0; /* wkr exists flag */
int wkrsz = INITWKR;/* num of wkr ptrs */
/* create INITWKR pointers to struct (initialize NULL) */
worker **wkrs = calloc (INITWKR, sizeof (*wkrs));
printf ("\nPunch the clock, enter name & minutes worked ([enter] alone to end)\n");
/* input loop, read name and minutes worked for each shift */
while (printf ("\n name: ") && scanf ("%m[^\n]%*c", &name) >= 1 &&
printf (" time: ") && scanf ("%d%*c", &tio) >= 1 )
{
idx = 0; /* reset loop vars */
exist = 0;
while (wkrs[idx]) /* check each filled worker */
{
/* check if already has struct */
if (strcmp (wkrs[idx]->name, name) == 0) {
exist = 1;
break;
}
idx++; /* idx points to first avail pointer at end */
}
/* if (idx >= INITWKR - 1) reallocate poiner array */
if (!exist) {
/* add new worker */
wkrs[idx] = malloc (sizeof (**wkrs));
wkrs[idx]-> name = strdup (name);
wkrs[idx]-> time = calloc (INITWKR, sizeof (int));
wkrs[idx]-> entries = 0;
wkrs[idx]-> size = INITWKR;
wkrs[idx]-> total = 0;
}
/* add time to worker */
tdx = 0;
while ((wkrs[idx]-> time)[tdx])
tdx++;
/* if (tdx >= wkrs[idx]-> size - 1) reallocate wkrs[idx]-> time, increment size */
(wkrs[idx]-> time)[tdx] = tio;
wkrs[idx]-> entries++;
wkrs[idx]-> total += tio;
if (wkrs[idx]-> total > tmax) {
tmax = wkrs[idx]-> total;
idxmax = idx;
}
}
if (name) free (name); /* free memory allocated by scanf */
printf ("\nWorker Time Summary:\n\n");
idx = 0;
while (wkrs[idx]) { /* output worker name/time & max */
if (idx == idxmax)
printf (" Worker[%2d] : %-24s time: %d (max time)\n", idx, wkrs[idx]->name, wkrs[idx]->total);
else
printf (" Worker[%2d] : %-24s time: %d\n", idx, wkrs[idx]->name, wkrs[idx]->total);
idx++;
}
printf ("\n");
idx = 0;
while (wkrs[idx]) { /* free dynamically allocated mem */
if (wkrs[idx]->name) free (wkrs[idx]->name);
if (wkrs[idx]->time) free (wkrs[idx]->time);
if (wkrs[idx]) free (wkrs[idx++]);
}
return 0;
}
example:
$ ./bin/workers
Punch the clock, enter name & minutes worked ([enter] alone to end)
name: JD Clark
time: 38
name: Mike Wu
time: 34
name: JD Clark
time: 39
name: Mike Wu
time: 53
name: JD Clark
time: 64
name: Tim Taylor
time: 55
name:
Worker Time Summary:
Worker[ 0] : JD Clark time: 141 (max time)
Worker[ 1] : Mike Wu time: 87
Worker[ 2] : Tim Taylor time: 55
I wrote you a little code to show you how would this work. Idea is to have dynamic arrays for both workers and arrivals/departures.
If you know how to dynamically allocate a memory for some array a for example:
int *a;
a = (int*) malloc(100*sizeof(int));
Then you know how to do it with structs:
struct s_name *my_s; // s_name is some random name of structure
my_s = (s_name*) malloc(100*sizeof(s_name));
And therefore you know how to allocate memory for some array inside a structure.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int h, m, s; // hour, minutes, seconds
} time;
typedef struct {
time *arrival;
time *departure;
time result;
int n_of_arrivals; // to keep track how many we have
int n_of_departures;
} work;
work *workers;
int main(void) {
int N;
printf("How many entries do you have?\n> ");
scanf("%d", &N);
// initially create space for 100 workers
workers = (work*) malloc(100*sizeof(work));
int number_of_workers = 100;
for (int n = 0; n < N; n++) {
char event; // this is '+' or '-'
int h, m, s, id;
scanf(" %c %d:%d:%d%d", &event, &h, &m, &s, &id);
// last position where we can put our worker is number_of_workers - 1
if (id > number_of_workers - 1) {
number_of_workers = id + 1;
// realloc memory for storing workers
workers = (work*) realloc(workers, number_of_workers*sizeof(work));
}
if (event == '+') {
// add 1 to amount of arrivals
workers[id].n_of_arrivals += 1;
int len = workers[id].n_of_arrivals;
// realloc memory for keeping arrivals of worker
workers[id].arrival = (time*) realloc(workers[id].arrival, len*sizeof(time));
// finally store new time
time *arrival = workers[id].arrival;
arrival[len - 1].h = h;
arrival[len - 1].m = m;
arrival[len - 1].s = s;
} else {
// this is the same as for adding arrivals
workers[id].n_of_departures += 1;
int len = workers[id].n_of_departures;
workers[id].departure = (time*) realloc(workers[id].departure, len*sizeof(time));
time *departure = workers[id].departure;
departure[len - 1].h = h;
departure[len - 1].m = m;
departure[len - 1].s = s;
}
}
for (int i = 0; i < number_of_workers; i++) {
// skip worker who doesn't have track of any arrivalls or depratures
if (!workers[i].n_of_arrivals && !workers[i].n_of_departures)
continue;
printf("Worker %d:\n", i);
// print nicely arrivals
if (workers[i].n_of_arrivals) {
int len = workers[i].n_of_arrivals;
printf("Arrivals:\n");
for (int k = 0; k < len; k++)
printf("%d:%d:%d\n", workers[i].arrival[k].h, workers[i].arrival[k].m, workers[i].arrival[k].s);
}
// // print nicely depratures
if(workers[i].n_of_departures) {
int len = workers[i].n_of_departures;
printf("Departures:\n");
for (int k = 0; k < len; k++)
printf("%d:%d:%d\n", workers[i].departure[k].h, workers[i].departure[k].m, workers[i].departure[k].s);
}
printf("\n");
}
return 0;
}
I didn't wrote an algorithm for calculating the result. I will leave that to you. :)