I'm actually learning C programming and my school actually doesn't allow us to use calloc / realloc without reprogramming them. That's why I'm asking for help.
Here is my problem :
I want to use void * to make my code reusable but I encounter the problem "dereferencing void * pointer" when I try to run through my array. I'm unable to pick up the type of the final pointer.
Here is my functions :
#include <stdlib.h>
void *my_calloc(size_t size, size_t n) //n = number of bytes your type : sizeof(<whatever>)
{
void *ptr = NULL;
if (size < 1 || n < 1)
return (NULL);
ptr = malloc(n * (size + 1));
if (ptr == NULL)
return (NULL);
for (int i = 0; i != (n * (size + 1)); i++) {
*ptr = NULL; //Here is my problem
ptr++;
}
return (ptr);
}
void *my_realloc(void *src, size_t size, size_t n)
{
void *dst = NULL;
int dst_len = 0;
if (src == NULL || size < 0 || n < 1)
return (NULL);
dst_len = my_strlen(src) + size;
if (dst_len == my_strlen(src))
return (src);
dst = my_calloc(dst_len, n);
if (dst == NULL)
return (NULL);
for (int i = 0; src[i] != NULL;i++)
dst[i] = src[i]; //Here is the same problem...
free(src);
return (dst);
}
I just find a problem while I was writing my post, my my_strlen function can only take a char *... so I would need a function my_strlen looking like :
int my_strlen(void *str)
{
int len = 0;
while (str[len] != NULL) { //same problem again...
len++;
}
return (len);
}
A typical function where i call calloc / malloc would be :
int main(void)
{
char *foo = NULL;
int size = 0;
int size_to_add = 0;
size = <any size>;
//free(foo); //only if foo has been malloc before
foo = my_calloc(size, typeof(*foo));
//something
size_to_add = <any size>;
foo = my_realloc(foo, size_to_add, sizeof(*foo))
//something
free(foo);
return (0);
}
Thank you for trying to help me.
my_calloc() has various troubles:
Attemptted pointer math on a void *
This is undefined behavior (UB).
Instead make ptr a character pointer.
// void *ptr = NULL;
unsigned char *ptr = NULL;
...
ptr++;
Attempt to de-reference a void *
This is also UB.
Instead make ptr a character pointer.
// void *ptr = NULL;
unsigned char *ptr = NULL;
...
// *ptr = NULL;
*ptr = '\0';
my_calloc() allocates more memory than calloc()
To do the same as calloc(), do not add one.
// ptr = malloc(n * (size + 1));
ptr = malloc(n * size);
No overflow protection
my_calloc() does not detect overflow with n * (size + 1). A test is
// Note: it is known n > 0 at this point
if (SIZE_MAX/n > size+1) return NULL;
// or if OP drop the + 1 idea,
if (SIZE_MAX/n > size) return NULL;
my_realloc() has various troubles:
Different signature
I'd expect the goal of "school actually doesn't allow us to use calloc / realloc without reprogramming them" was meant to create a realloc() substitute of which my_realloc() is not. If a different function is desired, consider a new name
void *my_realloc(void *src, size_t size, size_t n)
// does not match
void *realloc(void *ptr, size_t size);
Failure to handle a shrinking allocation
The copying of data does not take into account that the new allocation may be smaller than the prior one. This leads to UB.
Unneeded code
size < 0 is always false
Memory leak
The below code does not free src before returning. Further, it does not allocate anything when n>0. This differs from calloc(pit, 0) and calloc(NULL, 42).
// missing free, no allocation
if (src == NULL || size < 0 || n < 1) {
return (NULL);
}
Assumed string
my_strlen(src) assume src points to a valid string. calloc() does not assume that.
void is an incomplete type, so you can't dereference a void *. What you can do however is cast it to a char * or unsigned char * to access individual bytes.
So my_calloc can do this:
((char *)ptr)[i] = 0;
And my_realloc can do this:
((char *)dst)[i] = ((char *)src)[i];
Related
The calloc function in C returns a void pointer but the memory bytes pointed to are already initialized with values, How is this is achieved?
I am trying to write a custom calloc function in C but can't find a way to initialize the allocated memory bytes
My code
#include "main.h"
/**
* _calloc - Allocate memory for an array
* #nmemb: Number of elements
* #size: Size of each element
*
* Description: Initialize the memory bytes to 0.
*
* Return: a Void pointer to the allocated memory, if error return NULL
*/
void *_calloc(unsigned int nmemb, unsigned int size)
{
unsigned int i, nb;
void *ptr;
if (nmemb == 0 || size == 0)
return NULL;
nb = nmemb * size;
ptr = malloc(nb);
if (ptr == NULL)
return NULL;
i = 0;
while (nb--)
{
/*How do i initialize the memory bytes?*/
*(ptr + i) = '';
i++;
}
return (ptr);
}
Simply use pointer to another type to dereference it.
example:
void *mycalloc(const size_t size, const unsigned char val)
{
unsigned char *ptr = malloc(size);
if(ptr)
for(size_t index = 0; index < size; index++) ptr[index] = val;
return ptr;
}
or your version:
//use the correct type for sizes and indexes (size_t)
//try to have only one return point from the function
//do not use '_' as a first character of the identifier
void *mycalloc(const size_t nmemb, const size_t size)
{
size_t i, nb;
char *ptr = NULL;
if (nmemb && size)
{
nb = nmemb * size;
ptr = malloc(nb);
if(ptr)
{
i = 0;
while (nb--)
{
//*(ptr + i) = 'z';
ptr[i] = 'z'; // isn't it looking better that the pointer version?
i++;
}
}
}
return ptr;
}
Then you can use it assigning to other pointer type or casting.
example:
void printByteAtIndex(const void *ptr, size_t index)
{
const unsigned char *ucptr = ptr;
printf("%hhu\n", ucptr[index]);
}
void printByteAtIndex1(const void *ptr, size_t index)
{
printf("%hhu\n", ((const unsigned char *)ptr)[index]);
}
char *ft_substr(char const *s, unsigned int start, size_t len)
{
size_t i;
size_t d;
char *subs;
i = 0;
d = 0;
subs = (char *)malloc(sizeof(char) * (len + 1));
if (!subs || !s)
{
return NULL;
}
while (s[i])
{
if (i >= start && d < len)
{
subs[d] = s[i];
d++;
}
i++;
}
subs[d] = '\0';
return subs;
}
The code that appears above is the substr function I wrote, when I checked the memory leak using Memd, I noticed that there was an error on the 14th line.
subs = (char *)malloc(sizeof(char) * (len + 1));
Unfortunately, I do not have the authority to install Valgrind on the device I use, so I was able to test with memd. I'm fairly new to C, can you explain why I'm getting a memory leak error and how can I fix it?
The key to solving this would be to add a guard to the beginning of your function, before you allocate for subs which may never be returned and thus be unable to be freed.
if (!s) return NULL;
You can still check later when subs is allocated and return NULL if that allocation fails.
subs = (char *)malloc(sizeof(char) * (len + 1));
if (!subs || !s)
{
return (NULL);
}
I the sub-statement of the if statement will get the control when s is equal to NULL when there will be a memory leak.
Pay attention to that you may remove the check whether s is a null pointer as all C standard string functions do. If the user will call the function passing a null pointer then there will be undefined behavior.
And the call of malloc can allocate more memory then it is required.
Also the types of the second and third parameters are inconsistent.
char *ft_substr(char const *s, unsigned int start, size_t len)
The both should have the type size_t.
If you want to make it safe you need not only to check if s is not NULL but also if the start is not past the length of the string.
Try to have only one return point from the function and rather use positive checks (ie if something is valid, instead of checking if it is invalid).
char *ft_substr(char const * restrict s, const size_t start, const size_t length)
{
char *subs = NULL;
if(s)
{
size_t len = strlen(s);
if(len >= start)
{
subs = malloc(length + 1);
if(subs)
{
char *wrk = subs;
size_t tocopy = length;
s += start;
while(tocopy-- && (*wrk++ = *s++));
*wrk = 0;
}
}
}
return subs;
}
Assuming there is a function like this
int foo (char** str, int x)
{
char* p = *str + x;
foo2(&p); // declared as int foo2 (char** );
}
(oversimplified of course, the real function is recursive and much more complicated)
I've tried to do this:
int foo (char** str, int x)
{
foo2(&(*str + x));
}
But the compiler failed with error:
error: lvalue required as unary '&' operand
Why did the compiler shoot out with this error and how do I pass the pointer to a pointer to string x-byte(s) forwards, without declaring a variable and use its own address?
EDIT
Seems like there is some misunderstanding so I will post a complete simulation of what I want to achieve.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* string = "This is a sample string.";
char* ptr;
int randomizer;
int receive_string (char* buffer, int size) // recv
{
int i = 0;
if(ptr == NULL)
ptr = string;
for(i = 0; *ptr != '\0' && i < size; ptr++)
{
if(randomizer == 2)
{
randomizer++;
break;
}
buffer[i] = *ptr;
i++;
randomizer++;
}
if(*ptr == '\0')
{
buffer[i] = *ptr;
i++;
}
return i;
}
int read_string (char* *buffer, int size, int alloc)
{
int bytes = 0;
printf("Reading string..\n");
if(*buffer == NULL && alloc == 1)
{
printf("Allocating buffer..\n");
*buffer = calloc(size, sizeof(char));
}
bytes = receive_string(*buffer, size);
if(bytes == (-1))
{
return(-1);
}
if(bytes == 0)
{
return 0;
}
if(bytes < size)
{
char* p = *buffer + bytes;
//int temp = read_string(&p, size - bytes, 0); // works
//int temp = read_string(&(char *){&(*buffer)[bytes]}, size - bytes, 0); // works
int temp = read_string(buffer + bytes, size - bytes, 0); // doesn't work
if(temp > 0)
bytes += temp;
else return bytes;
}
return bytes;
}
int main()
{
char* buffer = NULL;
int bytes = read_string(&buffer, strlen(string) + 1, 1);
printf("[%u][%s]\n", bytes, buffer);
if(buffer)
free(buffer);
return 0;
}
The randomizer is the dumbest quickie to "simulate" a recv() that can not receive all bytes. This implementation simulates recv() but instead of reading from a socket queue it reads from a global string.
(*str + x) is not an lvalue as it is a temporay value that does not have an address so you cannot take its address with &. Even if the compiler stored the value in a temporary variable in RAM so its address could be taken how would you reference its value afterwards if foo2() modified the contents of the temporay variable.
Therefore you need to store the value in a temporary variable yourself.
if you want to pass the pointer to pointer to the particular char
foo2(&(char *){&(*str)[x]});
or
I think the following code is what you are trying to do. For kicks, I made it recursive and tested it with the alphabet for a string. Variables cnt and lmt need to be global. It will show a shrinking string if you run it. Just be sure to keep p and lmt small enough to not overflow the string.
void foo(char *s, int p) {
cnt++;
printf("%s\n", s);
if(cnt != lmt) foo(&s[p], p);
}
hello friends :) i'm practicing C programming. in this program i have a task to make array of string. i have no idea what's wrong here...probably something about realloc, error i get is _crtisvalidheappointer
#define _CRT_SECURE_NO_WARNINGS
#define MAX 100
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void readString(char **s)
{
int i = 0;
char c;
printf("\nInput string: ");
while ((c = getchar()) != '\n')
{
i++;
*s = realloc(*s, i*sizeof(char*));
if (*s == NULL) { printf("Memory allocation failed!"); exit(1); }
(*s)[i - 1] = c;
}
*s = realloc(*s, (i + 1)*sizeof(char));
if (*s == NULL) { printf("Memory allocation failed!"); exit(1); }
(*s)[i] = '\0';
}
char **load_words()
{
int cnt=0,wordcnt=0,i=0;
char **words = NULL, *input = NULL;
readString(&input);
while (input[cnt] != '\0' && cnt < strlen(input))
{
words = realloc(words, ++wordcnt);//errors in second repeat of the loop
words[wordcnt] = malloc(MAX);
i = 0;
while (input[cnt] != ' ')
{
words[wordcnt][i++] = input[cnt++];
}
words[wordcnt][i] = '\0';
realloc(words[wordcnt], (i + 1)*sizeof(char));
}
realloc(words, wordcnt);
free(input);
return words;
}
void main()
{
int i;
char **words = NULL;
words = load_words();
scanf("%d", &i);
}
can someone help me and tell me what did i do wrong here? this function should return array of strings but array should be double pointer(string matrix)
You need to change
words = realloc(words, ++wordcnt);
to
words = realloc(words, ++wordcnt * sizeof(*words));
Otherwise you are not allocating enough memory.
words[wordcnt] = malloc(MAX);
This also is not correct, you should access words[wordcnt-1].
You are using realloc but you're not saving its return value anywhere. This means the pointers you have still point to the memory that was freed and the newly allocated memory is leaked.
Look at the working function and you'll see how to use it properly.
One thing to realize when reallocating a double-pointer is that the size of type to realloc is always the sizeof (a pointer). It will be the same on any given system no matter the data type at issue. You can generically reallocate a double-pointer as follows:
/** realloc array of pointers ('memptr') to twice current
* number of pointer ('*nptrs'). Note: 'nptrs' is a pointer
* to the current number so that its updated value is preserved.
* no pointer size is required as it is known (simply the size
* of a pointer)
*/
void *xrealloc_dp (void *ptr, size_t *n)
{
void **p = ptr;
void *tmp = realloc (p, 2 * *n * sizeof tmp);
if (!tmp) {
fprintf (stderr, "xrealloc_dp() error: virtual memory exhausted.\n");
exit (EXIT_FAILURE); /* or break; to use existing data */
}
p = tmp;
memset (p + *n, 0, *n * sizeof tmp); /* set new pointers NULL */
*n *= 2;
return p;
}
note: the memset call is optional, but useful if you have initialized all non-assigned pointers to NULL (such as when using NULL as a sentinel)
note2: you are free to pass a parameter setting the exact number of pointers to increase (elements to add) or change the multiplier for the current allocation as needed for your code.
I use this code, with this structure, im trying to make function to add item into array of this structure
typedef struct goods{
char *name;
int num;
} goods;
void addWord(char *what, goods *where, int pnr, int *arrsize, int n){
if (pnr >= *arrsize){
where = (goods*)realloc(where,*arrsize*2*sizeof(goods*));
*arrsize*=2;
}
where[pnr].name = (char*)malloc(strlen(what)*sizeof(char));
strcpy(where[pnr].name,what);
where[pnr].num = n;
}
in main function i have this:
int extstore = 1;
goods *store = (goods*)malloc(1*sizeof(goods*));
addWord(line, store, nr, &extstore, n);
Why am I getting an "invalid next size" runtime-error on the line where = (goods*)realloc(where,*arrsize*2*sizeof(goods*)); in addWord()?
EDIT:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct goods{
char *name;
int r;
} goods;
int main()
{
int linelen, i, nr = 0, current_r;
char *line = NULL;
size_t len = 0;
int extstore = 1;
goods *store;
store = malloc(extstore*sizeof(goods*));
while (1){
while ((linelen = getline(&line, &len, stdin)) != -1){
if (line[linelen - 1] == '\n'){
line[linelen - 1] = '\0';
}
linelen = strlen(line);
if (line[0] == '#'){
if (sscanf(line,"#%d",¤t_r) != 1){
printf("bad input.");
return 0;
} else continue;
}
if (nr >= extstore){
store = realloc(store,extstore * sizeof(goods*) * 2);
extstore*=2;
}
store[nr].name = malloc(strlen(line)*sizeof(char));
strcpy(store[nr].name,line);
store[nr].r = current_r;
nr++;
}
if (linelen == -1) break;
}
printf("\n");
for (i = 0;i < nr;i++){
printf("%s, [id:%d]\n", store[i].name, store[i].r);
}
return 0;
}
extstore * sizeof(goods*) * 2
should be extstore * sizeof(goods) * 2 because the space for structures should be allocated - not just for pointers.
There is a fundamental problem in your code. You are passing pointer by value, which means that any change made to a pointer (not the variable pointed to, but the pointer itself) will not be visible from outside the function. You should pass a pointer by pointer instead, and you should check the result returned from realloc. Secondly, don't assign result of realloc back to same pointer - in case of failure you will lost pointer to memory -> thus, memory leak will occur.
To pass pointer by pointer:
void addWord( char *what, goods **where, size, ...) {
if ( *where == NULL) return; // nothing to do
if ( size < 1) return; // it would result in realloc=free call
goods *res = NULL;
res = realloc( *where, size * sizeof( goods));
if ( res != NULL) {
*where = res;
}
else {
// Error (re)allocating memory
// If realloc() fails the original block is left untouched,
// it is not freed or moved, so here *where is unchanged
}
And there is no need in C to cast a result from malloc.
* Error in `path': realloc(): invalid next size: 0x0000000000ec8010 *
This failure must be because "where" is invalid due to a heap corruption earlier in the execution.
C is pass-by-value.
Which means changing an argument in the function does not change the expression it was initialized from.
Thus, the first time realloc moves the memory, the pointer in main will be bad.
To correct that, either use an extra level of indirection, or preferably return the new value as the result.
(Anyway, you should check for allocation failure (malloc and realloc),
and you should not cast from void* to any pointer-type in C.)