Declaring Static Character Arrays (strings)

When you know (or have a reasonable idea how large your array needs to be, you can simply declare an array of sufficient size to handle your input (i.e. if you names are no longer than 25 characters, then you could safely declare name[26]. For strings, you always need at minimum the number of chars to store + 1 (for the null-terminating character).

If there may be a few characters more than 25, there's nothing wrong with declaring your array few bytes longer than needed to protect against accidental writing beyond the end of the array. Say name[32].

Let's declare an array of 5-characters below and look at how the information is stored in memory.

char name[5] = {0}; /* always initialize your arrays */

The above declaration creates an array of 5-contiguous bytes on the stack for your use. e.g., you can visualize the 5-bytes of memory initialized to zero as follows:

        +---+---+---+---+---+
 name   | 0 | 0 | 0 | 0 | 0 |
        +---+---+---+---+---+
          \
           'name' holds the starting address for
            the block of memory that is the array
            (i.e. the address of the first element
             like: 0x7fff050bf3d0)

Note: when using name to hold a 'character string', the actual string can be no longer than 4 chars because you must end a sting with a null-terminating character, which is the null-character '\0' (or simply numeric 0, both are equivalent)

To store information in name, you can either do it by assigning characters one-at-a-time:

name[0] = 'J';
name[1] = 'o';
name[2] = 'h';
name[3] = 'n';
name[4] =  0;   /* null-terminate. note: this was already done by initialization */

In memory you now have:

        +---+---+---+---+---+
 name   | J | o | h | n | 0 |    /* you actually have the ASCII value for */
        +---+---+---+---+---+    /* each letter stored in the elements    */

Of course, nobody assigns one character at a time in this manner. You options are many, using one of the functions provided by C, e.g. strcpy, strncpy, memcpy, or by reading information from a file stream, or file descriptor with fgets, getline, or by using a simple loop and index variable to do the assignment, or by using one of the string formatting functions, e.g. sprintf, etc... For example you can accomplish the same thing with any of the following:

/* direct copy */
strcpy (name, "John");
strncpy (name, "John", 5);
memcpy (name, "John", sizeof "John");  /* include copy of the terminating char */

/* read from stdin into name */
printf ("Please enter a name (4 char max): ");
scanf ("%[^\n]%*c", name);

Note: above with strncpy, if you had NOT initialized all element to 0 (the last of which will serve as your null-terminating character, and then used strncpy (name, "John", 4); you would need to manually terminate the string with name[4] = 0;, otherwise you would not have a valid string (you would have an unterminated array of chars which would lead to undefined behavior if you used name where a string was expected.)

If you do not explicitly understand this STOP, go read and understand what a null-terminated string is and how it differs from an array of characters. Seriously, stop now and go learn, it is that fundamental to C. (if it doesn't end with a null-terminating character - it isn't a c-string.

What if I don't know how many characters I need to store?

Dynamic Allocations of Character Strings

When you do not know how many characters you need to store (or generally how many of whatever data type), the normal approach is to declare a pointer to type, and then allocate a reasonably anticipated amount of memory (just based on your best understanding of what you are dealing with), and then reallocate to add additional memory as required. There is no magic to it, it is just a different way of telling the compiler how to manage the memory. Just remember, when you allocate the memory, you own it. You are responsible for (1) preserving a pointer to the beginning address of the memory block (so it can be freed later); and (2) freeing the memory when you are done with it.

A simple example will help. Most of the memory allocation/free functions are declared in stdlib.h.

char *name = NULL;  /* declare a pointer, and initialize to NULL */

name = malloc (5 * sizeof *name); /* allocate a 5-byte block of memory for name */

if (!name) {    /* validate memory was allocated -- every time */
    fputs ("error: name allocation failed, exiting.", stderr);
    exit (EXIT_FAILURE);
}

/* Now use name, just as you would the statically declared name above */
strncpy (name, "John", 5);

printf (" name contains: %s\n", name);

free (name);    /* free memory when no longer needed.
                   (if reusing name, set 'name = NULL;') 
                 */

Note: malloc does NOT initialize the contents of the memory it allocates. If you want to initialize your new block of memory with zero (as we did with the static array), then use calloc instead of malloc. You can also use malloc and then call memset as well.

What happens if I allocate memory, then need More?

As mentioned above discussing dynamic memory, the general scheme is to allocate a reasonable anticipated amount, then realloc as required. You use realloc to reallocate the original block of memory created by malloc. realloc essentially creates a new block of memory, copies the memory from your old block to the new, and then frees the old block of memory. Since the old block of memory is freed, you want to use a temporary pointer for reallocation. If reallocation fails, you still have your original block of memory available to you.

You are free to add as little or as much memory as you like at any call to realloc. The standard scheme usually seen is to start with some initial allocation, then reallocate twice that amount each time you run out. (the means you need to keep track of how much memory is currently allocated).

To sew this up, let's end with a simple example that simply reads a string of any length as the first argument to the program (use "quotes" if your string contains whitespace). It will then allocates space to hold the string, then reallocate to append more text to the end of the original string. Finally it will free all memory in use before exit:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (int argc, char **argv) {

    if (argc < 2) { /* validate input */
        fprintf (stderr, "error: insufficient input.  usage: %s \"name\"\n",
                 argv[0]);
        return 1;
    }

    size_t len = strlen (argv[1]);  /* length of input  */
    size_t sz_mem = len + 1;        /* memory required  */

    char *name = malloc (sz_mem * sizeof *name);  /* allocate memory for name */

    if (!name) {    /* validate memory created successfully or throw error */
        fputs ("error: name allocation failed, exiting.", stderr);
        return 1;
    }

    printf ("\n allocated %zu bytes of memory for 'name'\n", sz_mem);
    memset (name, 0, sz_mem); /* initialize memory to zero (optional) */

    strncpy (name, argv[1], sz_mem);  /* copy the null-terminator as well */
    printf (" name: '%s' (begins at address: %p)\n", name, name);

    /* realloc - make name twice as big */
    void *tmp = realloc (name, 2 * sz_mem);  /* use a temporary pointer */
    if (!tmp) {                              /* check realloc succeeded */
        fprintf (stderr, "error: virtual memory exhausted, realloc 'name'\n");
        return 1;
    }
    memset (tmp + sz_mem, 0, sz_mem * sizeof *name); /* zero new memory */
    name = tmp;         /* assign new block to name       */
    sz_mem += sz_mem;   /* update current allocation size */

    printf (" reallocated 'name' to %zu bytes\n", sz_mem);
    strncat (name, " reallocated", sizeof " reallocated");

    printf ("\n final name : '%s'\n\n", name);

    free (name);

    return 0;
}

Use/Output

$ ./bin/arraybasics "John Q. Public"

 allocated 15 bytes of memory for 'name'
 name: 'John Q. Public' (begins at address: 0xf17010)
 reallocated 'name' to 30 bytes

 final name : 'John Q. Public reallocated'

Memory Check

When you dynamically allocate memory, it is up to you to validate you are using the memory correctly and that you track and free all the memory you allocate. Use a memory error checker like valgrind to veryify your memory use is correct. (there is no excuse not to, it is dead-bang-simple to do) Just type valgrind yourprogramexe

$ valgrind ./bin/arraybasics "John Q. Public"
==19613== Memcheck, a memory error detector
==19613== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==19613== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==19613== Command: ./bin/arraybasics John\ Q.\ Public
==19613==

 allocated 15 bytes of memory for 'name'
 name: 'John Q. Public' (begins at address: 0x51e0040)
 reallocated 'name' to 30 bytes

 final name : 'John Q. Public reallocated'

==19613==
==19613== HEAP SUMMARY:
==19613==     in use at exit: 0 bytes in 0 blocks
==19613==   total heap usage: 2 allocs, 2 frees, 45 bytes allocated
==19613==
==19613== All heap blocks were freed -- no leaks are possible
==19613==
==19613== For counts of detected and suppressed errors, rerun with: -v
==19613== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

In the output, the following lines are of particular significance:

==19613== HEAP SUMMARY:
==19613==     in use at exit: 0 bytes in 0 blocks
==19613==   total heap usage: 2 allocs, 2 frees, 45 bytes allocated

This tells you that all memory allocated during your program has been properly freed. (make sure you close all open file streams, they are dynamically allocated as well).

Of equal importance is the ERROR SUMMARY:

==19613== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

There are no errors in the memory use. If you attempt to read, write or free memory from a location outside your block, or from an unitialized location or that would leave other memory unreachable, that information will show as an error.

(the suppressed: 2 from 2 just relate to additional debug libraries not present on my system)

This ended up longer than intended, but if it helps, it was worth it. Good luck.

Answer from David C. Rankin on Stack Overflow
Top answer
1 of 5
48

Declaring Static Character Arrays (strings)

When you know (or have a reasonable idea how large your array needs to be, you can simply declare an array of sufficient size to handle your input (i.e. if you names are no longer than 25 characters, then you could safely declare name[26]. For strings, you always need at minimum the number of chars to store + 1 (for the null-terminating character).

If there may be a few characters more than 25, there's nothing wrong with declaring your array few bytes longer than needed to protect against accidental writing beyond the end of the array. Say name[32].

Let's declare an array of 5-characters below and look at how the information is stored in memory.

char name[5] = {0}; /* always initialize your arrays */

The above declaration creates an array of 5-contiguous bytes on the stack for your use. e.g., you can visualize the 5-bytes of memory initialized to zero as follows:

        +---+---+---+---+---+
 name   | 0 | 0 | 0 | 0 | 0 |
        +---+---+---+---+---+
          \
           'name' holds the starting address for
            the block of memory that is the array
            (i.e. the address of the first element
             like: 0x7fff050bf3d0)

Note: when using name to hold a 'character string', the actual string can be no longer than 4 chars because you must end a sting with a null-terminating character, which is the null-character '\0' (or simply numeric 0, both are equivalent)

To store information in name, you can either do it by assigning characters one-at-a-time:

name[0] = 'J';
name[1] = 'o';
name[2] = 'h';
name[3] = 'n';
name[4] =  0;   /* null-terminate. note: this was already done by initialization */

In memory you now have:

        +---+---+---+---+---+
 name   | J | o | h | n | 0 |    /* you actually have the ASCII value for */
        +---+---+---+---+---+    /* each letter stored in the elements    */

Of course, nobody assigns one character at a time in this manner. You options are many, using one of the functions provided by C, e.g. strcpy, strncpy, memcpy, or by reading information from a file stream, or file descriptor with fgets, getline, or by using a simple loop and index variable to do the assignment, or by using one of the string formatting functions, e.g. sprintf, etc... For example you can accomplish the same thing with any of the following:

/* direct copy */
strcpy (name, "John");
strncpy (name, "John", 5);
memcpy (name, "John", sizeof "John");  /* include copy of the terminating char */

/* read from stdin into name */
printf ("Please enter a name (4 char max): ");
scanf ("%[^\n]%*c", name);

Note: above with strncpy, if you had NOT initialized all element to 0 (the last of which will serve as your null-terminating character, and then used strncpy (name, "John", 4); you would need to manually terminate the string with name[4] = 0;, otherwise you would not have a valid string (you would have an unterminated array of chars which would lead to undefined behavior if you used name where a string was expected.)

If you do not explicitly understand this STOP, go read and understand what a null-terminated string is and how it differs from an array of characters. Seriously, stop now and go learn, it is that fundamental to C. (if it doesn't end with a null-terminating character - it isn't a c-string.

What if I don't know how many characters I need to store?

Dynamic Allocations of Character Strings

When you do not know how many characters you need to store (or generally how many of whatever data type), the normal approach is to declare a pointer to type, and then allocate a reasonably anticipated amount of memory (just based on your best understanding of what you are dealing with), and then reallocate to add additional memory as required. There is no magic to it, it is just a different way of telling the compiler how to manage the memory. Just remember, when you allocate the memory, you own it. You are responsible for (1) preserving a pointer to the beginning address of the memory block (so it can be freed later); and (2) freeing the memory when you are done with it.

A simple example will help. Most of the memory allocation/free functions are declared in stdlib.h.

char *name = NULL;  /* declare a pointer, and initialize to NULL */

name = malloc (5 * sizeof *name); /* allocate a 5-byte block of memory for name */

if (!name) {    /* validate memory was allocated -- every time */
    fputs ("error: name allocation failed, exiting.", stderr);
    exit (EXIT_FAILURE);
}

/* Now use name, just as you would the statically declared name above */
strncpy (name, "John", 5);

printf (" name contains: %s\n", name);

free (name);    /* free memory when no longer needed.
                   (if reusing name, set 'name = NULL;') 
                 */

Note: malloc does NOT initialize the contents of the memory it allocates. If you want to initialize your new block of memory with zero (as we did with the static array), then use calloc instead of malloc. You can also use malloc and then call memset as well.

What happens if I allocate memory, then need More?

As mentioned above discussing dynamic memory, the general scheme is to allocate a reasonable anticipated amount, then realloc as required. You use realloc to reallocate the original block of memory created by malloc. realloc essentially creates a new block of memory, copies the memory from your old block to the new, and then frees the old block of memory. Since the old block of memory is freed, you want to use a temporary pointer for reallocation. If reallocation fails, you still have your original block of memory available to you.

You are free to add as little or as much memory as you like at any call to realloc. The standard scheme usually seen is to start with some initial allocation, then reallocate twice that amount each time you run out. (the means you need to keep track of how much memory is currently allocated).

To sew this up, let's end with a simple example that simply reads a string of any length as the first argument to the program (use "quotes" if your string contains whitespace). It will then allocates space to hold the string, then reallocate to append more text to the end of the original string. Finally it will free all memory in use before exit:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main (int argc, char **argv) {

    if (argc < 2) { /* validate input */
        fprintf (stderr, "error: insufficient input.  usage: %s \"name\"\n",
                 argv[0]);
        return 1;
    }

    size_t len = strlen (argv[1]);  /* length of input  */
    size_t sz_mem = len + 1;        /* memory required  */

    char *name = malloc (sz_mem * sizeof *name);  /* allocate memory for name */

    if (!name) {    /* validate memory created successfully or throw error */
        fputs ("error: name allocation failed, exiting.", stderr);
        return 1;
    }

    printf ("\n allocated %zu bytes of memory for 'name'\n", sz_mem);
    memset (name, 0, sz_mem); /* initialize memory to zero (optional) */

    strncpy (name, argv[1], sz_mem);  /* copy the null-terminator as well */
    printf (" name: '%s' (begins at address: %p)\n", name, name);

    /* realloc - make name twice as big */
    void *tmp = realloc (name, 2 * sz_mem);  /* use a temporary pointer */
    if (!tmp) {                              /* check realloc succeeded */
        fprintf (stderr, "error: virtual memory exhausted, realloc 'name'\n");
        return 1;
    }
    memset (tmp + sz_mem, 0, sz_mem * sizeof *name); /* zero new memory */
    name = tmp;         /* assign new block to name       */
    sz_mem += sz_mem;   /* update current allocation size */

    printf (" reallocated 'name' to %zu bytes\n", sz_mem);
    strncat (name, " reallocated", sizeof " reallocated");

    printf ("\n final name : '%s'\n\n", name);

    free (name);

    return 0;
}

Use/Output

$ ./bin/arraybasics "John Q. Public"

 allocated 15 bytes of memory for 'name'
 name: 'John Q. Public' (begins at address: 0xf17010)
 reallocated 'name' to 30 bytes

 final name : 'John Q. Public reallocated'

Memory Check

When you dynamically allocate memory, it is up to you to validate you are using the memory correctly and that you track and free all the memory you allocate. Use a memory error checker like valgrind to veryify your memory use is correct. (there is no excuse not to, it is dead-bang-simple to do) Just type valgrind yourprogramexe

$ valgrind ./bin/arraybasics "John Q. Public"
==19613== Memcheck, a memory error detector
==19613== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==19613== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==19613== Command: ./bin/arraybasics John\ Q.\ Public
==19613==

 allocated 15 bytes of memory for 'name'
 name: 'John Q. Public' (begins at address: 0x51e0040)
 reallocated 'name' to 30 bytes

 final name : 'John Q. Public reallocated'

==19613==
==19613== HEAP SUMMARY:
==19613==     in use at exit: 0 bytes in 0 blocks
==19613==   total heap usage: 2 allocs, 2 frees, 45 bytes allocated
==19613==
==19613== All heap blocks were freed -- no leaks are possible
==19613==
==19613== For counts of detected and suppressed errors, rerun with: -v
==19613== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

In the output, the following lines are of particular significance:

==19613== HEAP SUMMARY:
==19613==     in use at exit: 0 bytes in 0 blocks
==19613==   total heap usage: 2 allocs, 2 frees, 45 bytes allocated

This tells you that all memory allocated during your program has been properly freed. (make sure you close all open file streams, they are dynamically allocated as well).

Of equal importance is the ERROR SUMMARY:

==19613== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

There are no errors in the memory use. If you attempt to read, write or free memory from a location outside your block, or from an unitialized location or that would leave other memory unreachable, that information will show as an error.

(the suppressed: 2 from 2 just relate to additional debug libraries not present on my system)

This ended up longer than intended, but if it helps, it was worth it. Good luck.

2 of 5
5

How can I announce the array and then define it's size?

Don't; 'announce' it when you know what size it needs to be. You can't use it before then anyway.

In C99 and later, you can define variables when needed — anywhere in a statement block. You can also use VLAs (variable-length arrays) where the size is not known until runtime. Don't create enormous arrays as VLAs (e.g. 1 MiB or more — but tune the limit to suit your machine and prejudices); use dynamic memory allocation after all.

If you're stuck with the archaic C89/C90 standard, then you can only define variables at the start of a block, and arrays have sizes known at compile time, so you have to use dynamic memory allocation — malloc(), free() etc.

🌐
Reddit
reddit.com › r/c_programming › memory allocation for char array inside struct
r/C_Programming on Reddit: Memory allocation for char array inside struct
July 28, 2018 -

Hey friends,

I'm sure somebody on the internet jas alread answered this, but I honestly haven't found a suitable answer for me.

Let's say we have a struct like this:

typedef struct {
    char *name;
    int age;
} Human;

And a kind of "constructor" function like this:

Human *human_new(char *name, int age)
{
    Human *this = (Human *) malloc(sizeof(Human));

    this->name = name;
    this->age  = age;

    return this;
}

My question is, do I need to allocate memory for the char pointer name of the struct or is the assignment of this->name enough?

Top answer
1 of 5
25
My question is, do I need to allocate memory for the char pointer name of the struct or is the assignment of this->name enough? Depends on what you want to do. If you simply assign to this->name, then the object you've just allocated won't "own" the pointer or the contents of the string to which it points. It has merely borrowed the pointer. The string contents could change at any time, or the pointer could be invalidated by being freed, without the Human object even knowing. Borrowing pointers can indeed be useful in some cases, but it's almost certainly not what you want here. You probably want to strdup the name argument to create a completely new string with the same contents. You might even want to change name to be a const char *, to emphasise the fact that the value being passed is going to be copied, not borrowed. If you do create a whole new string for that name field, you will probably want to provide a destructor for your Human objects, e.g.: void human_free(Human *this) { free(this->name); free(this); } Also, on a completely different note, the cast in: Human *this = (Human *) malloc(sizeof(Human)); isn't necessary in C. Object pointers can be converted to and from void pointers without casts.
2 of 5
5
Yes, you will need to allocate memory for whatever you want to put in the name of the struct. malloc(sizeof(Human)) will allocate enough space for a char pointer and an int, it won't allocate memory for the contents of name because it has no idea what the size will be. If you know the upper bound on the size of name, or are happy to enforce one, you could change the definition of the struct to: typedef struct { char name[100]; int age; } Human; Then when you call malloc(sizeof(Human)) you'll get enough space for a 'string' containing up to 100 characters (only 99 usable because of the null terminator) and an int. If you do that, it would probably be sensible to define the maximum length of a name as #define MAX_HUMAN_NAME_LENGTH 100 to be clearer and so you only have to change it in one place.
Discussions

c - Allocating char array using malloc - Stack Overflow
Bring the best of human thought and AI automation together at your work. Explore Stack Internal ... Save this question. Show activity on this post. ... Duplicate of stackoverflow.com/questions/1011806/… though I disagree with the accepted answer there (I prefer to omit sizeof(char)) More on stackoverflow.com
🌐 stackoverflow.com
sizeof Char / Array length Memory allocation in C, - Stack Overflow
Can somebody tell me about the memory allocation in c? What is the size of a char variable? 2 or 4? Why the difference in the address value between 2 neighboring char elements in an array is only 1? More on stackoverflow.com
🌐 stackoverflow.com
Memory Allocation of char** array in C - Stack Overflow
I am writing a basic shell program in C for an assignment. I am currently having troubles allocating the memory that will be used to store the command line arguments that will be passed into the pr... More on stackoverflow.com
🌐 stackoverflow.com
arrays - C memory allocations - Stack Overflow
Bring the best of human thought and AI automation together at your work. Explore Stack Internal ... I was wondering how the C compiler allocated memory for a character array you initialize yourself, for instance: More on stackoverflow.com
🌐 stackoverflow.com
🌐
Medium
csnotes.medium.com › malloc-in-c-for-int-and-char-c3677b857b65
Malloc in C, for int * and char * | by zihan | Medium
May 15, 2021 - Each line is a string (char *, see Case 2). After we allocate memories for the number of lines we want AND we add the NULL-terminator at the end (very important, don’t forget!), we can allocate memory horizontally for each of these lines.
🌐
Quora
quora.com › How-do-I-dynamically-allocate-a-char-array-using-a-malloc-function-in-C
How to dynamically allocate a char array using a malloc function in C - Quora
In order to allocate memory dynamically using C language the following code will be enough: char *s = (char *)malloc(20 * sizeof(char)); The above line allocates memory for storing 20 characters or in fact 19 ch...
🌐
Quora
quora.com › How-do-I-allocate-memory-for-char-*array-2-dynamically-in-C
How to allocate memory for char *array [] [2] dynamically in C - Quora
Answer (1 of 7): I dislike all the other answers to this question, because they show the lack of understanding how to write maintainable and structured C programs and how to avoid complicating the syntax for yourself and others. There is a much easier way to look at this problem by using structs...
Top answer
1 of 2
3

You are not printing addresses correctly: an address is not an int, so you cannot use %d to print it. Use %p (for "pointer") instead, and cast the address to void* for printing:

printf ("This is the Address of val1 %p\n", (void*)&foo[1]);

Now your program produces this or similar output:

This is the Address of val1 0xffbd2fcf 
This is the Address of val2 0xffbd2fd0 
The size of each array member is 2

Two pointers are off by 1, which is the size of a single char. The size of the entire array is 2. If you want to print the size of a single element, use foo[0].

2 of 2
0

Memory Allocation is a bit tricky, but it's easier than you think. It is, as the name implies, the usage and selection of data to optimize program function and speed. Basically, Memory Allocation is a "perk" of languages such as C/C++ that allow programmers to only use EXACTLY as much data as needed, freeing up memory for other computer functions.

Some good info to know about memory...

  • memory is known in "bytes", these are 8-bit "groups" of data.
  • A "bit" is a value that is either 0 or 1.

Variable sizes:

  • Char: 1 byte
  • Int: 4 bytes
  • double: 8 bytes

When neighboring elements are of the same type (for example, an array of chars), they will have a difference in address that increments/decrements by the value of the memory size. Since a char variable has a size of 1 byte, neighboring elements will have addresses that differ by 1.

Ex: char addresses: 1204, 1205, 1206, 1207... (1 byte)

int addresses: 1204, 1208, 1212, 1216... (4 bytes)

Find elsewhere
🌐
codestudy
codestudy.net › blog › memory-allocation-char-and-char
Memory Allocation in C: char* vs char[] – What's the Difference?
Let’s break down the memory regions involved: char[]: When declared inside a function (local variable), the array is allocated on the stack. Its size is fixed at compile time (unless using VLAs), and it is automatically deallocated when the ...
Top answer
1 of 1
3

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;
}
🌐
Cplusplus
cplusplus.com › forum › general › 200288
char arrays and memory allocation - C++ Forum
My question is focused on the memory allocation of char arrays. I know that with strings, the size of the string is dynamically allocated in the background via the string class. But with char arrays, you have to explicitly assign the size of each char array (string).
🌐
USNA
usna.edu › Users › cs › adina › teaching › ic210 › fall2021 › lec › l38 › lec.html
[IC210: AY22] Class 38: Arrays and strings in C
The above code builds an array of 20 chars in the stack, pointed to by a char* aString. Again, you can technically build arrays in the stack like this for any type but it's exceedingly common with strings, which tend to be short, and so people put them in the stack, so they don't have to free them. Once you've allocated space for some chars, your pointer can be used with scanf to read in strings from the user:
Top answer
1 of 3
3

memcpy does not allocate any memory. In your memcpy call, the memory for the destination arr was allocated when the variable arr was defined (char arr[400]).

There's a problem there, which is that you haven't allocated enough room. You copy sizeof(updata) bytes into arr, which is probably 1+4+7*256=1797 (this may vary depending on sizeof(int) and on whether __packed__ actually leaves out all unused bytes on your platform). If you really need arr (you probably don't), make it at least sizeof(updata) large. Defining it with char arr[sizeof(updata)] is fine.

If the layout of the structure is defined by some external format, you should use a fixed-size type instead of int (which is 2 or 4 bytes depending on the platform, and could be other sizes but you're unlikely to encounter them).

If the layout of the structure is defined by some external binary format and you want to print out the 1797 bytes in this format, use fwrite.

fwrite(updata, sizeof(updata), 1, stdout);

If you want to have a human representation of the data, use printf with appropriate format specifications.

printf("ip='%c' udp=%d\n", updata.ip, updata.ip);
for (i = 0; i < sizeof(updata.rules)/sizeof(updata.rules[0]); i++) {
    puts(updata.rules[i].myname);
}

Despite the name, char is in fact the type of bytes. There is no separate type for characters in C. A character constant like 'a' is in fact an integer value (97 on almost all systems, as per ASCII). It's things like writing it with putchar or printf("%c", …) that make the byte interpreted as a character.

If your compiler is not signaling an error when you mix up a pointer (such as char*) with an integer, on the other hand, turn up the warning level. With Gcc, use at least gcc -O -Wall.


After actually compiling your code, I see the main error (you should have copy-pasted the error message from the compiler in your question):

udpdata.rules[0].myname = "lalla\0" ;

udpdata.rules[0].myname is an array of bytes. You can't assign to an array in C. You need to copy the elements one by one. Since this is an array of char, and you want to copy a string into it, you can use strcpy to copy all the bytes of the string. For a bunch of bytes in general, you would use memcpy.

strcpy(udpdata.rules[0].myname, "lalla");

(Note that "lalla\0" is equivalent to "lalla", all string literals are zero-terminated in C.¹) Since strcpy does not perform any size verification, you must make sure that the string (including its final null character) fits in the memory that you've allocated for the targets. You can use other functions such as strncat or strlcpy if you want to specify a maximum size.

¹ There's one exception (and only this exception) where "lalla" won't be zero-terminated: when initializing an array of 5 bytes, e.g. char bytes[5] = "lalla". If the array size is at least 6 or unspecified, there will be a terminating zero byte.

2 of 3
1
// this is really bad
udpdata.rules[0].myname = "lalla\0" ; 

// do this instead. You want the literal string in your field.
memcpy(udpdata.rules[0].myname, "lalla\0", 6);

....

// This is wrong.  arr is already a pointer.
memcpy (&arr , &udpdata, sizeof(udpdata));

// do this instead
mempcy (arr, &udpdata, sizeof(udpdate));

Concerning printing, I don't know how big ints are on your machine but if they are 4 bytes then

printf("%.7s", &arr[1+4]);

I'm not sure why you want to convert everything to a char array if you wanted to print out the content. Just use the struct and a for loop. Anyway I think you may want to read up on C arrays.

🌐
Reddit
reddit.com › r/c_programming › how does c allocate memory for arrays vs pointers vs normal variables?
r/C_Programming on Reddit: How does C allocate memory for arrays vs pointers vs normal variables?
August 29, 2023 -

So this is one of those questions where I have a bug and I think I kind of know what's going on, and my hypothesis has led me to ask the question, but I could be wrong and the question could have nothing to do with the actual problem.

I'm working on writing tests for a complex project for which I'm part of a team. I have some mocked functions for testing, and there's a whole framework for these mocked functions, and I can set the returns from these mocked functions using this framework. What happens often is that other test cases were not set up/closed down properly, and the mocked returns will "pollute" other tests. This seems to likely be the root of my issue.

In trying to debug this, I have a variable `foo`, which I want to be returned from a mocked function. I'm using the debugger to step through and see if `foo` is indeed being returned where I expect it to be. To compare the address of what's actually being returned to what I'm setting as the return, I throw a `SOME_TYPE* fooAddr = &foo;` in my test case code.

Now for some reason, when doing this, my issue vanishes. So my only guess is that there is some out of bounds memory access going on, and by making a new variable `fooAddr` here, it essentially puts some "padding" somewhere in memory which causes that memory out of bounds error to not actually fuck up stuff that shouldn't be fucked up; it might very well be fucking up `fooAddr`, but that doesn't affect anything in the tests.

So I mess around with this further to verify if this is the case. If I do, for example, this `SOME_TYPE*` variable, no issue. If I do a void*, no issue. If I do a char*, no issue. If I do a char[100], the issue appears. if I do a few `char`s, the issue shows up. If I do MORE chars, the issue disappears.

It seems the supposed OOB memory error needs a certain amount of "padding" to not blow up, and if I don't give it enough, the issue will persist.

My question is: why does something like a char* work, but a char[100] doesn't? I'm wondering if it has something to do with how memory for these things is allocated? Like a char* will be allocated "right there" in memory, which apparently helps my issue not be an issue. Yet a larger array of characters is "over there" and therefore doesn't help my issue.

This is all me spitballing and trying to figure it out on my own, so I may be way off basis with the overarching question, and I'd love to hear insights onto this

🌐
Swarthmore College
cs.swarthmore.edu › ~newhall › unixhelp › C_arrays.html
Arrays in C
Array bucket values are stored in contiguous memory locations (thus pointer arithmetic can be used to iterate over the bucket values), and 2D arrays are allocated in row-major order (i.e. the memory layout is all the values in row 0 first, followed by the values in row1, followed by values ...
🌐
Physics Forums
physicsforums.com › other sciences › programming and computer science
C: How to allocate memory for char*** data type • Physics Forums
January 3, 2016 - Data type of an array is char***. How to allocate memory for char*** data type? One string would be of type char *. An array of them would be of type char **. Why do you need three levels of indirection? Are you asking about a function parameter that is call by reference?
🌐
Arduino Forum
forum.arduino.cc › projects › programming
Dynamically Allocate Memory For An Array Of Chars - Programming - Arduino Forum
December 29, 2022 - I'm trying to create dynamic array of strings, but obviously I'm missing something... This does not compile: char **AOC; AOC = malloc(20 * sizeof(char *)); AOC[0] = malloc(20 * sizeof(char)); with this error: AOC = ma…
🌐
Wikibooks
en.wikibooks.org › wiki › A_Little_C_Primer › C_Dynamic_Memory_Allocation_&_Deallocation
A Little C Primer/C Dynamic Memory Allocation & Deallocation - Wikibooks, open books for an open world
September 10, 2008 - There may be no way of knowing how big an array needs to be for the specific task the program is performing, and so allocating an array in a fixed size will either result in wasted memory or in not having enough to do the job. The answer to this problem is to have the program allocate the memory at runtime, and that's what the "malloc()" library function does. For example, let's use "malloc()" to allocate an array of "char": /*malloc.c */ #include <stdio.h> #include <stdlib.h> /*For "malloc", "exit" functions.