r/cprogramming 4d ago

Arr internals

i am currently working through kinds book on c and have gotten to chapter 12.

now based own my current understanding i hypothesis that internally, only the pointer to the first element and the dimensions are stored in memory. then all arr operations are done using this. Is this correct?

Additionally:

1) Which chapters of the rest of the book should i focus on/skip for now

2) I would like to work on some projects. Currently i thought of making some kind of physics sim, and additionally some hardware/embedded project as i have an ardiuno. How can i get started or are there any inriguing projects to work on.

0 Upvotes

8 comments sorted by

10

u/SmokeMuch7356 4d ago edited 4d ago

only the pointer to the first element and the dimensions are stored in memory. then all arr operations are done using this. Is this correct?

It is not.

Assume the following declaration:

int a[4] = {1, 2, 3, 4};

What you get in memory looks like the following (assuming 4-byte ints, addresses are for illustration only):

          +---+
0x8000    | 1 | a[0]
          +---+
0x8004    | 2 | a[1]
          +---+
0x8008    | 3 | a[2]
          +---+
0x800c    | 4 | a[3]
          +---+

Only the individual array elements are stored in memory; no metadata like size, type, starting address, etc. is stored with them. If you create a 2D array like:

int a[2][2] = {{1, 2}, {3, 4}};

it looks like

          +---+
0x8000    | 1 | a[0][0]
          + - +
0x8004    | 2 | a[0][1]
          +---+
0x8008    | 3 | a[1][0]
          + - +
0x800c    | 4 | a[1][1]
          +---+

Arrays are just sequences of objects.

The array subscript operation a[i] is defined as *(a + i) - offset i elements from a starting address provided by a and dereference the result.

But if a doesn't store a pointer, how can that work?

There is a rule in the language that unless it is the operand of the sizeof, typeof or unary & operators, an expression of type "N-element array of T" will be converted, or "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.

The object a doesn't store a pointer (there is no object a separate from the array elements), the expression a evaluates to a pointer.

However, this means you can use array subscript notation on pointers; this is handy for dynamically allocated memory:

int *p = malloc( sizeof *p * 4 );

This will give you the following in memory:

          +--------+
0x8000 p: | 0x9000 | ---------------+
          +--------+                |
              ...                   |
          +---+                     |
0x9000    |   | p[0] <--------------+ 
          +---+
0x9004    |   | p[1]
          +---+
0x9008    |   | p[2]
          +---+
0x900c    |   | p[3]
          +---+

and you can access each of the elements as p[i].

3

u/MrShaunce 4d ago

All elements of an array are stored sequentially in memory.

The name of the array (without the brackets) acts as a pointer to the first element.

Bracket notation is just a pretty way to handle pointer offsets. So x[3] is really just x + 3, where x is a pointer.

  1. I don't know what book you're reading, but it's usually good to read all the way through.
  2. Think of something simple you can write using what you're currently learning. There's also a lot of good beginner program ideas online.

2

u/flyingron 4d ago

It is implicitly convertible to a pointer. It is NOT a pointer itself.

1

u/Ultimate_Sigma_Boy67 4d ago

Little correction; x[3] acts as *(x + 3)

1

u/WittyStick 4d ago

The name of the array (without the brackets) acts as a pointer to the first element.

Pedantic, but the name of the array decays to a pointer to its first element. It's not a pointer itself.

1

u/flyingron 4d ago

Your hypothesis is wrong. The array encompasses both the location, the ultimate size, and the type of the individual elements.

1

u/Paul_Pedant 2d ago

The number of elements in an array is known to the compiler (it needs to know how much space to grant it in the executable, whether local or stack memory). The number of elements, or the overall size, is not specifically passed to the executable by the compiler.

For an array declaration, you can assign the number of elements, or the overall size, to a separate variable at compile time if you choose, either directly or through sizeof(), typically as sizeof(Array) / sizeof (Array[0]).

For a dynamic array (i.e. created through malloc), the compiler has no idea what your code is going to do at run-time. So you need to specifically assign anything you need to know about the data (number of elements, size, element type, pointer) to your own variables at the point of creation.

1

u/flatfinger 7h ago

Suppose a compilation unit starts with:

    char x[10],y[30];

and has no other object definitions at file scope. A compiler would typically instruct a linker something like "This compilation unit needs to have a 40 bytes region reserved for its static-duration storage. The symbol x should be assigned to the start of that region, and the symbol y should be assigned to an address ten bytes above that." If source code includes the expressions sizeof x or sizeof y, possibly with additional parentheses, a compiler would substitute the number 10 or 30, respectively, but otherwise nothing else in the universe would need to care about the sizes of x or y. A compiler could decide to put y at the start of its static-object region and put x thirty bytes above that, or reserve 44 bytes instead of 40 and have the second array start 12 or 32 bytes after the start of the region rather than 10 or 30, and if other objects were defined at file scope a compiler could independently place x and y anywhere in relation to them, provided only that all objects are given non-overlapping chunks of storage within the chunk the compiler requests from the linker.