why does the first one prints the string value whereas second give segfault?

What's happening in Case I:

The type of staticArray is char [100] i.e. array of 100 char.
The type of &staticArray is char (*)[100].
The %s format specifier in scanf() expect the argument as a pointer to initial element of char array i.e. of type char *.

You are passing char (*)[100] type to scanf(), hence compiler is giving warning on this statement.

The &staticArray give you pointer to the array of type char (*)[100] which is numerically same as the base address of array.

Consider this example:

#include <stdio.h>

int main(void)
{
    char staticArray[100];

    printf ("staticArray: %p\n", (void*)staticArray);
    printf ("&staticArray : %p\n", (void*)&staticArray);

    return 0;
}

Output:

## ./a.out
staticArray: 0x7ffee4044a70
&staticArray : 0x7ffee4044a70

staticArray and &staticArray both yield a pointer to the same address1) but their type is different. That's why when you pass &staticArray to scanf(), getting warning during compilation due to type mismatch but when scanf() called, it treat that pointer as char * and read input and store the result to given location. When printing it later, it prints the string value.

What's happening in Case II:

The type of dynamicArray is char *.
The type of &dynamicArray is char **.
So, you are passing char ** type to scanf() which expects char * when %s format specifier is used. Hence compiler is giving warning on this statement. The pointer &dynamicArray is different from dynamicArray.

Consider this example:

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

int main(void)
{
    char* dynamicArray;
    dynamicArray = malloc(sizeof(char)*100);
    if (dynamicArray == NULL) {
        exit(EXIT_FAILURE);
    }

    printf ("dynamicArray: %p\n", (void*)dynamicArray);
    printf ("&dynamicArray : %p\n", (void*)&dynamicArray);

    free(dynamicArray);
    return 0;
}

Output:

## ./a.out
dynamicArray: 0x7fd615401690
&dynamicArray : 0x7ffee7ab7ad0

dynamicArray and &dynamicArray both yield different pointer.

When you pass &dynamicArray to scanf() (which read input and store the result to given location), it lead to undefined behavior2) because your program end up accessing invalid memory.

When you pass &dynamicArray to printf() with format specifier %s, printf(), it access that address to write the character string and end up accessing invalid memory, lead to undefined behavior2). Hence, you are getting segfault.


1) An array is automatically converted to a pointer to its first element but there are few exceptions to this rule (C11 Standards#6.3.2.1p3):

  • The array is the operand of sizeof operator.
  • The array is the operand of _Alignof operator.
  • The array is the operand of &.
  • The array is a string literal used to initialize an array.

2) An undefined behavior includes it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended.

Answer from H.S. on Stack Overflow
Top answer
1 of 2
3

why does the first one prints the string value whereas second give segfault?

What's happening in Case I:

The type of staticArray is char [100] i.e. array of 100 char.
The type of &staticArray is char (*)[100].
The %s format specifier in scanf() expect the argument as a pointer to initial element of char array i.e. of type char *.

You are passing char (*)[100] type to scanf(), hence compiler is giving warning on this statement.

The &staticArray give you pointer to the array of type char (*)[100] which is numerically same as the base address of array.

Consider this example:

#include <stdio.h>

int main(void)
{
    char staticArray[100];

    printf ("staticArray: %p\n", (void*)staticArray);
    printf ("&staticArray : %p\n", (void*)&staticArray);

    return 0;
}

Output:

## ./a.out
staticArray: 0x7ffee4044a70
&staticArray : 0x7ffee4044a70

staticArray and &staticArray both yield a pointer to the same address1) but their type is different. That's why when you pass &staticArray to scanf(), getting warning during compilation due to type mismatch but when scanf() called, it treat that pointer as char * and read input and store the result to given location. When printing it later, it prints the string value.

What's happening in Case II:

The type of dynamicArray is char *.
The type of &dynamicArray is char **.
So, you are passing char ** type to scanf() which expects char * when %s format specifier is used. Hence compiler is giving warning on this statement. The pointer &dynamicArray is different from dynamicArray.

Consider this example:

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

int main(void)
{
    char* dynamicArray;
    dynamicArray = malloc(sizeof(char)*100);
    if (dynamicArray == NULL) {
        exit(EXIT_FAILURE);
    }

    printf ("dynamicArray: %p\n", (void*)dynamicArray);
    printf ("&dynamicArray : %p\n", (void*)&dynamicArray);

    free(dynamicArray);
    return 0;
}

Output:

## ./a.out
dynamicArray: 0x7fd615401690
&dynamicArray : 0x7ffee7ab7ad0

dynamicArray and &dynamicArray both yield different pointer.

When you pass &dynamicArray to scanf() (which read input and store the result to given location), it lead to undefined behavior2) because your program end up accessing invalid memory.

When you pass &dynamicArray to printf() with format specifier %s, printf(), it access that address to write the character string and end up accessing invalid memory, lead to undefined behavior2). Hence, you are getting segfault.


1) An array is automatically converted to a pointer to its first element but there are few exceptions to this rule (C11 Standards#6.3.2.1p3):

  • The array is the operand of sizeof operator.
  • The array is the operand of _Alignof operator.
  • The array is the operand of &.
  • The array is a string literal used to initialize an array.

2) An undefined behavior includes it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended.

2 of 2
1

Removing the & operator in both examples will solve the warnings. I'm not sure what your intended behavior is, but they seem to work correctly upon moving the & operator. This is because arrays are inherently pointers to the first element of the array so adding & is not what you want.

Working examples(exactly the same expect for &):

char staticArray[100];
scanf("%s",staticArray);
printf("%s\n", staticArray);

and

char* dynamicArray;
dynamicArray = (char*) malloc(sizeof(char)*100);
scanf("%s", dynamicArray);
printf("%s\n", dynamicArray);
Top answer
1 of 2
6

Size of pointer is machine specific (whether it is 32-bit or 64-bit). The result would be 12 and 8 bytes respectively on 32 bit machine. For 64-bit machine, answer would be 16 and 8 bytes.
See the explanation

typedef struct {
   int row;         // 4 bytes
   int col;         // 4 bytes
   char* data;      // 4/8 bytes on 32/64-bit machine
}item1 ;  

Total size = 12/16 bytes on 32/64 bit machine.

typedef struct {
   int row;         // 4 bytes
   int col;         // 4 bytes
   char data[];     // 0 bytes --> Flexible array 
}item2 ;  

Total size = 8 bytes.

Why size of flexible array is 0?

C11: 6.7.2.1 (p18)

[...] In particular, the size of the structure is as if the flexible array member were omitted [...]

2 of 2
1
typedef struct item2 {
   int row;
   int col;
   char data[];
}

in this struct item2 we have 2 ints, so the size will be 2*sizeof(int) in most of the systems, and also we have an array char data[] with undefined size so it counts as zero in this case, I think if there is padding in the struct then the amount of elements that the array will store will be related with the pading, but I am not sure.

total= 8 bytes.

typedef struct item1 {
   int row;    
   int col;
   char* data;
}

In this struct we have 2 ints that means 8 bytes ocupped by ints, and a pointer in a 64 bit system that means another 8 bytes.

total= 16 bytes.

There are a few things you need to know, there is no padding in this case an neither in item2. and the diferrence between char* data; and char data[]; is that the last one is an array that will be allocated in the stack, and the first one is a pointer allocated in the stack that will be commonly used with dynamic memory from the heap.

Discussions

Need help creating a dynamic char array in C - Stack Overflow
I'm having trouble creating a dynamic char array. This is what I have so far. char * arr; arr = (char*)malloc (2 * sizeof (char)); It's not allocating space for only 2 characters, it's letting me ... More on stackoverflow.com
🌐 stackoverflow.com
Is my understanding of dynamically allocating arrays in C correct?
No, this is not accurate. First of all, each function doesn't have its own stack. I think you meant to talk about the size of the function's stack frame. The stack frame doesn't have to have a fixed size that's known at compile time, because the compiler can generate code to push and pop things onto the stack as necessary. And in fact, there is nothing stopping you from declaring a dynamically-sized array on the stack in C (this was added in the C99 language specification). Some actual reasons to use dynamic allocation instead of stack allocation might be: The stack generally has a limited size compared to the heap, so a large array might not fit You might want the array to continue existing after the function that created it has returned More on reddit.com
🌐 r/learnprogramming
7
1
October 20, 2022
char - Malloc array of characters dynamic vs static C - Stack Overflow
Coincidentally, the main problem ... short array. If you want to capture exactly three characters (with no nul terminator), use scanf("<", userInput) instead. Note that without the NUL, you must not expect to treat userInput as a string; printing it via printf for example will result in a random amount of gibberish owing to the fact that C does not know where the string ended. Now, to answer your actual question on "what's the difference between malloc and the static ... More on stackoverflow.com
🌐 stackoverflow.com
September 16, 2015
Dynamic vs dynamically created arrays in C - Stack Overflow
Note that a dynamically created ... array; a dynamically created array can only be fixed-size in C. " My professor is saying things that pretty directly contradict this quote, and is being very evasive when I ask further questions. He doesn't seem to acknowledge that there is a difference between dynamic vs ... More on stackoverflow.com
🌐 stackoverflow.com
🌐
Scaler
scaler.com › home › topics › what is character array in c?
What is Character Array in C? - Scaler Topics
October 10, 2025 - The value of the address pointer can be changed as it can change its address and point to the new array. Dynamic memory allocation can't be used in the character array. Dynamic memory allocation can be used in character pointers.
🌐
GeeksforGeeks
geeksforgeeks.org › c language › dynamic-array-in-c
Dynamic Array in C - GeeksforGeeks
July 23, 2025 - Array in C is static in nature, so its size should be known at compile time and we can't change the size of the array after its declaration. Due to this, we may encounter situations where our array doesn't have enough space left for required elements or we allotted more than the required memory leading to memory wastage. To solve this problem, dynamic arrays come into the picture.
Top answer
1 of 2
5

This line arr = (char*)malloc (2 * sizeof (char)); will allocate memory for 2 bytes only. But you are overwriting the memory by accessing the more 8 or more than 8 byes. If you access more than two byes means, it will give some unpredictable issue. In case you want more memory please follow the below code.

#define USER_SIZE 10
arr = (char*)malloc ( USER_SIZE * sizeof (char));

Assign the value in USER_SIZE macro and then allocate the memory as much as you want.

Example for 2D pointer ( 5 X 10 )

#define ROW 5
#define COLUMN 10
main()
{
  unsigned char **p = NULL, colum = 0;
  p = malloc ( ROW * sizeof ( unsigned char *) );
  for (;colum< ROW; ++colum )
  {
    p[colum] = malloc (COLUMN * sizeof (unsigned char  ));
  }
}
2 of 2
2

What you are doing is called buffer overflow by writing beyond the bounds of memory allocated by malloc call. The compiler doesn't do bounds checking (it assumes you know what you are doing, and you only pay for what you use) and allow you to compile and run. However, it will lead to undefined behaviour and your program may crash. You shouldn't rely on such behaviour.

You, the programmer, has to make sure that you don't do illegal memory access. You should not cast the result of malloc. Also, malloc can fail to allocate memory in which case it returns NULL, the null pointer, which you should take care of. You can combine the two statements into one.

int length = 8; // you can also use a macro
char *arr = malloc(length * sizeof *arr);
if(arr) {
    // malloc call successful
    // do stuff with arr
} 
🌐
Reddit
reddit.com › r/learnprogramming › is my understanding of dynamically allocating arrays in c correct?
r/learnprogramming on Reddit: Is my understanding of dynamically allocating arrays in C correct?
October 20, 2022 -

I was going through an example of C code, and I was trying to figure out WHY we have to dynamically allocate an array rather than statically allocate in a specific example. I will write the example in pseudo code as it is simple and the code is not the point of this question:

  1. obtain user input using scanf method and store in variable of type int n

  2. Create an array int A[n]

Is my logic sound for why this is wrong:

This is wrong because the compiler has to decide the size of the function stack. In order to asses the size of the function stack, it will observe each of the variables in the function. However, since the value of n will only be determined at runtime, we cannot determine how much stack memory is needed for array A because the size will only be determined at run time. As a result, we must dynamically allocate memory.

Top answer
1 of 2
2

Welcome to Stack Overflow! Coincidentally, the main problem with your code is that it is vulnerable to a stack overflow. scanf has no way of knowing how big userInput is, because you didn't tell it, and will happily continue filling memory long past the end of your very short array.

If you want to capture exactly three characters (with no nul terminator), use scanf("%3c", userInput) instead. Note that without the NUL, you must not expect to treat userInput as a string; printing it via printf for example will result in a random amount of gibberish owing to the fact that C does not know where the string ended.

Now, to answer your actual question on "what's the difference between malloc and the static array": the difference is of scope. If you only ever use userInput before its creating function returns, there is no practical difference, but you're in trouble the minute you try to do something like this:

int function1 {
     char my_string[3];

     scanf("%3c", my_string);

     return my_string; /* WRONG! DANGER! */
}

The return in the above example will happily return the pointer to my_string to the calling function. However, as soon as function1 returns, the stack is rolled back and the memory my_string occupied is essentially gone (and likely already re-used). The results are unpredictable but almost universally very bad for your program.

However, had you used malloc() instead, you could safely return the my_string pointer and the memory would persist until someone later called free(my_string) (where my_string is the pointer to the original my_string; it need not be named the same!).

This highlights another difference: with a stack variable such as char my_string[3];, you do not need to worry about (and indeed cannot) free() the memory, where as if the memory is malloc()'d instead, you must free() it if you wish to reclaim the memory.

There are some nuances to the above, such as file-scoped variables and static function variables, which I leave as topics for further reading.

2 of 2
1

As pointed in Giorgi's answer, the main problem is the incorrect usage of the address-of operator &.


However, the reason why it worked on one case and why it didn't work on another is very interesting.

  1. char array[3]: When you declare that array, memory space will be allocated for it and array will be a label to that location(memory address). When you pass array to scanf (or use it anywhere else without subscripting []), you're passing an address to that function. Because of that, when you use the & operator on the label array, it returns the same address to you BUT with different type (T(*)[3]), which your compiler probably complained about. But, as the memory address is valid, it worked as expected.

  2. char *array = malloc(): When you declare that variable, memory is also reserve for it, but this time in a different place and the space reserved is sizeof T(*), so it can hold a memory address. This variable also has an address in memory, which you can also get using &array. Then you malloc some memory and malloc returns to you an address of a memory block which you can now use. You can get that memory address by simply doing array. So, when you call scanf with the &array you're passing the variable address instead of the block address. That's why it crashes (I'm guessing you were not entering only two characters).

Check this code:

#include <stdio.h>
#include <stdio.h>

int main(void)
{
    char *array[3];
    char *ptr = malloc(3 * sizeof(char));

    printf ("array : %p\n", array);
    printf ("&array: %p\n\n", &array);
    printf ("ptr   : %p\n", ptr);
    printf ("&ptr  : %p\n", &ptr);

    scanf("%s", &ptr);

    printf ("ptr   : %p\n", ptr);

    return 0;
}

Which outputs:

$ ./draft
array : 0x7ffe2ad05ca0
&array: 0x7ffe2ad05ca0

ptr   : 0x19a4010
&ptr  : 0x7ffe2ad05c98
ABCD
ptr   : 0x44434241

scanf got the address of the pointer, so when it saves the value it reads from stdin, it overwrites the address we had from malloc! The memory block we had is now gone, memory is leaking... Now, this is bad because we're overwriting stuff on our stack, memory is leaking, and it will crash.

Observe the last output: the value of ptr (which previously was the address of an allocated block) is now 0x44434241 (DCBA, ASCII Table)! How nice is that?

Find elsewhere
🌐
Gokcehan
gokcehan.github.io › notes › dynamic-arrays-in-c-language.html
Dynamic Arrays in C Language
Character arrays can be null terminated but this is not always possible for other types of arrays. For example, it is not possible to distinguish null termination and the number zero in integer arrays. For this reason, you might need to store an additional length parameter in the same scope, whether it is a struct, function, or a global scope. Finally, if you need a dynamically growing array, you can store a capacity parameter in addition to length and reallocate space when necessary:
Top answer
1 of 5
3

When the professor uses word dynamic it means that an array can change its size on the fly. That is new elements can be added to or deleted from the array. A dynamically allocated array means the allocation of an array at run-time in the heap. Statically allocated arrays are allocated before the main function gets the control.

Take into account that C has Variable Length Arrays (VLA). Bit it is not the same as dynamic arrays. VLA means that an array may be recreated with different sizes. But in each such recreation you create a new array.

An example of a dynamic array is standard C++ class std::vector.

2 of 5
3

The answer to this question will depend on how pedantially one wants to treat terms like "array" and "dynamic". Is "array" supposed to refer exclusibely to array types? Or are we allowed to include malloc-ed arrays as well, accessible through pointers? Does "dynamic" refer to dynamic memory (even though the standard C nomenclature does not use this term)? Or are we allowed to consider local VLAs as "dynamic" as well?

Anyway, one can separate arrays into three conceptual categories

  1. Arrays with compile-time size
  2. Arrays with run-time initial size, which cannot be resized
  3. Arrays with run-time initial size, which can be resized

Apparently, your professor referred to the second category as "dynamically created arrays" and to the third category as "dynamic" arrays.

For example, arrays from the first category are the classic built-in C89/90-style C arrays

 int a[10];

Arrays from the second category are C99 VLAs

 int a[n];

(or new-ed arrays in C++).

Arrays from the third category are arrays allocated with malloc

 int *a = malloc(n * sizeof *a);

which can be later resized with realloc.

Of course, once we step beyond the built-in features of the core language, the division between these categories becomes purely conceptual. It is just a matter of the interface the array implementation offers to the user. For example, it is possible to implement arrays from the third category through arrays of the second category, by destroying the old fixed-size array and allocating a new one of different size. In fact, that is how realloc is allowed to work in general case.

🌐
Cplusplus
cplusplus.com › forum › beginner › 222549
Dynamic Array vs Array Benefits - C++ Forum
October 4, 2017 - It does not need to be known at compile-time, meaning you can get this value at run-time from a file, or from user input. Both things have the same end result. Because arr is a pointer to dynamic data, this data can be deleted and new data can be allocated to the pointer. Last (and least), ...
🌐
Reddit
reddit.com › r/cpp_questions › how do i cin and cout dynamic char arrays in c++? (example names)
r/cpp_questions on Reddit: How do I cin and cout dynamic char arrays in c++? (example names)
August 8, 2021 -

hey! I am stuck on this question, I have to cin and cout a dynamic array of characters (not using library function/ c++ string types), here are the two issues i am facing:

  1. I do not know how long the name would be so should I just take a precondition that it should be smaller than let's say 100? or is there a way that I can create a dynamic array without specifying coloum size?

  2. this code that i came up with is producing weird result and i have no clue how to correct it T_T

I would be really thankful if you all can help me with this T_T.

my needed output should look something like:

enter name #1: abc def

enter name #2: ghi jkl...

.

.

.

enter name #n: nnn mmm

int main() 
{
	int n, j=0;
	cout<<"How many names do you want to enter ? ";
	cin>>n;
	char** name = new char*[n];
	for(int i=0; i<n; i++)
	{
		name[i]=new char[100];
	}
	for(int i=0; i<n; i++)
	{
		cout<<"enter name #"<<i+1 <<" :";
		do
		{
			cin>>name[i][j];
			j++;
			
		}while(name[i][j]!='\n');
	}
	for(int i=0; i<n; i++)
	{
	    for(int j=0; name[i][j]!='\n' ; j++)
	    {
	        cout<<name[i][j];
	    }
	    cout<<endl;
	}
}

Edit: Thank You so much for helping me with this problem however I figured a way to do it, it's definitely not the best or the most useful but our professor won't vibe with any other straight ways lmao here's what I came up with and the output I get, please let me know what yall think!

int main() 
{
	int n;
	
	cout<<"How many names do you want to enter ? ";
	cin>>n;
	char** lastname = new char*[n];
	for(int i=0; i<n; i++)
		lastname[i]=new char[100];
		
	char ** firstname = new char*[n];
	for(int i=0; i<n; i++)
	    firstname[i]=new char[100];
		
	for(int i=0; i<n; i++)
	{
		cout<<"enter name #"<<i+1 <<" :";
	   	   cin>>firstname[i]>>lastname[i];
	   	   
	}
	
	for(int i=0; i<n; i++)
	{
	    cout<<firstname[i]<<" "<<lastname[i];
	    cout<<endl;
	}
	
}

/* OUTPUT :
How many names do you want to enter ? 3
enter name #1 :Fucker Certified
enter name #2 :Flex Offender
enter name #3 :Billy Balls
Fucker Certified
Flex Offender
Billy Balls
*/

I am pretty sure it's not the most efficient but this was all I could think of T_T

once again Thank You so muchhh!!

Top answer
1 of 2
10
That is the C way and therefore is an absolutely awful C++ question. Anyway you need to use this function: https://en.cppreference.com/w/cpp/io/basic_istream/getline Every time the function fails because the buffer is too small you will have to release the buffer and reserve a larger buffer.
2 of 2
4
Yikes! You can't use either getline or standard string? Your teacher is shit. I don't care what they think they're doing, teaching you low level semantics - this isn't the way to do it. By the time you're here, writing user IO, such lessons should be behind you. Arbitrary limits are the mark of a bad teacher and a bad lesson plan. With that aside, you need to implement your own string and your own getline, and apparently your own vector. I hope you already know how to write functions, and can use those. If you're not allowed to use a function, consider dropping out or petitioning to switch to a teacher who has a fucking clue how to do their job. To implement a dynamic container, you need 3 pointers: using pointer = T*; using reference = T&; pointer first, last, total; Then to allocate: void alloc(size_t size) { first = new T[size]; last = first; total = first + size; } Reallocation is always a doubling: size_t cap() { return total - first; } size_t size() { return last - first; } void copy_to(pointer dest) { for(auto iter = first; iter != last; ++iter) { dest[iter - first] = *iter; } } void realloc() { auto tmp_cap = cap(); auto tmp_size = size(); pointer tmp = new T[tmp_cap * 2]; copy_to(tmp); delete first; first = tmp; last = first + tmp_size; total = first + tmp_cap; } Wrap this stuff up in a class. This is basically how vectors and strings work. If you can make it a template, then you can use your new container type for both. You'll have to flesh out the rest of the code, including adding a new element to the buffer, probably with a push_back method. You'll have to see if you have capacity to add one more, and if not, reallocate. Otherwise, you add your new element to last, then ++last. Then you need to replicate getline. This is simply extracting one character at a time and checking if it's equal to a delimiter. If it isn't the delimiter, you push back onto your string. If it is the delimiter, you disregard it and stop your extracting loop. I'll leave this one entirely up to you. Finally, you need to be able to print your string type: ostream &operator <<(ostream &os, const your_string_type &str) { for(size_t i = 0; i < str.size(); ++i) { os << str[i]; } return os; } This is "operator overloading" syntax. You can write custom operators for almost anything. I made the presumption you would have written a reference operator[](size_t offset) hint hint for your string type. I'm omitting a lot because I'm not going to do your homework for you, but I hope I've given you some insight. If you can't use any of this for your homework, then I hope I've kind of blown your mind.
Top answer
1 of 3
49

There are several flavors of arrays, depending on how and where they are declared.

Fixed-length Arrays

Fixed-length arrays must have their size determined at compile time. You cannot change the size of a fixed-length array after it has been defined.

Fixed-length arrays are declared in one of the following ways:

T a[N];
T a[N] = { /* initializer list */ };
char_type a[N] = "string literal";
T a[]  = { /* initializer list */ };
char_type a[]  = "string literal";

In the first three cases, N must be a constant expression whose value must be known at compile time. In the first three cases, the size of the array is taken from N; in the last two cases, it's taken from the number of elements in the initializer list or the size of the string literal.

The initial contents of a fixed-length array depend on its storage duration and whether an initializer has been supplied.

If the array has static storage duration (meaning it was declared at file scope outside of any function body, or was declared with the static keyword) and no initializer is present, then all of the array elements are initialized to 0 (for scalars) or NULL (for pointers). If T is an aggregate type such as a struct or an array type, then each member of the aggregate is initialized with a 0 or NULL. union types are similarly zeroed out.

If the array has auto storage duration (meaning it was declared within a function or block without the static keyword) and no initializer is present, then the contents of the array are indeterminate - basically, garbage.

If the array is declared with an initializer list (regardless of storage duration), then the initial values of the array elements correspond to the initializer. If there are fewer elements in the initializer than the array (for example, N is 10 but you only initialize the first 5 elements), then the remaining elements are initialized as though the array had static storage duration. IOW, given the declaration

int a[10] = {0, 1, 2};

then the initial contents of the array are {0, 1, 2, 0, 0, 0, 0, 0, 0, 0}.

Fixed-length arrays containing string values may be initialized using a string literal. C allows for "wide" character strings, so char_type may be either char or wchar_t. The rules are the same for regular initializer lists, except that N (if specified) must be at least 1 more than the length of the string to account for the string terminator.

This means that

char a[10] = "test"; 

will be initialized as {'t', 'e', 's', 't', 0, 0, 0, 0, 0, 0} and

char a[] = "test";

will be initialized as {'t', 'e', 's', 't', 0}.

Arrays with static storage duration are stored such that they are available as soon as the program is loaded, and aren't released until the program exits. This usually means that they're stored in a memory segment like .data or .bss (or the equivalent for whatever executable format your system uses).

Arrays with auto storage duration are stored such that they are allocated at block or function entry and released at block or function exit (in practice, they'll probably be allocated at function entry, regardless of whether they're limited to a smaller scope within the function) - this typically translates to the stack, although it doesn't have to.

Variable-length Arrays

Variable-length arrays were added in C99 - they behave mostly like fixed-length arrays, except that their size is established at run time; N does not have to be a compile-time constant expression:

int n;
printf( "gimme the array size: ");
scanf( "%d", &n );
T a[n]; // for any type T

Contrary to what their name implies, you cannot change the size of a variable-length array after it has been defined. "Variable-length" simply means that the size isn't fixed at compile time, and can change from definition to definition.

Since their size isn't set until runtime, variable-length arrays may not be declared at file scope or with the static keyword, nor can they be declared with an initializer list. Exactly how the space for VLAs is managed is up to the implementation; it may be (and usually is) taken from the stack, but AFAIK may be taken from somewhere else.

Dynamic Arrays

Dynamic arrays are not really "arrays" as such, at least in terms of the data types of the objects we use to manage them. Dynamic arrays are allocated at runtime using one of malloc, calloc, or realloc, and that storage is held until released with a call to free.

T *p = malloc( sizeof *p * N ); // where N may be either a compile-time or
                                // run-time expression
...
free( p );

A dynamic array may be resized using the realloc library function, like so:

/**
 * If realloc fails, it will return NULL and leave the original array in 
 * place.  We assign the result to a temporary variable so we don't risk
 * losing our only reference to that memory. 
 */
T *tmp = realloc( p, sizeof *p * new_size );  
if ( tmp )                                    
  p = tmp;                                    

While the memory for the array elements themselves is taken from the heap (or whatever dynamic memory pool), the memory for the pointer variable p will be allocated from either a .bss or .data segment or from the stack, based on p's storage duration (static or auto).

Memory allocated with malloc or realloc is not initialized; the contents of that memory will be indeterminate. Memory allocated with calloc will be initialized with zeros.

Arrays vs. Pointers

At some point, somebody is going to tell you that "an array is just a pointer". That person is not correct.

When you declare an array (either fixed- or variable-length), enough storage is set aside for the elements of that array and nothing else; no storage is set aside for any metadata such as the array length or a pointer to the first element. Given the declaration

T a[N];

then the storage will look something like this:

    +---+
 a: |   | a[0]
    +---+
    |   | a[1]
    +---+
    |   | a[2]
    +---+
     ...
    +---+ 
    |   | a[N-1]
    +---+

There is no object a apart from the array elements themselves (or, more properly, the object a is the elements of the array), and the expression a may not be the target of an assignment.

But...

The expression a[i] is defined as *(a + i); that is, given a pointer value a, offset i elements (not bytes!) from that address and dereference the result. But if a is not a pointer, how can that work?

Like this - except when it is the operand of the sizeof or unary & operators, or is a string literal used as an array initializer in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.

This has several implications:

  • The expressions a, &a, and &a[0] will all yield the same value (the address of the first element of the array), but the types of the expressions will be different (T *, T (*)[N], and T *, respectively);
  • The subscript operator [] works equally well with both array expressions and pointer expressions (indeed, it's defined to work on pointer expressions);
  • When you pass an array expression to a function, what you are actually passing is a pointer value, not the entire array;

For dynamic arrays, the situation is different. Given the line

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

then your storage will look something like this:

   +---+
p: |   | ---+
   +---+    |
    ...     |
     +------+
     |
     V
   +---+
   |   | p[0]
   +---+
   |   | p[1]
   +---+
    ...   
   +---+
   |   | p[N-1]
   +---+

In this case, p is a separate object from the array. Thus, &p won't give you the same value as p and &p[0], and its type will be T ** as opposed to T (*)[N]. Also, since p is just a pointer variable, you can assign a new value to it (although if you do so without freeing the memory it points to first, you'll create a memory leak).

Similarly, sizeof p won't behave like sizeof a; it will simply return the size of the pointer variable, not the size of the allocated memory that the pointer points to.

2 of 3
2

Static arrays are allocated memory at compile time and the memory is allocated on the stack. Whereas, the dynamic arrays are allocated memory at the runtime and the memory is allocated from heap.

This is static integer array i.e. fixed memory assigned before runtime

int arr[] = { 1, 3, 4 };

This is dynamic integer array i.e memory assigned on runtime

int* arr = new int[3]; 
🌐
Steve's Data Tips and Tricks
spsanderson.com › steveondata › posts › 2025-03-12
Arrays and Pointers in C: A Complete Guide for Beginners – Steve’s Data Tips and Tricks
March 12, 2025 - Always free dynamically allocated memory when you’re done with it. char *str = "Hello"; str[0] = 'J'; // Trying to modify a string literal, which is undefined behavior · Use character arrays if you need to modify strings.
🌐
Reddit
reddit.com › r/c_programming › working with dynamic arrays in c
r/C_Programming on Reddit: Working with dynamic arrays in C
February 4, 2024 -

Hi there! How do C programmers work with dynamically allocated arrays – i.e vectors as in std::vector – in real C codebases, where dynamic allocations are possible (that is, there is heap memory and enough memory etc.)?

I keep finding myself wanting to do something like: struct vec { int* ptr; size_t len, cap; } and a bunch of other functions to create such a vector, push to it etc. If I were not to do this, I'd have to pass to many functions int** ptr, size_t* len, size_t* cap, which is very cumbersome.

None of the two methods above feel very C-like, given that there is no proper metaprogramming and that this approach seems like a too hefty abstraction for C but again, I don't have experience with real codebases, production code. In some situations I find that I can reduce the number of parameters, but not all.

What do you think? What is the solution? Do you have other approaches? Have you encountered this? If not, why?

Thank you very much for your input!

P.S. On mobile. If code formatting is broken, I'll fix it later. Also, the C23 changes which add some kind of generics using macros should not affect the answers here, as far as I can think; this question is in its essence concerned with abstraction in C and how it is approached in C.

Top answer
1 of 4
8
You’ll get a ton of different answers to this question because there is no standardized approach. The standardized approach arrived with C++ and std::vector. None of the two methods above feel very C-like […] You’ll see plenty of code like struct vec { int *data; size_t len, cap; }; This, at least, is clear and easy to understand. Any C programmer will look at this and immediately understand (or guess) that it’s a dynamic array. The only real question is how you work with it. Some people use macros. Either a bunch of macros to define all sorts of helper functions, or a small number of macros that you call directly. Some of these macros work on structures, some can work directly on variables stored anywhere. You can find examples in the Git codebase. Here’s one of them (line 1156): https://github.com/git/git/blob/2a540e432fe5dff3cfa9d3bf7ca56db2ad12ebb9/git-compat-util.h#L1156 Some people don’t use macros, and just use a small number of helper functions, or code that is copy-pasted. My advice? Keep it simple. Don’t go wild with the preprocessor. If you start doing lots of different tricks with the preprocessor, you can end up with C that is unreadable and hard to debug or analyze. Some amount of duplication is to be expected in a C codebase—use your own judgment, and try to keep your code simple.
2 of 4
6
Generally speaking, try to make the caller responsible for memory management. This allows them to use auto, static, or allocated storage as they like. Instead of forcing them to use allocated storage and the reallocating it, you can just fail and inform them of how much memory would be required to succeed. This is how the standard library operates, and snprintf is a reasonable example of the approach.
🌐
Deanza
voyager.deanza.edu › ~bentley › cis22b › examples › DynamicMemoryAllocationForCharArrays.html
Dynamic Memory Allocation for char arrays
// Using dynamic memory allocation for char arrays #include <iostream> #include <cstring> #include <cstdlib> using namespace std; int main() {
🌐
Cplusplus
cplusplus.com › forum › beginner › 169561
Dynamic character array - C++ Forum
July 14, 2015 - Here is where I am at the moment: Apologies for the awful code and any help much appreciated! ... If you just want to read a string, there is a string class. If you know the string won't be longer than some value, you can create a char buffer that large. Or if you really want to dynamically allocate that string: ... Topic archived. No new replies allowed. Home page | Privacy policy © cplusplus.com, 2000-2025 - All rights reserved - v3.3.3 Spotted an error? contact us
🌐
Scaler
scaler.com › home › topics › dynamic array in c
Dynamic Array in C - Scaler Topics
August 24, 2023 - They can be initialized with variable size, and their size can be modified later in the program. Dynamic arrays are allocated on the heap, whereas VLAs are allocated on the stack. It's important to note that, VLAs aren't the same as dynamic arrays.
🌐
Medium
medium.com › @bassettjosh397 › building-a-dynamic-array-from-first-principles-in-c-512fae478da7
Building a Dynamic Array From First Principles In C | by Josh Bassett | Medium
April 30, 2025 - We define as a typedef, which creates a data type that does not have to be defined as a structure, so I can call Array instead of struct Array. We declare a character, this represents the type. ... We declare ‘i, c, f’ for integer, character, float as a useful naming convention for each data type. We finally declare a next and previous index to allow for linked list traversal. This structure gave me a solid foundation to start building out the essential operations. Adding elements to a dynamic array is not straightforward, we need to consider the implications of adding elements without understanding type nor memory constraints.
🌐
GeeksforGeeks
geeksforgeeks.org › c language › how-to-create-a-dynamic-array-of-strings-in-c
How to Create a Dynamic Array of Strings in C? - GeeksforGeeks
July 23, 2025 - Strings are arrays of characters terminated by the null character '\0'. A dynamic array of strings will ensure to change it's size dynamically during the runtime of the program as per the user's needs.
🌐
Codecademy
codecademy.com › docs › arrays › dynamic arrays
C | Arrays | Dynamic Arrays | Codecademy
January 30, 2025 - A dynamic array in C refers to an array whose size can be adjusted during runtime. Unlike static arrays, whose size must be fixed at compile time, dynamic arrays offer flexibility by utilizing memory allocation functions from the stdlib.h library, ...