How to use snprintf instead of strcpy in this situation? - c

void put(char* key, int value)
{
int i = 0;
// Iterate through elements of hashtable
while (array[i].flag == 1)
{
// If key already exists, update the value
// and return
if (strcmp(array[i].data->key, key) == 0)
{
array[i].data->value = value;
return;
}
i = i + 1;
// Error Handling, when end of hashtable is reached
if (i == max)
{
i = 0;
// p rintf("\n Hash table is full, cannot insert any more item \n");
// return;
}
}
// Insert new item into the hashtable
array[i].flag = 1;
array[i].data = (struct item*) malloc(sizeof(struct item));
array[i].data->key = (char *)malloc((strlen(key)+1)*sizeof(char));
// so here I have to use snprintf instead of strcy because it's
// forbidden, and I don't know how
**strcpy(array[i].data->key, key);**
//snprintf(array[i].data->key,sizeof(key),"%s",key);
array[i].data->value = value;
}

Firstly, never cast malloc, and do not forget verify the return value of malloc.
array[i].data = malloc(sizeof(struct item));
if (!array[i].data) {
// handle error
return; // for example
}
array[i].data->key = malloc((strlen(key)+1)*sizeof(char));
if (!array[i].data->key) {
// handle error
}
Using strlen instead of sizeof that returns the size of pointer, not the length of string:
snprintf(array[i].data->key, strlen(key) + 1,"%s", key);

here I have to use snprintf instead of strcy because it's forbidden, and I don't know how
strcpy() can overflow is the destination is insufficient. That really does not apply here as the needed space is allocated.
// array[i].data = (struct item*) malloc(sizeof(struct item));
// array[i].data->key = (char *)malloc((strlen(key)+1)*sizeof(char));
// strcpy(array[i].data->key, key);
array[i].data = malloc(sizeof *(array[i].data));
size_t sz = strlen(key) + 1;
array[i].data->key = malloc(sz);
snprintf(array[i].data->key, sz, "%s", key);
// or
strcpy(array[i].data->key, key);
// or
memcpy(array[i].data->key, key, sz);
Cast not needed
size of of referenced data better to use that sizeof type.
Avoid 2 trips finding the length.
Returns check omitted for brevity.
With checks
array[i].data = malloc(sizeof *(array[i].data));
if (array[i].data == NULL) return fail; // or do something to indicate error
size_t sz = strlen(key) + 1;
array[i].data->key = malloc(sz);
if (array[i].data->key == NULL) return fail;
int count = snprintf(array[i].data->key, sz, "%s", key);
if (count < 0 || (unsigned) count >= sz) return fail;
Primary mistake
OP's attempt fails as sizeof(key) is the size of a pointer, perhaps 2, 4 or 8. Instead, what is needed is the size of the allocation for array[i].data->key as in sz (see above) or strlen(key) + 1.
snprintf(array[i].data->key,sizeof(key),"%s",key); // bad

Related

What's wrong with my realloc doing on 2d-array

I am solving binary tree paths leet code programming question 257. I am having issue for one of the larger input where my code is getting segmentation fault. I suspect that there is an problem with my realloc but I am not able to figure it out.
Below is my approach:
Initially I started by dynamically allocating 80 bytes of memory of type char (80/8 = 10 rows)and storing the returned address to char **res variable.
char ** res = (char **)malloc(sizeof(char *) * sum);
I am calling findpath function recursively to find all the binary tree paths. Whenever one path is found , I dynamic allocate 100 bytes for each row index.
res[resIdx] = (char *)malloc(sizeof(char) * 100);
I have one global variable resIdx which points to the current row index where I copy the found binary tree path and increment the global variable resIdx.
if the resIdx becomes greater then total number of rows which was previously allocated then I do realloc of the memory but it looks like realloc is getting failed.
if (resIdx >= sum)
{
sum = sum + 10;
res = (char **)realloc(res,sizeof(char *) * sum); //Any issue here?
}
Can anyone please help me to figure out what's wrong I am doing in my code. Below is my full code
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int sum;
int resIdx;
void findpath (struct TreeNode* root, int *ls,int ls_idx,char **res);
char ** binaryTreePaths(struct TreeNode* root, int* returnSize){
if (root == NULL)
{
*returnSize = 0;
return NULL;
}
resIdx = 0;
sum = 10;
char ** res = (char **)malloc(sizeof(char *) * sum);
int ls[100];
findpath(root,&ls[0],0,res);
*returnSize = resIdx;
return &res[0];
}
void findpath (struct TreeNode* root, int *ls,int ls_idx,char **res)
{
char temp[100];
int l=0,i=0;
if (root->left == NULL && root->right == NULL)
{
ls[ls_idx] = root->val;
ls_idx+=1;
if (resIdx >= sum)
{
sum = sum + 10;
res = (char **)realloc(res,sizeof(char *) * sum);
}
res[resIdx] = (char *)malloc(sizeof(char) * 100);
while (i < ls_idx)
{
if (i==0)
{
l = l + sprintf(&temp[l], "%d", ls[i]);
}
else
{
l = l + sprintf(&temp[l], "->%d", ls[i]);
}
i++;
}
strcpy(res[resIdx],temp);
resIdx++;
return;
}
ls[ls_idx] = root->val;
if (root->left != NULL)
{
findpath(root->left,ls,ls_idx+1,res);
}
if (root->right != NULL)
{
findpath(root->right,ls,ls_idx+1,res);
}
return;
}
The last argument to your findPath function is declared as a char** type; thus, when you make the call findpath(root,&ls[0],0,res); in binaryTreePaths, where the res variable is a char** type, a copy of that pointer is passed to the findPath function (most likely, but not necessarily, by placing that copy on the stack).
Then, if reallocation is required, the res = (char **)realloc(res,sizeof(char *) * sum); line in that function overwrites the value in the passed copy and, at the same time (if the call is successful – vide infra), will (probably) invalidate (i.e. free) the memory referenced by the previous address in that res copy. Thus, when control returns to the calling binaryTreePaths function, its own version of res will not have been modified and will remain pointing to that (now invalid) memory.
So, in order for your findPath function to be able to modify the given res argument, that must be passed as a pointer – in this case, a pointer to a char**, which will be of type char***; then, when called, you will need to pass the address of the res variable in binaryTreePaths.
Note also that directly overwriting a pointer in a call to realloc, as you have done in the line of code quoted above is dangerous. This is because, should that call fail, then you have lost the original data pointer (it will have been overwritten with NULL) and error recovery will be very difficult. You should save the return value in a temporary variable and only replace your original if the call succeeds.
With the code you have provided, I cannot properly test for any other errors but, taking the points above in hand, the below is a possible fix. See also: Do I cast the result of malloc?
int sum;
int resIdx;
void findpath(struct TreeNode* root, int* ls, int ls_idx, char*** res); // Note last argument type!
char** binaryTreePaths(struct TreeNode* root, int* returnSize)
{
if (root == NULL) {
*returnSize = 0;
return NULL;
}
resIdx = 0;
sum = 10;
char** res = malloc(sizeof(char*) * sum);
int ls[100];
findpath(root, &ls[0], 0, &res); // Pass ADDRESS of res
*returnSize = resIdx;
return &res[0];
}
void findpath(struct TreeNode* root, int* ls, int ls_idx, char*** res)
{
char temp[100];
int l = 0, i = 0;
if (root->left == NULL && root->right == NULL) {
ls[ls_idx] = root->val;
ls_idx += 1;
if (resIdx >= sum) {
sum = sum + 10;
char** test = realloc(*res, sizeof(char*) * sum);
if (test == NULL) {
// Handle/signal error
return;
}
*res = test; // Only replace original if realloc succeeded!
}
(*res)[resIdx] = malloc(sizeof(char) * 100);
while (i < ls_idx) {
if (i == 0) {
l = l + sprintf(&temp[l], "%d", ls[i]);
}
else {
l = l + sprintf(&temp[l], "->%d", ls[i]);
}
i++;
}
strcpy((*res)[resIdx], temp);
resIdx++;
return;
}
ls[ls_idx] = root->val;
if (root->left != NULL) {
findpath(root->left, ls, ls_idx + 1, res);
}
if (root->right != NULL) {
findpath(root->right, ls, ls_idx + 1, res);
}
return;
}

How to fix copy of struct to array of structs segfault

I have the following two structs:
typedef struct {
char* key;
char* value;
} kvpair;
typedef struct {
kvpair ** array;
size_t length;
} kvarray;
And I want to copy new key and value pairs to the kvarray. I use realloc to allocate memory for each new item to be added to the kvpair array but struggling to work out how to copy the key and value.
If I do it like this:
kvs->array resized using realloc
// *** get segfault here!!! how to fix ***
kvs->array[kvs->length]->key = key;
kvs->array[kvs->length]->value = value;
But if I allocate memory separately for a kvpair* and do this way:
kvpair* kvp = malloc(sizeof(kvpair));
// copy key and value
// This below then works
kvs->array[kvs->length] = kvp;
// but there is a memory leak - or seems to be double allocation of memory for same thing
How to do this correctly?
The code is below (see // * get segfault here!!! how to fix * comment)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct {
char* key;
char* value;
} kvpair;
typedef struct {
kvpair ** array;
size_t length;
} kvarray;
kvarray * readKVs(const char** array, size_t length);
void freeKVs(kvarray * pairs);
int main() {
const char* things[] = { "wood=brown\n", "brick=red\n",
"grass=green", "hedge=green", "leaf=green" };
const size_t sz = sizeof(things) / sizeof(things[0]);
kvarray* kvs = readKVs(things, sz);
freeKVs(kvs);
}
kvarray * readKVs(const char** array, size_t length) {
kvarray* kvs = NULL;
for (size_t i = 0; i < length; ++i) {
const char* line = array[i];
if (kvs == NULL) {
kvs = malloc(sizeof(kvarray));
kvs->length = 0;
kvs->array = NULL;
}
char * found = strchr(line, '=');
if (found == NULL) {
// skip to next line
continue;
}
size_t len = strlen(line);
size_t pos = found - array[i];
char* value = NULL;
if (len > (pos + 1)) {
// non-blank value
// length of value is len - pos
value = malloc(len - (pos + 1));
strncpy(value, &line[pos + 1], (len - (pos + 1)) - 1);
// null terminate string
value[len - (pos + 1) - 1] = '\0';
printf("value:'%s'\n", value);
}
char* key = malloc(found - line + 1); // +1 for null terminator
strncpy(key, line, pos);
// remember strncpy bug!
key[found - line] = '\0'; // ensure null termination.
printf("key:'%s', length=%lu\n", key, strlen(key));
/*
// if I allocate an individual pair, then I am duplicating memory so should have to do this below
kvpair* kvp = malloc(sizeof(kvpair));
//kvpair kvp = {NULL, NULL};
printf("about to assign kvs->key = key\n");
kvp->key = key;
printf("about to assign kvs->value = value\n");
kvp->value = value;
*/
kvs->array = realloc(kvs->array, (kvs->length + 1) * sizeof(kvpair*));
// I want to be able to do this 2 lines below - but crashes
// *** get segfault here!!! how to fix ***
kvs->array[kvs->length]->key = key;
kvs->array[kvs->length]->value = value;
kvs->length++;
printf("kvs->length now=%lu\n", kvs->length);
}
return kvs;
}
void freeKVs(kvarray * pairs) {
if (pairs == NULL) {
return;
}
for (size_t i = 0; i < pairs->length; ++i) {
free(pairs->array[i]->key);
free(pairs->array[i]->value);
free(pairs->array[i]);
}
free(pairs);
}
When you do
kvs->array = realloc(kvs->array, (kvs->length + 1) * sizeof(kvpair*));
the contents of the new memory allocated will be indeterminate, it's not initialized. That means the next line
kvs->array[kvs->length]->key = key;
you will dereference an invalid pointer kvs->array[kvs->length]. That of course will lead to undefined behavior.
The solution is of course to make kvs->array[kvs->length] point somewhere valid, for example by doing
kvs->array[kvs->length] = malloc(sizeof(kvpair));

realloc: invalid checksum for freed object

I have an error using realloc to replace malloc.
This code below runs OK on my computer.
int vector_grow(Vector* vec) {
unsigned long newcap;
int * newarr;
if (0 == vec->cap) {
vec->arr = (int*)malloc(START_CAPACITY * sizeof(*vec->arr));
if (NULL == vec->arr)
return -1;
vec->cap = START_CAPACITY;
return 0;
}
newarr = malloc (newcap * sizeof(*vec->arr));
if (NULL == newarr)
return -1;
memcpy (newarr, vec->arr, vec->len * sizeof(*vec->arr));
free (vec->arr);
vec->arr = newarr;
vec->cap = newcap;
return 0;
}
I want to change the malloc to realloc, but the error occurs.
int vector_grow(Vector* vec) {
unsigned long newcap;
if (0 == vec->cap) {
vec->arr = (int*)malloc(START_CAPACITY * sizeof(*vec->arr));
if (NULL == vec->arr)
return -1;
vec->cap = START_CAPACITY;
return 0;
}
newcap = 2 * vec->cap;
if ((vec->arr = (int*)realloc(vec->arr, newcap * sizeof(int))) == NULL)
return -1;
return 0;
}
It says
malloc: *** error for object 0x7fca64c02598: incorrect checksum for freed object - object was probably modified after being freed.
I don't know any difference between those two snippets of code, if you know what causes the error, please tell me! Thank you very much!
Bug in missing vec->cap = in updated code certainly contribute to various calls to malloc() and calling code's misuse of data.
int vector_grow(Vector* vec) {
unsigned long newcap;
if (0 == vec->cap) {
... // not important to show the bug
}
newcap = 2 * vec->cap;
if ((vec->arr = (int*)realloc(vec->arr, newcap * sizeof(int))) == NULL)
return -1;
// Add missing update
vec->cap = newcap;
return 0;
}
Also better to test for allocation success
void *p = realloc(vec->arr, sizeof *(vec->arr) * newcap);
if (p == NULL) {
return -1;
}
vec->arr = p;
vec->cap = newcap;
The only scenario where I can imagine such error message is when you actually modify the pointer, for example
int *x = malloc(2 * sizeof *x);
if (x != NULL) {
x = x + 1;
free(x);
}
The pointer that MUST be passed to free() MUST had been returned by malloc()/calloc()/realloc(), passing any other pointer including a pointer to the same data but at a different position like x in the example above is undefined behavior.

Create a safe nullpointer in regards to memory leaks

Look at this:
char (*options)[MAXLEN];
char *ptr;
ptr = strtok(input, " \r\n");
if(!ptr)
continue;
int i = 0;
optionen = malloc(sizeof(*options));
if(!options)
die("malloc");
while(ptr){
if(i > 0){
options = realloc(options, (i+1)*sizeof(*options));
if(!options)
die("malloc");
}
strcpy(options[i], ptr);
if(!options[i])
die("strcpy");
ptr = strtok(NULL, " \r\n");
i++;
}
/*create another entry options[i] that is a nullpointer*/
Purpose is that the exec(3) command requires a nullpointer as the last entry to the *options[] array to work properly.
Problem: How can I add another entry to the array that is a NULL - pointer? I understand that I cannot allocate another options[i] and set it to NULL because some guy on stackoverflow told me to never do that (memory leak).
Note: input is some array that contains some commandline - input(char input[MAXLEN]; ) and die() just calls perror() and then exit()
Whoever told you that you "cannot allocate another options[i] and set it to NULL" was wrong. That is exactly what you do.
However, you have bugs in your code, and the bugs suggest that you don't understand what it means to "allocate another options[i]."
char (*options)[MAXLEN]; /* This is wrong */
char *ptr;
ptr = strtok(input, " \r\n");
if(!ptr)
continue;
int i = 0;
optionen = malloc(sizeof(*options));
if(!options)
die("malloc");
while(ptr){
if(i > 0){
options = realloc(options, (i+1)*sizeof(*options));
if(!options)
die("malloc");
}
strcpy(options[i], ptr); /* This is also wrong */
if(!options[i])
die("strcpy");
ptr = strtok(NULL, " \r\n");
i++;
}
First off, char (*options)[MAXLEN] is the wrong kind of array, and an entry in it cannot be set to NULL. You need instead char **options.
Second off, realloc operation that you are already doing is, in fact, allocating more options[i] slots. But you also need to allocate space for the strings themselves (but not for the NULL). You do that with strdup.
options[i] = strdup(ptr); /* Instead of the "also wrong" line */
(Depending on what input is, you might be able to get away with just using the string pointers strtok returns, but without seeing more of your code I can't be sure that's safe.)
And then, after the loop, you simply set the final options[i] slot to NULL and you're done.
I would structure the loop a bit differently, like this:
char **options;
char *ptr;
size_t i, asize;
ptr = strtok(input, " \t\r\n");
if (!ptr) continue;
options = 0;
i = 0;
asize = 2;
do {
while (i >= asize) {
asize *= 2;
options = xreallocarray(options, asize, sizeof(char *));
}
options[i++] = xstrdup(ptr);
ptr = strtok(0, " \t\r\n");
} while (ptr);
while (i >= asize) {
asize *= 2;
options = xreallocarray(options, asize, sizeof(char *));
}
options[i] = 0;
execve(options[0], options, environ);
die("execve");
The functions xreallocarray and xstrdup are nonstandard, but they should be in your bag of simple functions that you add to every program that you write. Here are their definitions. They use the die() function you already have.
void *
xreallocarray(void *optr, size_t nmemb, size_t size)
{
/* s1*s2 <= SIZE_MAX if both s1 < K and s2 < K where K = sqrt(SIZE_MAX+1) */
const size_t MUL_NO_OVERFLOW = ((size_t)1) << (sizeof(size_t) * 4);
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
nmemb > 0 && SIZE_MAX / nmemb < size) {
errno = ENOMEM;
die("malloc");
}
void *rv = realloc(optr, size * nmemb);
if (!rv)
die("malloc");
return rv;
}
char *
xstrdup(const char *s)
{
size_t n = strlen(s) + 1;
char *rv = malloc(n);
if (!rv)
die("malloc");
memcpy(rv, s, n);
return rv;
}
My suggestion: Create another array as follows and use it in the call to exec.
char** args = malloc((i+1)*sizeof(*args));
for (int j = 0; j < i; ++j )
{
args[j] = options[j];
}
args[i] = NULL;
execvp(..., args);

C string append

I'm looking for an efficient method for appending multiple strings.
The way it should work is C++ std::string::append or JAVA StringBuffer.append.
I wrote a function which actually reallocs previous source pointer and does strcat.
I believe this is not an efficient method as compiler may implement this free and malloc.
Other way I could think of (like std::vector) is allocate memory in bulk (1KB for eg) and do strcpy. In that case every append call will check if the total required allocation is more than (1200 bytes) the amount allocated in bulk, realloc to 2KB. But in that case there will be some memory wasted.
I'm looking for a balance between the above but the preference is performance.
What other approaches are possible. Please suggest.
I would add each string to a list, and add the length of each new string to a running total. Then, when you're done, allocate space for that total, walk the list and strcpy each string to the newly allocated space.
The classical approach is to double the buffer every time it is too small.
Start out with a "reasonable" buffer, so you don't need to do realloc()s for sizes 1, 2, 4, 8, 16 which are going to be hit by a large number of your strings.
Starting out at 1024 bytes means you will have one realloc() if you hit 2048, a second if you hit 4096, and so on. If rampant memory consumption scares you, cap the growth rate once it hits something suitably big, like 65536 bytes or whatever, it depends on your data and memory tolerance.
Also make sure you buffer the current length, so you can do strcpy() without having to walk the string to find the length, first.
Sample function to concatenate strings
void
addToBuffer(char **content, char *buf) {
int textlen, oldtextlen;
textlen = strlen(buf);
if (*content == NULL)
oldtextlen = 0;
else
oldtextlen = strlen(*content);
*content = (char *) realloc( (void *) *content, (sizeof(char)) * (oldtextlen+textlen+1));
if ( oldtextlen != 0 ) {
strncpy(*content + oldtextlen, buf, textlen + 1);
} else {
strncpy(*content, buf, textlen + 1);
}
}
int main(void) {
char *content = NULL;
addToBuffer(&content, "test");
addToBuffer(&content, "test1");
}
I would do something like this:
typedef struct Stringbuffer {
int capacity; /* Maximum capacity. */
int length; /* Current length (excluding null terminator). */
char* characters; /* Pointer to characters. */
} Stringbuffer;
BOOL StringBuffer_init(Stringbuffer* buffer) {
buffer->capacity = 0;
buffer->length = 0;
buffer->characters = NULL;
}
void StringBuffer_del(Stringbuffer* buffer) {
if (!buffer)
return;
free(buffer->characters);
buffer->capacity = 0;
buffer->length = 0;
buffer->characters = NULL;
}
BOOL StringBuffer_add(Stringbuffer* buffer, char* string) {
int len;
int new_length;
if (!buffer)
return FALSE;
len = string ? strlen(string) : 0;
if (len == 0)
return TRUE;
new_length = buffer->length + len;
if (new_length >= new_capacity) {
int new_capacity;
new_capacity = buffer->capacity;
if (new_capacity == 0)
new_capacity = 16;
while (new_length >= new_capacity)
new_capacity *= 2;
new_characters = (char*)realloc(buffer->characters, new_capacity);
if (!new_characters)
return FALSE;
buffer->capacity = new_capacity;
buffer->characters = new_characters;
}
memmove(buffer->characters + buffer->length, string, len);
buffer->length = new_length;
buffer->characters[buffer->length] = '\0';
return TRUE;
}

Resources