3

I'm trying to understand C-pointers. As background, I'm used to coding in both C# and Python3.

I understand that pointers can be used to save the addresses of a variable (writing something like type* ptr = &var;) and that incrementing pointers is equivalent to incrementing the index of an array of objects of that object type type. But what I don't understand is whether or not you can use pointers and deferenced objects of the type (e.g. int) without referencing an already-defined variable.

I couldn't think of a way to do this, and most of the examples of C/C++ pointers all seem to use them to reference a variable. So it might be that what I'm asking is either impossible and/or bad coding practice. If so, it would be helpful to understand why.

For example, to clarify my confusion, if there is no way to use pointers without using predefined hard-coded variables, why would you use pointers at all instead of the basic object directly, or arrays of objects?

There is a short piece of code below to describe my question formally.

Many thanks for any advice!

// Learning about pointers and C-coding techniques.

#include <stdio.h>

/* Is there a way to define the int-pointer age WITHOUT the int variable auxAge? */

int main()  // no command-line params being passed
{
    int auxAge = 12345;
    int* age = &auxAge;
    // *age is an int, and age is an int* (i.e. age is a pointer-to-an-int, just an address to somewhere in memory where data defining some int is expected)
    // do stuff with my *age int e.g. "(*age)++;" or "*age = 37;"
    return 0;
}
Gregory Fenn
  • 460
  • 2
  • 13

5 Answers5

8

Yes, you can use dynamic memory (also known as "heap") allocation:

#include <stdlib.h>

int * const integer = malloc(sizeof *integer);
if (integer != NULL)
{
  *integer = 4711;
  printf("forty seven eleven is %d\n", *integer);
  free(integer);
  // At this point we can no longer use the pointer, the memory is not ours any more.
}

This asks the C library to allocate some memory from the operating system and return a pointer to it. Allocating sizeof *integer bytes makes the allocation fit an integer exactly, and we can then use *integer to dereference the pointer, that will work pretty much exactly like referencing an integer directly.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • 6
    Also, as a completely egomaniacal meta-comment, this happens to be my 6,000:th answer posted here. :) Yay me. – unwind Feb 14 '19 at 15:00
  • Ahh that's interesting to know about! I assume it is near the end of the C course I'm taking (standard lib functions are way later down the line, which seems odd to me). So would my original code, where I declare and assign a normal `int` and then assign a seperate point to its address, still only work on the stack? Are all variables saved in the stack by default unless you specifically call `malloc(byteCount)`? – Gregory Fenn Feb 14 '19 at 18:22
1

Parameters to a function in C are always pass by value, so changing a parameter value in a function isn't reflected in the caller. You can however use pointers to emulate pass by reference. For example:

void clear(int *x)
{
    *x = 0;
}

int main()
{
    int a = 4;
    printf("a=%d\n", a);   //  prints 4
    clear(&a);
    printf("a=%d\n", a);   //  prints 0
    return 0;
}

You can also use pointers to point to dynamically allocated memory:

int *getarray(int size)
{
    int *array = malloc(size * sizeof *array);
    if (!array) {
        perror("malloc failed"); 
        exit(1);
    }
    return array;
}

These are just a few examples.

dbush
  • 205,898
  • 23
  • 218
  • 273
1

There are many good reasons to use pointers in C, and one of them is, that you can only pass by value in C - you cannot pass by reference. Therefore passing pointer to an existing variable saves you the overhead of copying it to stack. As an example, let's assume this very large structure:

struct very_large_structure {
    uint8_t kilobyte[1024];
}

And now assume a function which needs to use this structure:

bool has_zero(struct very_large_structure structure) {
    for (int i = 0; i < sizeof(structure); i++) {
        if (0 == structure.kilobyte[i]) {
            return true;
        }
    }

    return false;
}

So for this function to be called, you need to copy the whole structure to stack, and that can be especially on embedded platforms where C is widely used an unacceptable requirement.

If you will pass the structure via pointer, you are only copying to the stack the pointer itself, typically a 32-bit number:

bool has_zero(struct very_large_structure *structure) {
    for (int i = 0; i < sizeof(*structure); i++) {
        if (0 == structure->kilobyte[i]) {
            return true;
        }
    }

    return false;
}

This is by no mean the only and most important use of pointers, but it clearly shows the reasoning why pointers are important in C.

Petr
  • 486
  • 7
  • 19
1

But what I don't understand is whether or not you can use pointers and deferenced objects of the type (e.g. int) without referencing an already-defined variable.

Yes, there are two cases where this is possible.

The first case occurs with dynamic memory allocation. You use the malloc, calloc, or realloc functions to allocate memory from a dynamic memory pool (the "heap"):

int *ptr = malloc( sizeof *ptr ); // allocate enough memory for a single `int` object
*ptr = some_value; 

The second case occurs where you have a fixed, well-defined address for an I/O channel or port or something:

char *port = (char *) OxDEADBEEF;

although this is more common in embedded systems than general applications programming.

EDIT

Regarding the second case, chapter and verse:

6.3.2.3 Pointers

...
5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.67)

67) The mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment.
John Bode
  • 119,563
  • 19
  • 122
  • 198
  • Is the second example actually allowed by the C standard, or do compilers for embedded systems tend to add an extension to the language to support it? – Christian Gibbons Feb 14 '19 at 16:23
  • 1
    @ChristianGibbons: It's allowed, whether it's *meaningful* depends on the implementation. It depends on how the integer value is converted to a pointer value, whether that resulting pointer value is valid, etc. – John Bode Feb 14 '19 at 16:28
0

Most common reason: because you wish to modify the contents without passing them around.

Analogy:
If you want your living room painted, you don't want to place your house on a truck trailer, move it to the painter, let him do the job and then haul it back. It would be expensive and time consuming. And if your house is to wide to get hauled around on the streets, the truck might crash. You would rather tell the painter which address you live on, have him go there and do the job.

In C terms, if you have a big struct or similar, you'll want a function to access this struct without making a copy of it, passing a copy to the function, then copy back the modified contents back into the original variable.

// BAD CODE, DONT DO THIS
typedef struct { ... } really_big;
really_big rb;
rb = do_stuff(rb);

...    

rb do_stuff (really_big thing) // pass by value, return by value
{
  thing->something = ...;
  ...
  return thing;  
}

This makes a copy of rb called thing. It is placed on the stack, wasting lots of memory and needlessly increasing the stack space used, increasing the possibility of stack overflow. And copying the contents from rb to thing takes lots of execution time. Then when it is returned, you make yet another copy, from thing back to rb.

By passing a pointer to the struct, none of the copying takes place, but the end result is the very same:

void do_stuff (really_big* thing)
{
  thing->something = ...;
}
Lundin
  • 195,001
  • 40
  • 254
  • 396