So I am trying to create a Memory Management System. In order to do this I have a set amount of space (allocated by malloc) and then I have a function myMalloc which will essentially return a pointer to the space allocated. Since we will then try and free it, we are trying to set a header of the allocated space to be the size of the allocated space, using memset.
memset(memPtr,sizeBytes,sizeof(int));
We then need to be able to read this so we can see the size of it. We are attempting to do this by using memcpy and getting the first sizeof(int) bytes into a variable. For testing purposes we are just trying to do memset and then immediately get the size back. I've included the entire method below so that you can see all declarations. Any help would be greatly appreciated! Thanks!
void* FirstFit::memMalloc(int sizeBytes){
node* listPtr = freelist;
void* memPtr;
// Cycle through each node in freelist
while(listPtr != NULL)
{
if(listPtr->size >= sizeBytes)
{
// We found our space
// This is where the new memory allocation begins
memPtr = listPtr->head;
memset(memPtr,sizeBytes,sizeof(int));
void *size;
memcpy(size, memPtr, sizeof(int));
// Now let's shrink freelist
listPtr->size = listPtr->size - sizeBytes;
int *temp = (int*)listPtr->head + (sizeBytes*sizeof(int));
listPtr->head = (int*) temp;
return memPtr;
}
listPtr = listPtr->next;
}
::Edit::
Sorry! When running this, we keep getting a seg fault when attempting to run the memcpy line. We have been playing with different ideas for the past hour or so and honestly just have no idea where the error is occurring.
::Edit2::
I also posted this as a comment, but figured I'd put it here as well, so it was easier to find...
Our problem is that we have an allocated space that we are allowed to work with, specified by one malloc call for 128MB. We can only use this, so we can't initialize size to anything using malloc. I guess, is there a way to do this without initializing size. If not, is there anyway to get the int that the header is set to without using memcpy.
The prototype for memcpy is
void * memcpy ( void * destination, const void * source, size_t num );
The problem lies here:
void *size; /* you haven't initialized this variable, and then you're writing to what it points to*/
memcpy(size, memPtr, sizeof(memPtr)); /* because size points to uninitialized memory it seg faults*/
EDIT1:
Please review this tutorial on pointers in C and C++ Unless you understand pointers you will not understand why those two lines of code, back to back, are a bad pair.
You code has numerous bugs in it - rather than go through them one-by-one, I'll give you a commented version of what it should look like:
void* FirstFit::memMalloc(size_t sizeBytes) // size_t is the appropriate type for memory sizes
{
node* listPtr = freelist;
void* memPtr;
// The actual allocation needs to be bigger, to have space to hold the size itself.
size_t allocSize = sizeBytes + sizeof allocSize;
// Test to make sure that allocSize didn't wrap around to zero
if (allocSize < sizeBytes)
{
return NULL;
}
// Cycle through each node in freelist
while(listPtr != NULL)
{
if(listPtr->size >= allocSize)
{
// We found our space
// This is where the new memory allocation begins
memPtr = listPtr->head;
// Copy the size to the start of the memory region
memcpy(memPtr, &allocSize, sizeof allocSize);
// Increment the pointer to be returned past the size
char *tempPtr = (char *)memPtr;
memPtr = (void *)(tempPtr + sizeof allocSize);
// Shrink the block
listPtr->size -= allocSize;
tempPtr = (char *)listPtr->head;
listPtr->head = (void *)(tempPtr + allocSize);
// TODO: If the block is now zero-sized, remove it from the linked list
return memPtr;
}
listPtr = listPtr->next;
}
/* No space */
return NULL;
}
void *size; is an uninitialized pointer, when you try to memcpy into it, your process will try to write this invalid location resulting seg fault.
Your use of memset is very odd:
memset(memPtr,sizeBytes,sizeof(int));
is equivalent to (assuming a 32 bit integer):
*((char *)memPtr + 0) = (sizeByte & 0xFF);
*((char *)memPtr + 1) = (sizeByte & 0xFF);
*((char *)memPtr + 2) = (sizeByte & 0xFF);
*((char *)memPtr + 3) = (sizeByte & 0xFF);
As you can see, it's setting each byte to the same value which is the lower byte of sizeBytes.
I'm not sure what you are intending to do so I can't offer a fix.
if you are writing it in windows... you can use
IsBadWritePtr
To Verify that the calling process has write access to the specified range of memory.
There may be three reason
1>pointers is either garbage or NULL
2>the amount you're trying to copy is too much.
i.e. copying past the end of the block of memory. Potentially "backwards" copy of string-literal would also cause this behaviour
char *s = "Hello";
char t[10];
memcpy(s, t, 6);
In creating your own memory management system, now that you have learned what memset() does and doesn't, hopefully already knowing enough of the low level stuff that you know the difference between memcpy() and memmove(), your next step is to learn about "alignment" and about the guarantees that malloc() fulfills, but your code doesn't.
Related
As far of my concern if realloc fails we loose the information and realloc set the Buffer(pointer) to NULL
Consider de following program:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void){
char *ptr = malloc(256);
if (!ptr){
printf("Error, malloc\n");
exit(1);
}
strcpy(ptr, "Michi");
ptr = realloc (ptr, 1024 * 102400000uL); /* I ask for a big chunk here to make realloc to fail */
if (!ptr){
printf("Houston we have a Problem\n");
}
printf("PTR = %s\n", ptr);
if (ptr){
free(ptr);
ptr = NULL;
}
}
And the output of course is:
Houston we have a Problem
PTR = (null)
I just lost the information inside ptr.
Now to fix this we should use a temporary buffer(pointer) before to see if we get that chunk of memory and if we get it we can use it, if not we still have the main buffer(pointer) safe.
Now please consider the following program, where instead of calling realloc I call malloc on a temporary buffer(pointer):
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void){
char *ptr = malloc(256);
char *tmpPTR = NULL;
if (!ptr){
printf("Error, malloc\n");
exit(1);
}
strcpy(ptr, "Michi");
tmpPTR = malloc (1024 * 102400000uL);
if (tmpPTR){
strcpy(tmpPTR, ptr);
strcat(tmpPTR, " - Aloha");
if (ptr){
free(ptr);
ptr = NULL;
}
}else{
printf("Malloc failed on tmpPTR\n\n");
}
if (ptr){
printf("PTR = %s\n", ptr);
free(ptr);
ptr = NULL;
}else if (tmpPTR){
printf("tmpPTR = %s\n", tmpPTR);
free(tmpPTR);
ptr = NULL;
}
}
And the output is:
Malloc failed on tmpPTR
PTR = Michi
Now why should I ever use realloc?
Is there any benefit of using realloc instead of malloc based on this context?
Your problem is with how you use realloc. You don't have to assign the result of realloc to the same pointer that you re-allocate. And as you point out it even poses a problem if the realloc fails. If you immediately assign the result to ptr then indeed you lose the previous buffer when something goes wrong. However, if you assign the result of realloc to tmpPTR, then ptr remains fine, even if the realloc fails. Use realloc as follows:
char * ptr = malloc(256);
if(!ptr){
return 1;
}
char * tmpPTR = realloc(ptr, 512);
if(!tmpPTR){
printf("Houston, we have a problem");
// ptr is fine
}else{
ptr = tmpPTR;
}
// ptr is realloc()ed
In the above code, tmpPTR is not a new (temporary) buffer, but just a (temporary) pointer. If the realloc is succesful it points to the same buffer (but possibly in a different location), and if it fails it is NULL. realloc doesn't always need to allocate a new buffer, but may be able to change the existing one to fit the new size. But if it fails, the original buffer will not be changed.
If you use malloc with a temporary buffer, then (for this example) you need at least 256 + 512 = 768 bytes and you always need to copy the old data. realloc may be able to re-use the old buffer so copying is not necessary and you don't use more memory than requested.
You can use your malloc approach, but realloc is almost always more efficient.
The realloc scheme is simple. You do not need a separate call to malloc. For example if you initially have 256 bytes allocated for ptr, simply use a counter (or index, i below) to keep track of how much of the memory within the block allocated to ptr has been used, and when the counter reaches the limit (1 less than the max for 0-based indexes, or 2 less than the max if you are using ptr as a string), realloc.
Below shows a scheme where you are simply adding 256 additional bytes to ptr each time the allocation limit is reached:
int i = 0, max = 256;
char *ptr = malloc(max);
/* do whatever until i reaches 255 */
if (i + 1 >= max) {
void *tmp = realloc (ptr, max + 256);
if (!tmp) {
fprintf (stderr, "error: realloc - memory exhausted.\n")
/* handle error */
}
ptr = tmp;
max += 256;
}
note: your handle error can exit whatever loop you are in to preserve the existing data in ptr. You do not need to exit at that point.
The advantage of realloc over malloc is that it may be able to extend the original dynamic memory area so without the need to copy all the previous elements; you can't do that with malloc1. And whether this optimization is available costs no work to you.
Let's assume you have a previously allocated pointer:
char *some_string = malloc(size); // assume non-NULL
Then
if (realloc_needed) {
char *tmp = realloc(some_string, new_size);
if ( tmp == NULL )
// handle error
else
some_string = tmp; // (1)
At (1), you update the old pointer with the new one. Two things can happen: the address has effectively changed (and the elements been automatically copied) or it hasn't - you don't really care. Either way, your data is now at some_string.
Only the actual implementation (OS / libc) knows whether it's possible to enlarge the block: you don't get to see it, it's an implementation detail. You can however check your implementation's code and see how it's implemented.
Now to fix this we should use a temporary buffer(pointer) before to see if we get that chunk of memory and if we get it we can use it, if not we still have the main buffer(pointer) safe.
That not only doesn't help, it makes things worse because now you no longer have the pointer to the block you tried to reallocate. So how can you free it?
So it:
Wastes memory.
Require an extra allocate, copy, and free.
Makes the realloc more likely to fail because of 1.
Leaks memory since the pointer to the block you tried to reallocate is lost.
So no, that's not a good way to handle realloc returning NULL. Save the original pointer when you call realloc so you can handle failure sanely. The point of realloc to save you from having to manage two copies of the data and to avoid even making them when that's possible. So let realloc do this work for you whenever you can.
It is technically malloc(size) that is unneeded, because realloc(NULL, size) performs the exact same task.
I often read inputs of indeterminate length. As in the following function example, I rarely use malloc(), and instead use realloc() extensively:
#include <stdlib.h>
#include <errno.h>
struct record {
/* fields in each record */
};
struct table {
size_t size; /* Number of records allocated */
size_t used; /* Number of records in table */
struct record item[]; /* C99 flexible array member */
};
#define MAX_ITEMS_PER_READ 1
struct table *read_table(FILE *source)
{
struct table *result = NULL, *temp;
size_t size = 0;
size_t used = 0, n;
int err = 0;
/* Read loop */
while (1) {
if (used + MAX_ITEMS_PER_READ > size) {
/* Array size growth policy.
* Some suggest doubling the size,
* or using a constant factor.
* Here, the minimum is
* size = used + MAX_ITEMS_PER_READ;
*/
const size_t newsize = 2*MAX_ITEMS_PER_READ + used + used / 2;
temp = realloc(result, sizeof (struct table) +
newsize * sizeof (result->item[0]));
if (!temp) {
err = ENOMEM;
break;
}
result = temp;
size = newsize;
}
/* Read a record to result->item[used],
* or up to (size-used) records starting at result->item + used.
* If there are no more records, break.
* If an error occurs, set err = errno, and break.
*
* Increment used by the number of records read: */
used++;
}
if (err) {
free(result); /* NOTE: free(NULL) is safe. */
errno = err;
return NULL;
}
if (!used) {
free(result);
errno = ENODATA; /* POSIX.1 error code, not C89/C99/C11 */
return NULL;
}
/* Optional: optimize table size. */
if (used < size) {
/* We don't mind even if realloc were to fail here. */
temp = realloc(result, sizeof (struct table) +
used * sizeof table->item[0]);
if (temp) {
result = temp;
size = used;
}
}
result->size = size;
result->used = used;
errno = 0; /* Not normally zeroed; just my style. */
return result;
}
My own practical reallocation policies tend to be very conservative, limiting the size increase to a megabyte or so. There is a very practical reason for this.
On most 32-bit systems, userspace applications are limited to 2 to 4 gigabyte virtual address space. I wrote and ran simulation systems on a lot of different x86 systems (32-bit), all with 2 to 4 GB of memory. Usually, most of that memory is needed for a single dataset, which is read from disk, and manipulated in place. When the data is not in final form, it cannot be directly memory-mapped from disk, as a translation -- usually from text to binary -- is needed.
When you use realloc() to grow the dynamically allocated array to store such huge (on 32-bit) datasets, you are only limited by the available virtual address space (assuming there is enough memory available). (This especially applies to 32-bit applications on 64-bit systems.)
If, instead, you use malloc() -- i.e., when you notice your dynamically allocated array is not large enough, you malloc() a new one, copy the data over, and discard the old one --, your final data set size is limited to a lesser size, the difference depending on your exact array size growth policy. If you use the typical double when resizing policy, your final dataset is limited to about half (the available virtual address space, or available memory, whichever is smaller).
On 64-bit systems with lots and lots of memory, realloc() still matters, but is much more of a performance issue, rather than on 32-bit, where malloc() is a limiting factor. You see, when you use malloc() to allocate a completely new array, and copy the old data to the new array, the resident set size -- the actual amount of physical RAM needed by your application -- is larger; you use 50% more physical RAM to read the data than you would when using realloc(). You also do a lot of large memory-to-memory copies (when reading a huge dataset), which are limited to physical RAM bandwidth, and indeed slow down your application (although, if you are reading from a spinning disk, that is the actual bottleneck anyway, so it won't matter much).
The nastiest effect, and the most difficult to benchmark, are the indirect effects. Most operating systems use "free" RAM to cache recently accessed files not modified yet, and this really does decrease the wall clock time used by most workloads. (In particular, caching typical libraries and executables may shave off seconds from the startup time of large application suites, if the storage media is slow (ie. a spinning disk, and not a SSD).) Your memory-wasting malloc()-only approach gobbles up much more actual physical RAM than needed, which evicts cached, often useful, files from memory!
You might benchmark your program, and note that there is no real difference in run times between using your malloc()-only approach and realloc() approach I've shown above. But, if it works with large datasets, the users will notice that using the malloc()-only program slows down other programs much more than the realloc()-using program, with the same data!
So, although on 64-bit systems with lots of RAM using malloc() only is basically an inefficient way to approach things, on 32-bit systems it limits the size of dynamically allocated arrays when the final size is unknown beforehand. Only using realloc() can you there achieve the maximum possible dataset size.
Your assumption is wrong. Please do note that a pointer is not a buffer. When the function realloc() succeeds, it deallocates the old pointer(frees the original buffer) and return a new pointer to the new allocation(buffer), but when it fails, it leaves the old buffer intact and returns NULL.
So, you do not need a temporary buffer. You need a temporary pointer. I am going to borrow the example from kninnug, this is what you need to do:
char * ptr = malloc(256);
if (!ptr) {
return 1;
}
char * tmpPTR = realloc(ptr, 512);
if (!tmpPTR) {
printf("Houston, we have a problem");
// ptr is fine
}
else {
ptr = tmpPTR;
}
// ptr is realloc()ed
I'm creating a source files containing buffer functionality that I want to use for my other library that I'm creating.
It is working correctly but I'm having trouble getting rid of the buffer structure that I'm creating in one of the functions. The following snippets should help illustrate my problem:
C header:
//dbuffer.h
...
typedef struct{
char *pStorage;
int *pPosition;
int next_position;
int number_of_strings;
int total_size;
}DBUFF;
...
C source:
//dbuffer.c
...
DBUFF* dbuffer_init(char *init_pArray)
{
//Find out how many elements the array contains
int size = sizeof_pArray(init_pArray);
//Initialize buffer structure
DBUFF *buffer = malloc(sizeof(DBUFF));
//Initialize the storage
buffer->pStorage = malloc( (sizeof(char)) * (size) );
strncpy( &(buffer->pStorage)[0] , &init_pArray[0] , size);
buffer->number_of_strings = 1;
buffer->total_size = size;
buffer->next_position = size; //size is the next position because array allocates elements from 0 to (size-1)
//Initialize the position tracker which keeps record of starting position for each string
buffer->pPosition = malloc(sizeof(int) * buffer->number_of_strings );
*(buffer->pPosition + (buffer->number_of_strings -1) ) = 0;
return buffer;
}
void dbuffer_destroy(DBUFF *buffer)
{
free(buffer->pStorage);
free(buffer);
}
...
Main:
#include <stdio.h>
#include <stdlib.h>
#include "dbuffer.h"
int main(int argc, char** argv)
{
DBUFF *buff;
buff = dbuffer_init("Bring the action");
dbuffer_add(buff, "Bring the apostles");
printf("BUFFER CONTENTS: ");
dbuffer_print(buff);
dbuffer_destroy(buff);
// Looks like it has been succesfully freed because output is garbage
printf("%s\n", buff->pStorage);
//Why am I still able to access struct contents after the pointer has been freed ?
printf("buff total size: %d\n", buff->total_size);
return (EXIT_SUCCESS);
}
Output:
BUFFER CONTENTS: Bring the action/0Bring the apostles/0
��/�
buff total size: 36
RUN SUCCESSFUL (total time: 94ms)
Question:
Why am I still able to access struct contents using the line below after the pointer to the struct has been freed ?
printf("buff total size: %d\n", buff->total_size);
Once you've called free() on the allocated pointer, attempt to make use of the pointer invokes undefined behavior. You should not be doing that.
To quote C11 standard, chapter §7.22.3.4, free() function
The free() function causes the space pointed to by ptr to be deallocated, that is, made
available for further allocation. [..]
It never say's anything about a cleanup, which you might be (wrongly) expecting.
Just to add clarity, calling free() does not always actually free up the allocated physical memory. It just enables that pointer (memory space) to be allocated again (returning the same pointer, for example) for successive calls to malloc() and family. After calling free(), that pointer is not supposed to be used from your program anymore but C standard does not guarantee of a cleanup of the allocated memory.
If any attempt is made to read memory that has been freed can crash your program. Or they might not. As far as the language is concerned, its undefined behaviour.
Your compiler won't warn you about it(or stop you from accessing it). But clearly don't do this after calling free -
printf("buff total size: %d\n", buff->total_size);
As a good practice you can set the freed pointer to NULL .
free() call will just mark the memory in heap as available for use. So you still have the pointer pointing to this memory location but it's not available anymore for you. Thus, the next call to malloc() is likely to assign this memory to the new reservation.
To void this situations normally once you free() the memory allocated to a pointer you should set it to NULL. De-referencing NULL is UB also but at least when debugging you can see tha pointer should not be used because it's not pointing to a valid memory address.
[too long for a comment]
To allow your "destructor" to set the pointer passed to NULL modify your code like this:
void dbuffer_destroy(DBUFF ** buffer)
{
if ((NULL == buffer) || (NULL == *buffer))
{
return;
}
free((*buffer)->pPosition);
free((*buffer)->pStorage);
free(*buffer);
*buffer = NULL;
}
and call it like this:
...
dbuffer_destroy(&buff);
...
Using what I have learned here: How to use realloc in a function in C, I wrote this program.
int data_length; // Keeps track of length of the dynamic array.
int n; // Keeps track of the number of elements in dynamic array.
void add(int x, int data[], int** test)
{
n++;
if (n > data_length)
{
data_length++;
*test = realloc(*test, data_length * sizeof (int));
}
data[n-1] = x;
}
int main(void)
{
int *data = malloc(2 * sizeof *data);
data_length = 2; // Set the initial values.
n = 0;
add(0,data,&data);
add(1,data,&data);
add(2,data,&data);
return 0;
}
The goal of the program is to have a dynamic array data that I can keep adding values to. When I try to add a value to data, if it is full, the length of the array is increased by using realloc.
Question
This program compiles and does not crash when run. However, printing out data[0],data[1],data[2] gives 0,1,0. The number 2 was not added to the array.
Is this due to my wrong use of realloc?
Additional Info
This program will be used later on with a varying number of "add" and possibly a "remove" function. Also, I know realloc should be checked to see if it failed (is NULL) but that has been left out here for simplicity.
I am still learning and experimenting with C. Thanks for your patience.
Your problem is in your utilisation of data, because it points on the old array's address. Then, when your call realloc, this area is freed. So you are trying to access to an invalid address on the next instruction: this leads to an undefined behavior.
Also you don't need to use this data pointer. test is sufficient.
(*test)[n-1] = x;
You don't need to pass data twice to add.
You could code
void add(int x, int** ptr)
{
n++;
int *data = *ptr;
if (n > data_length) {
data_length++;
*ptr = data = realloc(oldata, data_length * sizeof (int));
if (!data)
perror("realloc failed), exit(EXIT_FAILURE);
}
data [n-1] = x;
}
but that is very inefficient, you should call realloc only once in a while. You could for instance have
data_length = 3*data_length/2 + 5;
*ptr = data = realloc(oldata, data_length * sizeof (int));
Let's take a look at the POSIX realloc specification.
The description says:
If the new size of the memory object would require movement of the object, the space for the previous instantiation of the object is freed.
The return value (emphasis added) mentions:
Upon successful completion with a size not equal to 0, realloc() returns a pointer to the (possibly moved) allocated space.
You can check to see if the pointer changes.
int *old;
old = *test;
*test = realloc(*test, data_length * sizeof(int));
if (*test != old)
printf("Pointer changed from %p to %p\n", old, *test);
This possible change can interact badly because your code refers to the "same" memory by two different names, data and *test. If *test changes, data still points to the old chunk of memory.
The realloc reference says:
The function may move the memory block
to a new location, in which case the
new location is returned.
Does it mean that if I do this:
void foo() {
void* ptr = malloc( 1024 );
unsigned char* cptr = ( unsigned char* )ptr+256;
ptr = realloc( ptr, 4096 );
}
then cptr may become invalid if realloc moves the block?
If yes, then does realloc signal in any way, that it will move the block, so that I can do something to prevent cptr from becoming invalid?
Yes, cptr will become invalid as realloc moves the block! And no, there is no mention of signalling to you to tell that it is moving the block of memory. By the way, your code looks iffy...read on... please see my answer to another question and read the code very carefully on how it uses realloc. The general consensus is if you do this:
void *ptr = malloc(1024);
/* later on in the code */
ptr = realloc(ptr, 4096);
/* BAM! if realloc failed, your precious memory is stuffed! */
The way to get around that is to use a temporary pointer and use that as shown:
void *ptr = malloc(1024);
/* later on in the code */
void *tmp = realloc(ptr, 4096);
if (tmp != null) ptr = tmp;
Edit: Thanks Secure for pointing out a gremlin that crept in when I was typing this earlier on.
This is coming a bit late, but the solution to this problem (which nobody has mentioned) is not to use pointers into allocated blocks that will need to be allocated. Instead, use integer-valued offsets from the base pointer or (better) use a struct type and member elements to address specific locations in the allocated object.
Yes, cptr will become invalid if realloc moves the block.
No, there is no signal. You would have to check the return value against the original ptr location.
Yes.
Best thing to do is compare ptr before and after the reallocation, and see if it has been moved. You shouldn't assign a pointer to the offset value, instead you should store the offset and then index the original operator with it.
i.e.
Instead of
void* newPtr = ptr + 10;
*newPtr = something;
Use
int new = 10;
ptr[new] = something;
Yes, the cptr becomes invalid if realloc moves the block.
I am writing the calloc function in a memory management assignment (I am using C). I have one question, I wrote the malloc function and thinking about using it for calloc as it says calloc will take num and size and return a block of memory that is (num * size) which I can use malloc to create, however, it says that I need to initialize all bytes to 0 and I am confused about how to do that in general?
If you need more info please ask me :)
So malloc will return a pointer (Void pointer) to the first of the usable memory and i have to go through the bytes, initialize them to zero, and return the pointer to that front of the usable memory.
I am assuming you can't use memset because it's a homework assignment assignment, and deals with memory management. So, I would just go in a loop and set all bytes to 0. Pseudocode:
for i = 1 to n:
data[i] = 0
Oh, if you're having trouble understanding how to dereference void *, remember you can do:
void *b;
/* now make b point to somewhere useful */
unsigned char *a = b;
When you need to set a block of memory to the same value, use the memset function.
It looks like this: void * memset ( void * ptr, int value, size_t num );
You can find more information about the function at: http://www.cplusplus.com/reference/clibrary/cstring/memset/
If you can't use memset, then you'll need to resort to setting each byte individually.
Since you're calling malloc from your calloc function, I'm going to assume it looks something like this:
void *calloc (size_t count, size_t sz) {
size_t realsz = count * sz;
void *block = malloc (realsz);
if (block != NULL) {
// Zero memory here.
}
return block;
}
and you just need the code for "// Zero memory here.".
Here's what you need to know.
In order to process the block one byte at a time, you need to cast the pointer to a type that references bytes (char would be good). To cast your pointer to (for example) an int pointer, you would use int *block2 = (int*)block;.
Once you have the right type of pointer, you can use that to store the correct data value based on the type. You would do this by storing the desired value in a loop which increments the pointer and decrements the count until the count reaches zero.
Hopefully that's enough to start with without giving away every detail of the solution. If you still have problems, leave a comment and I'll flesh out the answer until you have it correct (since it's homework, I'll be trying to get you to do most of the thinking).
Update: Since an answer's already been accepted, I'll post my full solution. To write a basic calloc in terms of just malloc:
void *calloc (size_t count, size_t sz) {
size_t realsz, i;
char *cblock;
// Get size to allocate (detect size_t overflow as well).
realsz = count * sz;
if (count != 0)
if (realsz / count != sz)
return NULL;
// Allocate the block.
cblock = malloc (realsz);
// Initialize all elements to zero (if allocation worked).
if (cblock != NULL) {
for (i = 0; i < realsz; i++)
cblock[i] = 0;
}
// Return allocated, cleared block.
return cblock;
}
Note that you can work directly with char pointers within the function since they freely convert to and from void pointers.
Hints:
there is already a posix library function for zeroing a block of memory
consider casting the void * to some pointer type that you can dereference / assign to.