0

from my understanding char* is a pointer to the first char byte of our allocated memory. Char z[4] creates a array of 4 chars and z is a pointer to the first char? Are these the same thing?

Does the z[4] allocate memory?

  char *s = malloc(5);
  char z[4];

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
sw30
  • 77
  • 5
  • 2
    `s` points to a block of 5 bytes (can be made to point some where else), `z` **is** a block of 4 bytes (cannot be made to be anything else). In many uses `z` is silently converted to a pointer to the first of those 4 bytes. – pmg Feb 24 '21 at 21:25
  • Correction: `s` points to the first byte of a block of 5 bytes... – pmg Feb 24 '21 at 21:39
  • [C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)](http://port70.net/~nsz/c/c11/n1570.html#6.3.2.1p3) – David C. Rankin Feb 24 '21 at 22:04
  • `z` is not a pointer. Imagining that it is one is a frequent source of confusion. (Instructors who say things like "arrays are constant pointers" are a big part of the problem.) – Steve Summit Feb 24 '21 at 23:23

4 Answers4

3

I don't think it's possible to explain/understand these without pictures.

char z[4];

This gives you an array of 4 chars, known by the name z:

   +---+---+---+---+
z: |   |   |   |   |
   +---+---+---+---+

This can hold a string up to three characters long (leaving room for the trailing \0).

char *s = malloc(5);

The char *s part gives you a pointer to char, that can point to any character anywhere. The malloc(5) part gives you, in effect, a nameless array of 5 characters somewhere in memory (more formally, on "the heap"). And then the = (assignment) operator makes the former point to the latter. You can imagine it looking like this:

   +---------------+
s: |       *       |
   +-------|-------+
           |
           V
         +---+---+---+---+---+
         |   |   |   |   |   |
         +---+---+---+---+---+

The malloc'ed region gives you room for a string up to 4 characters long.

Because pointers can point anywhere, you can reassign them later. For example, you could make s point to the first character of the z array, by saying

s = &z[0];

Then you'd have this picture:

         +---+---+---+---+
      z: |   |   |   |   |
         +---+---+---+---+
           ^
           |
   +-------|-------+
s: |       *       |
   +---------------+
           
           
         +---+---+---+---+---+
         |   |   |   |   |   |
         +---+---+---+---+---+

But now we come to the "fun" part. It's going to sound strange or absurd at first, but once you get it, suddenly C's handling of pointers and arrays (and therefore strings) is going to make much more sense. What if you say

s = z;

At first this may make no sense. It looks like we're trying to take an array (on the right) and assign it to a pointer (on the left). That looks like a type mismatch. Also you may have heard (truly) that you cant assign arrays in C. So this looks doubly impossible. But, by special dispensation of Dennis Ritchie, you can do this, and it is, by definition, exactly the same as if you had said

s = &z[0];

That is: Whenever you try to use an array in an expression, what you get is a pointer to the array's first element. That is, in this example, just as if you you had said &z[0].

So, it's true, there are two ways of slinging strings around in C: as arrays, and as pointers. You can do it the array way and say

char y[] = "cat";
char z[4];
strcpy(z, y);

Or you can do it the pointer way and say

char *s = "bear";
char *t;

t = s;

Both of these "assign" one string to the other, but one of them does it by copying characters (using strcpy), and one of them does it by just manipulating pointers.

I also glossed over a little point. When I said

char y[] = "cat";

the compiler initialized the array for me, giving me this:

   +---+---+---+---+
y: | c | a | t | \0|
   +---+---+---+---+

But when I said

char *s = "bear";

the compiler created that string "bear" for me somewhere, in anonymous memory, sort of like (but definitely not exactly like) when I called malloc:

   +---------------+       +---+---+---+---+---+
s: |       *-------------->| b | e | a | r | \0|
   +---------------+       +---+---+---+---+---+

You can also mix'n'match, up to a point. As we saw earlier, you can make a pointer point to an array, so this is fine:

t = z;

When you copy strings, however, you have to make sure theres enough space. If you said

strcpy(z, "kangaroo");

for example, things might explode, because "kangaroo" has more characters than array z can hold.

You also have to be careful with constant strings. If you've just said

char *s = "bear";

you cannot say

strcpy(s, "kangaroo");

for two reasons: One, the anonymous array that s points to isn't big enough, but perhaps even more importantly, the anonymous array that s points to is constant, and you're not allowed to scribble on it.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
1

There are declared two objects of different types: a pointer and an array.

char *s = malloc(5);
char z[4];

Note: instead of the type char[4] I will use the type char[5] to make the numbers of allocated elements to be equal.

To see the difference you can run the following simple program.

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

int main(void) 
{
    char *s = malloc(5);
    char z[5];
    
    printf( "sizeof( s ) = %zu\n", sizeof( s ) );
    printf( "sizeof( z ) = %zu\n", sizeof( z ) );
    
    free( s );
    
    return 0;
}

The program output might look like

sizeof( s ) = 8
sizeof( z ) = 5

A pointer to an object of the array type will look like

char ( *p )[5] = &z;

while a pointer to the pointer type will look like

char **p = &s;

In other cases (except using a string literal to initialize an array) an array designator is indeed converted to a pointer to its first element.

From the C STandard (6.3.2.1 Lvalues, arrays, and function designators)

3 Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined

As for your question

Does the z[4] allocate memory?

then you declared an array for which was allocated memory.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

char z[4] creates a array of 4 chars and z is a pointer to the first char? Are these the same thing?

They are not the same, z is an array of 4 characters, it is not a pointer, its type is different from s which is indeed a pointer to char. The latter can point to any given character in an array of characters, the one you allocated or any other, for instance, it can also point to a character in z.

Does the z[4] allocate memory?

The type of storage used by both is different z is stored in the stack, assuming it's declared inside a function, (i.e it's not a global variable), whereas s, or better, the memory pointed by s is stored on the heap, it's what's called a dynamically allocated array, the first is handled automatically, the latter is handled manually, you must reserve the memory yourself, as you did, with malloc, reserving a memory block that can store 5 characters.

More info about memory management here:

Where in memory are my variables stored in C?

The stack should be prioritized when storing small amounts of data because the access is faster, but it has very limited size, so if your have large data buffers you must store them in the heap.

For reference if you like to know more details about the matter you can check this post:

What and where are the stack and heap?

Another difference is that s's lifetime is limited to the lifetime of the scope where it belongs and will always have the same size, it cannot be redimensioned, whereas the memory pointed by s is not limited to a given scope, and can be resized, you must also keep track of it and ultimately deallocate it when you no longer need it, under penalty of causing a memory leak.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • Despite several Stack Overflow answers that say so, I don't believe `z` is put onto the stack *unless it is declared inside of a function.* Several other answers say that the C specification doesn't actually specify where these are stored. As a global variable, *the size of `z` is known at compile time,* so it doesn't really make much sense to store it on the stack. – Robert Harvey Feb 24 '21 at 21:51
  • @RobertHarvey I assumed it was, but you're right, it must be mentioned. – anastaciu Feb 24 '21 at 21:53
1

from my understanding char* is a pointer to the first char byte of our allocated memory. Char z[4] creates a array of 4 chars and z is a pointer to the first char?

No, z is an array of 4 char. In most places where it appears in an expression, its (array) value is automatically converted to a pointer to the first char in the array (it "decays" to a pointer), but that's a very different thing.

Are these the same thing?

No, not at all. But the pointer value to which z decays has the same type as s.

Does the z[4] allocate memory?

  char *s = malloc(5);
  char z[4];

The declaration char z[4]; declares an array of four char, and ensures that memory is provided for such an object. Thus, yes, it allocates memory, but it does not dynamically allocate memory in the sense that malloc() does.

Compare the preceding statement: it declares a pointer to char and ensures that memory for the pointer is provided, but it also calls malloc() to obtain an initial value for that pointer. Supposing that malloc() succeeds, there are two allocations in that case: a so-called "automatic" one for s, and a dynamic one for an object to which s initially points.

Supposing that the two declarations appear at block scope, their lifetimes end when execution of the innermost block containing them terminates, and the memory they occupied is then automatically available for reuse. Not so with the memory allocated by malloc(). The lifetime of that block of memory ends only when it is free()d, regardless of what happens with s. If you lose the pointer to that memory, for instance by letting s's lifetime end before freeing the block, without preserving the pointer value somehow, then the block cannot be freed for the duration of the program's run -- this is known as a "memory leak".

John Bollinger
  • 160,171
  • 8
  • 81
  • 157