Memory Allocation Char* and Char[]

char* in struct memory allocation

You have two option for allocating memory for struct members:

1- Static memory allocation:

struct _setup {
char selfName[32];
int8_t zone;
char selfSSID[32];
char selfWPA2[32];
}

Static allocation recommended if your allocation size is short and you know that size at compile time. otherwise use dynamic allocation.

static allocation is faster than dynamic allocation. but if you want to create huge count of object from this struct you may get Stackoverflow exception. becasue you may utilize all stack space. (if your real array size is about 32, Stackoverflow exception is improbable)

2- Dynamic Allocation :

struct _setup {
char* selfName;
int8_t zone;
char* selfSSID;
char* selfWPA2;
_setup()
{
selfName= (char*)malloc(sizeof(char)*32);
selfSSID= (char*)malloc(sizeof(char)*32);
selfWPA2= (char*)malloc(sizeof(char)*32);
}
~_setup()
{
free(selfName);
free(selfSSID);
free(selfWPA2);
}
}

you can use Dynamic allocation with malloc or new operators. but you should remember that you must free() these dynamic memories after using them. I placed free in distructor, you can put those everywhere you want.

Memory Allocation char* and char[]

The first one creates a pointer variable (four or eight bytes of storage depending on the platform) and stores a location of a string literal there, The second one creates an array of six characters (including zero string terminator byte) and copies the literal there.

You should get a compiler warning on the first line since the literal is const.

Memory Allocation of char** array in C

There are a number of bugs.

This won't compile (e.g. errorp instead of perror).

cap is too small to contain a line. Better to use (e.g.) char cap[1000];

Doing a preallocate of each arglst[i] once before the main loop is problematic. One of the cells has to get a NULL value so it works with execvp. However, doing so would cause a memory leak. The solution is to use strdup inside the strtok loop so that cells are only allocated when needed.

Also, because arglst[i] is set only once during initialization, doing a loop with free near the bottom causes UB [accessing the buffer after being freed]. This is fixed with the use of strdup below.

The commented out code references variables (e.g. cmd and cap) that should not be relied upon. At that point, cmd will be NULL, causing a segfault.

The return 0; is placed incorrectly. Only one iteration (and thus only one command) will be executed.

The final free of arglst (e.g. free(arglst)) is done inside the outer loop, so referencing it on the second iteration is UB.

There are a few more issues [annotated below]


Here's a refactored version. It fixes the bugs and is heavily annotated.

I've used the preprocessor to show old/original code vs new/fixed code:

#if 0
// old code
#else
// new code
#endif

Likewise, using #if 1 for purely new code.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#if 1
#include <sys/wait.h>
#endif

int
main(void)
{
// NOTE/BUG: this must be large enough to contain a command line
#if 0
char cap[] = "";
#else
char cap[1000];
#endif
char *cmd;

const int MAX_ARGS = 16;
char **arglst = malloc(MAX_ARGS * sizeof(*arglst));

// NOTE/BUG: because we need to add a NULL terminator, don't preallocate the
// elements -- we'll leak memory
#if 0
const int MAX_ARG_SIZE = 256;
for (int i = 0; i < MAX_ARGS; i++) {
arglst[i] = (char *) malloc(MAX_ARG_SIZE * sizeof(char));
}
#endif

pid_t cpid;

// implement ls, cd, mkdir, chdir, rm, rmdir
while (1) {
printf("Enter a command: ");
// NOTE/BUG: this didn't work too well
#if 0
scanf("%[^\n]s", cap);
#else
fgets(cap,sizeof(cap),stdin);
cap[strcspn(cap,"\n")] = 0;
#endif

int index = 0;

cmd = strtok(cap, " ");
while (cmd != NULL) {
// NOTE/BUG: we should strdup dynamically rather than preallocate -- otherwise,
// we leak memory when we set the necessary NULL pointer below
#if 0
strcpy(arglst[index], cmd);
#else
arglst[index] = strdup(cmd);
#endif
cmd = strtok(NULL, " ");
index++;
}

// NOTE/FIX: we have to add a NULL terminator before passing to execvp
#if 1
arglst[index] = NULL;
#endif

for (int i = 0; i < index; i++) {
printf("%s\n", arglst[i]);
}
printf("%d\n", index);

// NOTE/BUG: we can't [shouldn't] rely on cap here
#if 0
if (strcmp(cap, "quit") == 0)
exit(EXIT_SUCCESS);
#else
if (strcmp(arglst[0], "quit") == 0)
exit(EXIT_SUCCESS);
#endif

if ((cpid = fork()) == -1)
perror("fork()");
else if (cpid == 0) {
// NOTE/BUG: cmd will be NULL here
#if 0
if (execvp(cmd, arglst) == -1) {
errorp("cmd error");
exit(EXIT_FAILURE);
}
#else
if (execvp(arglst[0], arglst) == -1) {
perror("cmd error");
exit(EXIT_FAILURE);
}
#endif
// NOTE/BUG: this will never be executed
#if 0
exit(EXIT_SUCCESS);
#endif
}
else {
cpid = wait(NULL);
// NOTE/BUG -- cmd is NULL and this serves no purpose
#if 0
strcpy(cmd, "/bin/");
#endif
}

// NOTE/BUG: in the _old_ code that did a single preallocate of these cells
// _before_ the loop, freeing them here is wrong -- they would never be
// reallocated because -- the fix using strdup alleviates the issue
for (int i = 0; i < index; i++) {
free(arglst[i]);
}

// NOTE/BUG: freeing this is wrong because we do the allocation only _once_
// above the outer loop
#if 0
free(arglst);
#endif

// NOTE/BUG -- this should be placed at the end to allow multiple commands --
// here it stops after the first command is input
#if 0
return 0;
#endif
}

// NOTE/FIX: correct placement for the above
#if 1
free(arglst);

return 0;
#endif
}

Here's that version cleaned up so that only the fixed code remains:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

int
main(void)
{
char cap[1000];
char *cmd;

const int MAX_ARGS = 16;
char **arglst = malloc(MAX_ARGS * sizeof(*arglst));

pid_t cpid;

// implement ls, cd, mkdir, chdir, rm, rmdir
while (1) {
printf("Enter a command: ");
fgets(cap,sizeof(cap),stdin);
cap[strcspn(cap,"\n")] = 0;

int index = 0;

cmd = strtok(cap, " ");
while (cmd != NULL) {
arglst[index] = strdup(cmd);
cmd = strtok(NULL, " ");
index++;
}

arglst[index] = NULL;

for (int i = 0; i < index; i++) {
printf("%s\n", arglst[i]);
}
printf("%d\n", index);

if (strcmp(arglst[0], "quit") == 0)
exit(EXIT_SUCCESS);

if ((cpid = fork()) == -1)
perror("fork()");
else if (cpid == 0) {
if (execvp(arglst[0], arglst) == -1) {
perror("cmd error");
exit(EXIT_FAILURE);
}
}
else {
cpid = wait(NULL);
}

for (int i = 0; i < index; i++) {
free(arglst[i]);
}
}

free(arglst);

return 0;
}

Note that the above does not check for the number of actual arguments exceeding MAX_ARGS.

While we could add that check, a better way is to use realloc on arglst to dynamically increase it, so an arbitrary limit on the number of arguments isn't needed

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

int
main(void)
{
char cap[1000];
char *cmd;

char **arglst = NULL;
int argmax = 0;

pid_t cpid;

// implement ls, cd, mkdir, chdir, rm, rmdir
while (1) {
printf("Enter a command: ");
fgets(cap,sizeof(cap),stdin);
cap[strcspn(cap,"\n")] = 0;

int index = 0;

cmd = strtok(cap, " ");
while (cmd != NULL) {
if (index >= argmax) {
argmax += 10;
arglst = realloc(arglst,sizeof(*arglst) * (argmax + 1));
}

arglst[index] = strdup(cmd);
cmd = strtok(NULL, " ");

index++;
}

arglst[index] = NULL;

for (int i = 0; i < index; i++) {
printf("%s\n", arglst[i]);
}
printf("%d\n", index);

if (strcmp(arglst[0], "quit") == 0)
exit(EXIT_SUCCESS);

if ((cpid = fork()) == -1)
perror("fork()");
else if (cpid == 0) {
if (execvp(arglst[0], arglst) == -1) {
perror("cmd error");
exit(EXIT_FAILURE);
}
}
else {
cpid = wait(NULL);
}

for (int i = 0; i < index; i++) {
free(arglst[i]);
}
}

free(arglst);

return 0;
}

The original code used malloc and/or strdup on the individual elements of arglst (e.g. arglst[i]).

This makes the code general enough to be used in more complex scenarios. But, as the code is written, the malloc/strdup for the individual elements really isn't necessary.

This is because the cells are fully used [up] at the bottom of the main loop, so we don't need to save them.

We can reuse the cap buffer space on each loop iteration because we do not need any tokens to live on iteration to iteration.

We can simply store the return value from strtok and simplify the code:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

int
main(void)
{
char cap[1000];
char *cmd;

char **arglst = NULL;
int argmax = 0;

pid_t cpid;

// implement ls, cd, mkdir, chdir, rm, rmdir
while (1) {
printf("Enter a command: ");
fgets(cap,sizeof(cap),stdin);
cap[strcspn(cap,"\n")] = 0;

int index = 0;

cmd = strtok(cap, " ");
while (cmd != NULL) {
if (index >= argmax) {
argmax += 10;
arglst = realloc(arglst,sizeof(*arglst) * (argmax + 1));
}

arglst[index] = cmd;
cmd = strtok(NULL, " ");

index++;
}

arglst[index] = NULL;

for (int i = 0; i < index; i++) {
printf("%s\n", arglst[i]);
}
printf("%d\n", index);

if (strcmp(arglst[0], "quit") == 0)
exit(EXIT_SUCCESS);

if ((cpid = fork()) == -1)
perror("fork()");
else if (cpid == 0) {
if (execvp(arglst[0], arglst) == -1) {
perror("cmd error");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
else {
cpid = wait(NULL);
}
}

free(arglst);

return 0;
}

C++ dynamic memory allocation (char[] and int[])

The operator overloads << and >> have special overloads for const char* and char* respectively, because those are null-terminated C-style strings. They are
treated diifferently than other pointers/other arrays.

Here's a little comparison of the semantics used:

cin >> str;

means "read a null terminated string into an array, where str is the pointer to the first element".

cout << str; 

means "Print a null terminated string, where str is the pointer to the first element".


However there are such semantics for other pointer types like int*.

cin >> p;

wont work, there is no such thing as "reading an array of ints", or "reading a pointer", while

cin >> *p; 

works and means "read a single integer and store it in the value of p", that is, the first element in the array get's modified.

cout << p;

means "print the value of the pointer", again, because there are no special semantics for int* like "Print array of ints". On the other hand

cout << *p;

meanse "Print one integer, that is the value of p", that is, the first element in the array get's printed.

What does allocating memory of size char * do?

The following code

char **str = malloc(sizeof(char *) * 5);

allocates memory for 5-consecutive elements of type char *, i.e.: pointer to char.

You could then allocate N-consecutive elements of type char and assign their addresses to these pointers:

for (int i = 0; i < 5; i++)
str[i] = malloc(sizeof(char) * N));

Dynamic memory allocation for char***

This is not so hard, you just need to keep your head clear.

First off, keep the general pattern in mind: To allocate an array of n elements of type T, use:

T * p = malloc(n * sizeof(T));

Then initialize the values p[i] for i in the range [0, n), then de-initialize the elements when you're done, if necessary, and then release the array allocation with:

free(p);

Now just do this recursively for your array of arrays of strings, bearing in mind that a string is an array of characters, m rows of n columns of strings:

// allocate the table (the row pointers)
char *** tas = malloc(m * sizeof(char**));

for (size_t i = 0; i != m; ++i)
{
// allocate each row (the row cells)
tas[i] = malloc(n * sizeof(char*));
for (size_t j = 0; j != n; ++j)
{
// initialize the row cell by allocating the string
tas[i][j] = /* allocate string */
}
}

And on the way back, to free everything:

for (size_t i = 0; i != m; ++i)
{
for (size_t j = 0; j != n; ++j)
{
free(tas[i][j]); // free the string
}

free(tas[i]); // free the row
}

free(tas); // free the table

Memory allocation of nested char pointer

I would like to use strcpy to put a string to the location where char *str is pointing. But as far as I know that does not work because there is no allocated memory where *str is pointing to, right?

Right.

My question would be, how do I accomplish that without changing the given typedef bson_value_t? Do I need to allocate memory for any one bson_value_t that I initialized?

No, you do not (for this purpose) need to dynamically allocate memory for any bson_value_t. The space, if any, to which any of those value.v_utf8.str members point will be external to the the bson_value_t, so arranging for that memory is a separate consideration.

As far as the data structure itself is concerned, the string data could be dynamically allocated, or they could be statically allocated (such as the contents of a string literal), or they could even be a locally-declared array (automatically allocated), though this last will present issues if the lifetime of the string data is shorter than that of the bson_value_t.

However, do make sure that you know what assumptions are made by the library from which you are drawing this. For example, if any of its functions assume that they can reallocate space to provide for lengthening the string, or if they assume that they can modify the contents in place, then such assumptions affect what kind of storage you need to provide.



Related Topics



Leave a reply



Submit