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. :)
Related
My goal here is to perform MergeSort on a dynamic array-like data structure I called a dictionary used to store strings and their relative weights. Sorry if the implementation is dumb, I'm a student and still learning.
Anyway, based on the segfaults I'm getting, I'm incorrectly allocating memory for my structs of type item to be copied over into the temporary lists I'm making. Not sure how to fix this. Code for mergesort and data structure setup is below, any help is appreciated.
/////// DICTIONARY METHODS ////////
typedef struct {
char *item;
int weight;
} item;
typedef struct {
item **wordlist;
//track size of dictionary
int size;
} dict;
//dict constructor
dict* Dict(int count){
//allocate space for dictionary
dict* D = malloc(sizeof(dict));
//allocate space for words
D->wordlist = malloc(sizeof(item*) * count);
//initial size
D->size = 0;
return D;
}
//word constructor
item* Item(char str[]){
//allocate memory for struct
item* W = malloc(sizeof(item));
//allocate memory for string
W->item = malloc(sizeof(char) * strlen(str));
W->weight = 0;
return W;
}
void merge(dict* D, int start, int middle, int stop){
//create ints to track lengths of left and right of array
int leftlen = middle - start + 1;
int rightlen = stop - middle;
//create new temporary dicts to store the two sides of the array
dict* L = Dict(leftlen);
dict* R = Dict(rightlen);
int i, j, k;
//copy elements start through middle into left dict- this gives a segfault
for (int i = 0; i < leftlen; i++){
L->wordlist[i] = malloc(sizeof(item*));
L->wordlist[i] = D->wordlist[start + i];
}
//copy elements middle through end into right dict- this gives a segfault
for (int j = 0; j < rightlen; j++){
R->wordlist[j] = malloc(sizeof(item*));
R->wordlist[j]= D->wordlist[middle + 1 + k];
}
i = 0;
j = 0;
k = leftlen;
while ((i < leftlen) && (j < rightlen)){
if (strcmp(L->wordlist[i]->item, R->wordlist[j]->item) <= 0) {
D->wordlist[k] = L->wordlist[i];
i++;
k++;
}
else{
D->wordlist[k] = R->wordlist[j];
j++;
k++;
}
}
while (i < leftlen){
D->wordlist[k] = L->wordlist[i];
i++;
k++;
}
while (j < rightlen){
D->wordlist[k] = L->wordlist[j];
j++;
k++;
}
}
void mergeSort(dict* D, int start, int stop){
if (start < stop) {
int middle = start + (stop - start) / 2;
mergeSort(D, start, middle);
mergeSort(D, middle + 1, stop);
merge(D, start, middle, stop);
}
I put print statements everywhere and narrowed it down to the mallocs in the section where I copy the dictionary to be sorted into 2 separate dictionaries. Also tried writing that malloc as malloc(sizeof(D->wordlist[start + i])). Is there something else I need to do to be able to copy the item struct into the wordlist of the new struct?
Again, I'm new to this, so cut me some slack :)
There are numerous errors in the code:
In merge() when copying elements to the R list, the wrong (and uninitialized) index variable k is being used instead of j. R->wordlist[j]= D->wordlist[middle + 1 + k]; should be R->wordlist[j]= D->wordlist[middle + 1 + j];.
In merge() before merging the L and R lists back to D, the index variable k for the D list is being initialized to the wrong value. k = leftLen; should be k = start;.
In merge() in the loop that should copy the remaining elements of the "right" list to D, the elements are being copied from the "left" list instead of the "right" list. D->wordlist[k] = L->wordlist[j]; should be D->wordlist[k] = R->wordlist[j];.
In Item(), the malloc() call is not reserving space for the null terminator at the end of the string. W->item = malloc(sizeof(char) * strlen(str)); should be W->item = malloc(sizeof(char) * (strlen(str) + 1)); (and since sizeof(char) is 1 by definition it can be simplified to W->item = malloc(strlen(str) + 1);).
Item() is not copying the string to the allocated memory. Add strcpy(W->item, str);.
There are memory leaks in merge():
L->wordlist[i] = malloc(sizeof(item*)); is not required and can be removed since L->wordlist[i] is changed on the very next line: L->wordlist[i] = D->wordlist[start + i];.
Similarly, R->wordlist[j] = malloc(sizeof(item*)); is not required and can be removed since R->wordlist[j] is changed on the very next line.
L and R memory is created but never destroyed. Add these lines to the end of merge() to free them:
free(L->wordlist);
free(L);
free(R->wordlist);
free(R);
None of the malloc() calls are checked for success.
Allocate it all at once, before the merge sort even starts.
#include <stdlib.h>
#include <string.h>
// Weighted Word --------------------------------------------------------------
//
typedef struct {
char *word;
int weight;
} weighted_word;
// Create a weighted word
//
weighted_word* CreateWeightedWord(const char *str, int weight){
weighted_word* W = malloc(sizeof(weighted_word));
if (W){
W->word = malloc(strlen(str) + 1); // string length + nul terminator
if (W->word)
strcpy( W->word, str);
W->weight = weight;
}
return W;
}
// Free a weighted word
//
weighted_word *FreeWeightedWord(weighted_word *W){
if (W){
if (W->word)
free(W->word);
free(W);
}
return NULL;
}
// Dictionary (of Weighted Words) ---------------------------------------------
//
typedef struct {
weighted_word **wordlist; // this is a pointer to an array of (weighted_word *)s
int size; // current number of elements in use
int capacity; // maximum number of elements available to use
} dict;
// Create a dictionary with a fixed capacity
//
dict* CreateDict(int capacity){
dict* D = malloc(sizeof(dict));
if (D){
D->wordlist = malloc(sizeof(weighted_word*) * capacity);
D->size = 0;
D->capacity = capacity;
}
return D;
}
// Free a dictionary (and all weighted words)
//
dict *FreeDict(dict *D){
if (D){
for (int n = 0; n < D->size; n++)
FreeWeightedWord(D->wordlist[n]);
free(D->wordlist);
free(D);
}
return NULL;
}
// Add a new weighted word to the end of our dictionary
//
void DictAddWord(dict *D, const char *str, int weight){
if (!D) return;
if (D->size == D->capacity) return;
D->wordlist[D->size] = CreateWeightedWord(str, weight);
if (D->wordlist[D->size])
D->size += 1;
}
// Merge Sort the Dictionary --------------------------------------------------
// Merge two partitions of sorted words
// words • the partitioned weighted word list
// start • beginning of left partition
// middle • end of left partition, beginning of right partition
// stop • end of right partition
// buffer • temporary work buffer, at least as big as (middle-start)
//
void MergeWeightedWords(weighted_word **words, int start, int middle, int stop, weighted_word **buffer){
int Lstart = start; int Rstart = middle; // Left partition
int Lstop = middle; int Rstop = stop; // Right partition
int Bindex = 0; // temporary work buffer output index
// while (left partition has elements) AND (right partition has elements)
while ((Lstart < Lstop) && (Rstart < Rstop)){
if (strcmp( words[Rstart]->word, words[Lstart]->word ) < 0)
buffer[Bindex++] = words[Rstart++];
else
buffer[Bindex++] = words[Lstart++];
}
// if (left partition has any remaining elements)
while (Lstart < Lstop)
buffer[Bindex++] = words[Lstart++];
// We don't actually need this. Think about it. Why not?
// // if (right partition has any remaining elements)
// while (Rstart < Rstop)
// buffer[Bindex++] = words[Rstart++];
// Copy merged data from temporary buffer back into source word list
for (int n = 0; n < Bindex; n++)
words[start++] = buffer[n];
}
// Merge Sort an array of weighted words
// words • the array of (weighted_word*)s to sort
// start • index of first element to sort
// stop • index ONE PAST the last element to sort
// buffer • the temporary merge buffer, at least as big as (stop-start+1)/2
//
void MergeSortWeightedWords(weighted_word **words, int start, int stop, weighted_word **buffer){
if (start < stop-1){ // -1 because a singleton array is by definition sorted
int middle = start + (stop - start) / 2;
MergeSortWeightedWords(words, start, middle, buffer);
MergeSortWeightedWords(words, middle, stop, buffer);
MergeWeightedWords(words, start, middle, stop, buffer);
}
}
// Merge Sort a Dictionary
//
void MergeSortDict(dict *D){
if (D){
// We only need to allocate a single temporary work buffer, just once, right here.
dict * Temp = CreateDict(D->size);
if (Temp){
MergeSortWeightedWords(D->wordlist, 0, D->size, Temp->wordlist);
}
FreeDict(Temp);
}
}
// Main program ---------------------------------------------------------------
#include <stdio.h>
int main(int argc, char **argv){
// Command-line arguments --> dictionary
dict *a_dict = CreateDict(argc-1);
for (int n = 1; n < argc; n++)
DictAddWord(a_dict, argv[n], 0);
// Sort the dictionary
MergeSortDict(a_dict);
// Print the weighted words
for (int n = 0; n < a_dict->size; n++)
printf( "%d %s\n", a_dict->wordlist[n]->weight, a_dict->wordlist[n]->word );
// Clean up
FreeDict(a_dict);
}
Notes for you:
Be consistent. You were inconsistent with capitalization and * placement and, oddly, vertical spacing. (You are waaay better than most beginners, though.) I personally hate the Egyptian brace style, but to each his own.
I personally think there are far too many levels of malloc()s in this code too, but I will leave it at this one comment. It works as is.
Strings must be nul-terminated — that is, each string takes strlen() characters plus one for a '\0' character. There is a convenient library function that can copy a string for you too, called strdup(), which AFAIK exists on every system.
Always check that malloc() and friends succeed.
Don’t forget to free everything you allocate. Functions help.
“Item” was a terribly non-descript name, and it overlapped with the meaning of two different things in your code. I renamed them to separate things.
Your dictionary object should be expected to keep track of how many elements it can support. The above code simply refuses to add words after the capacity is filled, but you could easily make it realloc() a larger capacity if the need arises. The point is to prevent invalid array accesses by adding too many elements to a fixed-size array.
Printing the array could probably go in a function.
Notice how I set start as inclusive and stop as exclusive. This is a very C (and C++) way of looking at things, and it is a good one. It will help you with all kinds of algorithms.
Notice also how I split the Merge Sort up into two functions: one that takes a dictionary as argument, and a lower-level one that takes an array of the weighted words as argument that does all the work.
The higher-level merge sort a dictionary allocates all the temporary buffer the merge algorithm needs, just once.
The lower-level merge sort an array of (weighted_word*)s expects that temporary buffer to exist and doesn’t care (or know anything) about the dictionary object.
The merge algorithm likewise doesn't know much. It is simply given all the information it needs.
Right now the merge condition simply compares the weighted-word’s string value. But it doesn’t have to be so simple. For example, you could sort equal elements by weight. Create a function:
int CompareWeightedWords(const weighted_word *a, const weighted_word *b){
int rel = strcmp( a->word, b->word );
if (rel < 0) return -1;
if (rel > 0) return 1;
return a->weight < b->weight ? -1 : a->weight > b->weight;
}
And put it to use in the merge function:
if (CompareWeightedWords( words[Rstart], words[Lstart] ) < 0)
buffer[Bindex++] = words[Rstart++];
else
buffer[Bindex++] = words[Lstart++];
I don’t think I forgot anything.
How am I supposed to use dynamic memory allocations for arrays?
For example here is the following array in which i read individual words from a .txt file and save them word by word in the array:
Code:
char words[1000][15];
Here 1000 defines the number of words the array can save and each word may comprise of not more than 15 characters.
Now I want that that program should dynamically allocate the memory for the number of words it counts. For example, a .txt file may contain words greater that 1000. Now I want that the program should count the number of words and allocate the memory accordingly.
Since we cannot use a variable in place of [1000], I am completely blank at how to implement my logic. Please help me in this regard.
You use pointers.
Specifically, you use a pointer to an address, and using a standard c library function calls, you ask the operating system to expand the heap to allow you to store what you need to.
Now, it might refuse, which you will need to handle.
The next question becomes - how do you ask for a 2D array? Well, you ask for an array of pointers, and then expand each pointer.
As an example, consider this:
int i = 0;
char** words;
words = malloc((num_words)*sizeof(char*));
if ( words == NULL )
{
/* we have a problem */
printf("Error: out of memory.\n");
return;
}
for ( i=0; i<num_words; i++ )
{
words[i] = malloc((word_size+1)*sizeof(char));
if ( words[i] == NULL )
{
/* problem */
break;
}
}
if ( i != num_words )
{
/* it didn't allocate */
}
This gets you a two-dimensional array, where each element words[i] can have a different size, determinable at run time, just as the number of words is.
You will need to free() all of the resultant memory by looping over the array when you're done with it:
for ( i = 0; i < num_words; i++ )
{
free(words[i]);
}
free(words);
If you don't, you'll create a memory leak.
You could also use calloc. The difference is in calling convention and effect - calloc initialises all the memory to 0 whereas malloc does not.
If you need to resize at runtime, use realloc.
Malloc
Calloc
Realloc
Free
Also, important, watch out for the word_size+1 that I have used. Strings in C are zero-terminated and this takes an extra character which you need to account for. To ensure I remember this, I usually set the size of the variable word_size to whatever the size of the word should be (the length of the string as I expect) and explicitly leave the +1 in the malloc for the zero. Then I know that the allocated buffer can take a string of word_size characters. Not doing this is also fine - I just do it because I like to explicitly account for the zero in an obvious way.
There is also a downside to this approach - I've explicitly seen this as a shipped bug recently. Notice I wrote (word_size+1)*sizeof(type) - imagine however that I had written word_size*sizeof(type)+1. For sizeof(type)=1 these are the same thing but Windows uses wchar_t very frequently - and in this case you'll reserve one byte for your last zero rather than two - and they are zero-terminated elements of type type, not single zero bytes. This means you'll overrun on read and write.
Addendum: do it whichever way you like, just watch out for those zero terminators if you're going to pass the buffer to something that relies on them.
While Ninefingers provided an answer using an array of pointers , you can also use an array of arrays as long as the inner array's size is a constant expression. The code for this is simpler.
char (*words)[15]; // 'words' is pointer to char[15]
words = malloc (num_words * sizeof(char[15]);
// to access character i of word w
words[w][i];
free(words);
If you're working in C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define WORD_LEN 15
int resizeArray(char (**wordList)[WORD_LEN], size_t *currentSize, size_t extent)
{
int result = 1;
char (*tmp)[WORD_LEN] = realloc(*wordList,
(*currentSize + extent) * sizeof **wordList);
if (tmp)
{
*currentSize += extent;
*wordList = tmp;
}
else
result = 0;
return result;
}
int main(void)
{
char *data[] = {"This", "is", "a", "test",
"of", "the", "Emergency",
"Broadcast", "System", NULL};
size_t i = 0, j;
char (*words)[WORD_LEN] = NULL;
size_t currentSize = 0;
for (i = 0; data[i] != NULL; i++)
{
if (currentSize <= i)
{
if (!resizeArray(&words, ¤tSize, 5))
{
fprintf(stderr, "Could not resize words\n");
break;
}
}
strcpy(words[i], data[i]);
}
printf("current array size: %lu\n", (unsigned long) currentSize);
printf("copied %lu words\n", (unsigned long) i);
for (j = 0; j < i; j++)
{
printf("wordlist[%lu] = \"%s\"\n", (unsigned long) j, words[j]);
}
free(words);
return 0;
}
If you intend to go for C++, STL is very useful for something dynamic allocation and is very easy. You can use std::vector ..
In modern C (C99) you have an additional choice, variable length arrays, VLA, such as that:
char myWord[N];
In principle you could also do such a thing in two dimensions, but if your sizes get too big, you may risk a stack overflow. In your case the easiest thing would be to use a pointer to such an array and to use malloc / realloc to resize them:
typedef char Word[wordlen];
size_t m = 100000;
Word* words = malloc(m * sizeof(Word));
/* initialize words[0]... words[m-1] here */
for (size_t i = 0; i < m; ++i) words[i][0] = '\0';
/* array is too small? */
m *= 2;
void *p = realloc(words, m*sizeof(Word));
if (p) words = p;
else {
/* error handling */
}
.
free(words);
This code should work (modulo typos) if wordlen is a constant or a variable, as long as you keep everything inside one function. If you want to place it in a function you should declare your function something like
void myWordFunc(size_t wordlen, size_t m, char words[m][wordlen]);
that is the length parameters must come first to be known for the declaration of words.
If the 15 in your example is variable, use one of the available answers (from Ninefingers or John Boker or Muggen).
If the 1000 is variable, use realloc:
words = malloc(1000 * sizeof(char*));
// ... read 1000 words
if (++num_words > 1000)
{
char** more_words = realloc(words, 2000 * sizeof(char*));
if (more_words) {printf("Too bad");}
else {words = more_words;}
}
In my code above, the constant 2000 is a simplification; you should add another variable capacity to support more than 2000 words:
if (++num_words > capacity)
{
// ... realloc
++capacity; // will reallocate 1000+ words each time; will be very slow
// capacity += 1000; // less reallocations, some memory wasted
// capacity *= 2; // less reallocations but more memory wasted
}
Here is a little information on dynamically allocating 2d arrays:
http://www.eskimo.com/~scs/cclass/int/sx9b.html
char ** words = malloc( 1000 * sizeof(char *));
int i;
for( i = 0 ; i < 1000 ; i++)
*(words+i) = malloc(sizeof(char) * 15);
//....
for( i = 0 ; i < 1000 ; i++)
free(*(words+i));
free(words);
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.
● 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 have dynamic array that contains thousands of elements or even more, in order not to consume a large size of memory, I can remove unwanted elements from it (i.e elements have been used and no need for them any more) so from the beginning I can allocate a smaller memory size by estimating the maximum required size after removing the elements each time.
I use this way but it takes a very very long time to finish, sometime takes 30 minutes!
int x, y ;
for (x = 0 ; x<number_of_elements_to_remove ; x++){
for (y = 0 ; y<size_of_array; y++ ){
array[y] = array[y+1];
}
}
Is there a faster way than this?
Instead of removing elements one at a time, with two loops making for an O(n2) solution, you can make a single loop, with a single read and a single write index. Go through the array, copying items as you go:
int rd = 0, wr = 0;
while (rd != size_of_array) {
if (keep_element(array[rd])) {
array[wr++] = array[rd];
}
rd++;
}
At the end of the loop wr is the number of elements kept in the array.
as I noticed you want to only delete elements from the start of the array, try this:
int x;
for(x = 0 ; x< size_of_array - number_of_elements_to_remove; x++)
array[x] = array[number_of_elements_to_remove + x];
this way you're using one for loop which reduces the complexity alot
It seems you essentially do
int y;
for (y = 0; y<size_of_array; y++){
array[y] = array[y+numbre_of_elements_to_remove];
}
The above should be faster, but there are still some caveats / problems with your code (e.g., access beyond the end od the array).
Here is the code to defragment the array.
int sparse_to_compact(int*arr, int total, int*is_valid) {
int i = 0;
int last = total - 1;
// trim the last invalid elements
for(; last >= 0 && !is_valid[last]; last--); // trim invalid elements from last
// now we keep swapping the invalid with last valid element
for(i=0; i < last; i++) {
if(is_valid[i])
continue;
arr[i] = arr[last]; // swap invalid with the last valid
last--;
for(; last >= 0 && !is_valid[last]; last--); // trim invalid elements
}
return last+1; // return the compact length of the array
}
I copied the code from this answer.
I think more efficient way is to use a link-list of buckets. And the buckets are managed by bit-string memory manager. It is like the following,
struct elem {
uint32_t index; /* helper to locate it's position in the array */
int x; /* The content/object kept in the array */
}
Suppose, our array content is int and it is encapsulated in a structure named struct elem.
enum {
MAX_BUCKET_SIZE = 1024,
MAX_BITMASK_SIZE = (MAX_BUCKET_SIZE + 63) >> 6,
};
struct bucket {
struct bucket*next; /* link to the next bucket */
uint64_t usage[MAX_BITMASK_SIZE]; /* track memory usage */
struct elem[MAX_BUCKET_SIZE]; /* the array */
};
A bucket is defined as an array of struct elem and usage mask.
struct bucket_list {
struct bucket*head; /* dynamically allocated bucket */
}container;
And a bucket list is a linked list containing all the buckets.
So we need to write memory manager code.
At first we need new bucket to be allocated when needed.
struct bucket*bk = get_empty_bucket(&container);
if(!bk) { /* no empty bucket */
/* allocate a bucket */
struct bucket*bk = (struct bucket*)malloc(sizeof(struct bucket));
assert(bk);
/* cleanup the usage flag */
memset(bk->usage, 0, sizeof(bk->usage));
/* link the bucket */
bk->next = container.head;
container.head = bk;
}
Now as we have the bucket we need to set the value in the array when needed.
for(i = 0; i < MAX_BITMASK_SIZE; i++) {
uint64_t bits = ~bk.usage[i];
if(!bits) continue; /* no space */
/* get the next empty position */
int bit_index = _builtin_ctzl(bits);
int index = (i<<6)+bit_index;
/* set the array value */
bk->elem[index].index = index;
bk->elem[index].x = 34/* my value */;
bk.usage[i] |= 1<<bit_index; /* mark/flag the array element as used */
}
Deleting the array elements is easy as to mark them unused. Now when all the elements in a bucket is unused we can delete the bucket from the link-list.
We can sometimes defragment buckets or optimize them to fit in smaller space. Otherwise when we assign new elements we can select more crowded buckets over less crowded one. When we delete we can swap the element of less crowded one into more crowded one.
It is possible to delete elements of array in efficient way,
int remove_element(int*from, int total, int index) {
if(index != (total-1))
from[index] = from[total-1];
return total; // **DO NOT DECREASE** the total here
}
It is done by swapping the element with the last value.