简体   繁体   中英

Confused about the pointers and generic(void) pointers in C

I missed a couple classes, and don't really understand the flowing lecture slide's examples about the void pointer.

  1. In the line "no,no,no", why we cannot deference P since P has been assigned a real pointer type q?

  2. In the line " ?? ", is it legal to cast a pointer to a integer? ( I think it can compile because C doesn't check cast, but don't know what kind of result it is going to get

in this example Conversion of integer pointer to integer
he casted *p = 10; a = (int)p; and someone answered that a is going to be a very large value. but in my case,is (int) *p... Is it the same case as the example that I gave above?

  1. In the last line, I am confused about the expression. Is * ((int *)p) actually = p? Thanks!

在此输入图像描述

在此输入图像描述

"void*" means "this value is a pointer to something, but I'm not telling you what it points to". Start with this statement, and everything makes sense.

So it should be clear that you can't dereference a void*, because nobody told you what it points to! If you don't know whether it points to an int, a char, or some struct, what do you think should happen if you dereference it?

In C, you can assign a void* to any pointer, for example to an int*. The compiler doesn't know what the void* points to, but when you write r = p; the compiler says "I hope you know what you are doing, I trust you. " (A C++ compiler in the same situation doesn't trust you). If the void* p did indeed point to an int, everything is fine. Otherwise, things are more or less bad.

The ?? one is wrong. You can't dereference *p. Doesn't matter what you do afterwards, *p isn't allowed. And no pointer is cast to an integer. There is an attempt to dereference a pointer, which isn't allowed. The result of the dereference couldn't be anything useful since you don't know what p points to, so you have nothing useful to cast to an int.

Now what happens in * (int *) p: p is a void * - a pointer to something, but you don't know what it points to. (int * )p is a cast: p is converted to an int*. That means the compiler will believe that (int*)p points to an int - that may or may not be true. * (int * ) p dereferences the int*: So in total, you convinced the compiler that p points to an int, and to read the int that p hopefully points to. If p did actually point to an int, it's fine. If p didn't point to an int, you're in trouble.

Void pointers are specifically pointers that do not have a data type associated with them. You have to recast them to a specific type (Ie: int*) to have the ability to access their contents.

To clarify your specific points:

1) While p may have a pointer address, it does not have a specific data type associated to it (The data type is necessary for accessing the contents)

2) The result of this action is probably deterministic on the contents. I wouldn't recommend this as a good practice.

3) This is telling the compiler to treat the void pointer as an integer pointer. Therefore, it "knows" the data type associated with it, and accesses it as an int.

In the line "no,no,no", why we cannot deference P since P has been assigned a real pointer type q?

int a =2, *q = a, *r; //line 1
void *p;  //line 2
p = q; // line 3
r = p; // line 4

With line 3 above, you are assigning the pointer q to p . But it doesn't change the type of p - it's still a void pointer. So you can't dereference it. So the following is invalid:

printf("%d\n", *p); // p is a void pointer - dereferencing here is illegal

Alternatively, you can do:

printf("%d\n", *((int*) p)); // p is a void pointer

The above is valid. Because, we convert p to int * before dereferencing it. Consider an example,

void func(void *p)
{
   // what's `p` point to here?
}

int main(void)
{
   int i = 5;
   long x = 10;
   void *p;

   p = &i; // line 1
   p = &x; //line 2

func(p);
}

You can comment out either line1 or line2 but is there anyway in func() to know whether p is pointing to a long data or int data? This is no different to your case.

But the conversion from void * to type * (data pointer) is always necessary before accessing the data its pointing to. Note that it can't be any data pointer though. For example,

int i = 42;
float f = 4.0;

void *p = &f;

printf("%d\n", *((int*))p); // wrong!

This is illegal. p points to a float * . Dereferencing as if it's pointing to a int data is wrong.


In the line " ?? ", is it legal to cast a pointer to a integer?

printf("%d\n", (int) *p);

Since p being a void* , you are not allowed dereference in the first place. The cast after dereferencing doesn't change anything and the dereferencing is illegal.

If you were to cast the pointer itself like:

printf("%d\n", (int)p);

This has implementation-defined behaviour in C. C also provides intptr_t / uintptr_t types for the integer to pointer conversions.

If a pointer p has type void * , then the expression *p has type void , and the void type has no value 1 ; that's why you have to convert the pointer to a "real" pointer type before dereferencing it.

Pointers and integers may be converted to each other, but the values are not guaranteed to be meaningful. However, that's not what the line marked ?? is doing; it's attempting to dereference p and cast the result, but since p is a pointer to void , that's not allowed.

The expression *((int *) p) casts p from pointer to void to pointer to int and the dereferences the result. Since p points to a , this evaluates to the value of a ( 2 ).


1. The void type is an incomplete type that cannot be completed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM