When C was created, in 1972, computers were much slower. Most programs were written in assembly. C came along as a better assembly allowing programmers to manipulate memory directly with pointers. Programmers worked much closer to the machine and had to understand how memory worked to make their programs efficient.
Memory can be though of as an array of bytes where each address is on index in the array and holds 1 byte. If a computer has 4K of memory, it would have 4096 addresses in the memory array. How operating systems handle memory is much more complex than this, but the analogy provides an easy way to think about memory to get started.
Let’s say our computer has 4K of memory and the next open address is 2048. We declare a new char variable i = ‘a’. When the variable gets declared, enough memory is set aside for its value from unused memory. The variable name is linked to the starting address in memory. Our char i has a value ‘a’ stored at the address 2048. Our char is a single byte so it only takes up index 2048. If we use the & operator on our variable it would return the address 2048. If the variable was a different type, int for instance, it would take up 4 bytes and use up elements 2048-2051 in the array. Using the & would still return 2048 though because the int starts at that index even though it takes up 4 bytes. Let’s look at an example.
// intialize a char variable, print its address and the next address char charvar = '\0' ; printf ( & quot ; address of charvar = % p \ n & quot ; , ( void * ) ( & amp ; charvar ) ) ; printf ( & quot ; address of charvar - 1 = % p \ n & quot ; , ( void * ) ( & amp ; charvar - 1 ) ) ; printf ( & quot ; address of charvar + 1 = % p \ n & quot ; , ( void * ) ( & amp ; charvar + 1 ) ) ; // intialize an int variable, print its address and the next address int intvar = 1 ; printf ( & quot ; address of intvar = % p \ n & quot ; , ( void * ) ( & amp ; intvar ) ) ; printf ( & quot ; address of intvar - 1 = % p \ n & quot ; , ( void * ) ( & amp ; intvar - 1 ) ) ; printf ( & quot ; address of intvar + 1 = % p \ n & quot ; , ( void * ) ( & amp ; intvar + 1 ) ) ;Running that you should get output like the following:
address of charvar = 0x7fff9575c05f address of charvar - 1 = 0x7fff9575c05e address of charvar + 1 = 0x7fff9575c060 address of intvar = 0x7fff9575c058 address of intvar - 1 = 0x7fff9575c054 address of intvar + 1 = 0x7fff9575c05c
In the first example on lines 1-5 we declare a char variable, print out the address-of the char, and then print out the address just before and just after the char in memory. We get the addresses before and after by getting the using the & operator and then adding or subtracting one. In the second example on lines 7-11 we do the same thing except this time we declare an int variable, printing out its address and the addresses right before and after it.
In the output we see the addresses in hexadecimal. What is important to notice is that the char addresses are 1 byte before and after while the int the addresses are 4 bytes before and after. Math on memory addresses, pointer math, is based on the sizeof the type being referenced. The size of a given type is platform dependent but for this example our char takes 1 byte and our int takes 4 bytes. Subtracting 1 address from a char gives a memory address that is 1 byte previous while subtracting 1 from an int gives a memory address that is 4 bytes previous.
Even though in our example we were using the address-of operator to get the addresses of our variables, the operations are the same when using pointers that hold the address-of a varible.
Some commenters have brought up that storing &charvar – 1, an invalid address because it is before the array, is technically unspecified behavior. This is true. The C standard does have areas that are unspecified and on some platforms even storing an invalid address will cause an error.
Arrays in C are contiguous memory areas that hold a number of values of the same data type (int, long, *char, etc.). Many programmers when they first use C think arrays are pointers. That isn’t true. A pointer stores a single memory address, an array is a contiguous area of memory that stores multiple values.