Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C pointer syntax

Tags:

c

syntax

pointers

What is the difference between these two lines of code?

int *ptr = &x;

and

void* q = &x;
int* p = q;

I'm very new to C and the concept of pointers - having been taught primarily in Java - so just a little confused.

Thanks in advance.

like image 610
bobble14988 Avatar asked Sep 02 '25 09:09

bobble14988


2 Answers

void * is used to note a generic pointer in C.
Meaning it can point to any type.

So in the first case int *ptr = &x; you use a pointer to an int so anyone using it knows it is manipulating an integer.

In the second case void* q = &x; you point to an integer address via a gereric pointer.
The problem is that it is not clear what type this pointer is refering to.

So the first and 2 examples have the same effect (in your specific example) but the void * is not safe to be used like this.

like image 108
Cratylus Avatar answered Sep 04 '25 06:09

Cratylus


void* q = &x;

It's probably worth understanding how things are represented in memory so you understand the meaning of void*. A pointer is a position in memory containing another position in memory. For a real-world example, consider the following code:

int x = 4;
int* y = &x;
void* z = &x;

On my 64-bit system, I can use gdb to examine the memory. The results are as you see in the following table:

Address         | Memory Value                               | C expression
================================================================================
0x7fffffffdf8c  | 0x04 0x00 0x00 0x00                        | int x = 4;
0x7fffffffdf98  | 0x8c 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00    | int* y = &x;
0x7fffffffdf90  | 0x8c 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00    | void* z = &x;

What do we see here? Well, 0x7fffffffdf8c to 0x7fffffffdf90 is taken up by the value 0x04 and all zeros - integers on my platform are 4-bytes wide and the order is little endian, so the bytes are the opposite of what a human would expect to read. Then, we see the next 8-bytes are taken up by none other than the address of x, and likewise again for the second pointer.

So the process of using a pointer is to read the address at an address and work with that. It sounds like you might be fairly happy with that concept as is, so, moving on:

The type of the pointer does not affect the size of the pointer. This is key. Looking at both your pointer values above, they are actually both the same size. The size value here talks about the target memory - it instructs the compiler to load and operate on a certain amount (number of bytes - the size/width of a type) of memory.

void*, as others have said, is "typeless". Rather, it is just a pointer and the compiler/C language will not be able to support you dereferencing it, since there is no type information - there is no way to safely tell how much memory at the target address you want to read.

However, this fact is sometimes useful. The idea of using types is to provide consistency in your code - if a function expects a 64-bit integer, using types enforces this requirement so that you don't introduce errors. Sometimes, however, you don't mind what type you get. In these cases, your requirements are "some memory. any memory!" - the best example of this I can think of is memcpy - which might work a little like this:

void *memcpy(void * s1, const void* s2, size_t n)
{
    char *r1 = s1;
    const char *r2 = s2;
    while (n) {
        *r1++ = *r2++;
        -n;
    }
    return s1;
}

Adapted from uclibc. Here, the variable types do not matter at all - internally, the function decides to manipulate memory in sizeof(char) types (char is usually but not always a byte wide) but it could equally have operated on uint64_t or some other value. All the type does here is control how many bytes are being considered from the start address as part of the type.

To give you another table, here's a comparison of some type sizes:

Address of ptr  | Type in code    | Memory it "sees" when dereferenced
===========================================================================
0x7fffffffdf90  | unsigned 64-bit | 0x8c 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffdf90  | unsigned 32-bit | 0x8c 0xdf 0xff 0xff 
0x7fffffffdf90  | unsigned 16-bit | 0x8c 0xdf 
0x7fffffffdf90  | unsigned  8-bit | 0x8c 
0x7fffffffdf90  | void*           | Doesn't know how wide it is.

You might wonder why there are no casts in that libc function - well, there is no need. All pointers are the same size so nothing else needs to be done.


Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!