Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ static_cast from int* to void* to char* - can you help me to understand this code?

I'm a beginner in C++, and I have problem with understanding some code.

I had an exercise to do, to write function which returns size of int, and do not use sizeof() and reinterpret_cast. Someone gave me solution, but I do not understand how it works. Can you please help me to understand it? This is the code:

int intSize() {
  int intArray[10];
  int * intPtr1;
  int * intPtr2;

  intPtr1 = &intArray[1];
  intPtr2 = &intArray[2];

  //Why cast int pointer to void pointer?
  void* voidPtr1 = static_cast<void*>(intPtr1);

  //why cast void pointer to char pointer?
  char* charPtr1 = static_cast<char*>(voidPtr1);

  void* voidPtr2 = static_cast<void*>(intPtr2);
  char* charPtr2 = static_cast<char*>(voidPtr2);

  //when I try to print 'charPtr1' there is nothing printed
  //when try to print charPtr2 - charPtr1, there is correct value shown - 4, why?
  return charPtr2 - charPtr1;
}

To summarize what I don't understand is, why we have to change int* to void* and then to char* to do this task? And why we have the result when we subtract charPtr2 and charPtr1, but there is nothing shown when try to print only charPtr1?

like image 304
user2550609 Avatar asked Jul 04 '13 13:07

user2550609


2 Answers

First of all, never do this in real-world code. You will blow off your leg, look like an idiot and all the cool kids will laugh at you.

That being said, here's how it works: The basic idea is that the size of an int is equal to the offset between two elements in an int array in bytes. Ints in an array are tightly packed, so the beginning of the second int comes right after the end of the first one:

int* intPtr1 = &intArray[0];
int* intPtr2 = &intArray[1];

The problem here is that when subtracting two int pointers, you won't get the difference in bytes, but the difference in ints. So intPtr2 - intPtr1 is 1, because they are 1 int apart.

But we are in C++, so we can cast pointers to anything! So instead of using int pointers, we copy the value to char pointers, which are 1 byte in size (at least on most platforms).

char* charPtr1 = reinterpret_cast<char*>(intPtr1);
char* charPtr2 = reinterpret_cast<char*>(intPtr2);

The difference charPtr2 - charPtr1 is the size in bytes. The pointers still point to the same location as before (i.e. the start of the second and first int in the array), but the difference will now be calculated in sizes of char, not in sizes of int.

Since the exercise did not allow reinterpret_cast you will have to resort to another trick. You cannot static_cast from int* to char* directly. This is C++'s way of protecting you from doing something stupid. The trick is to cast to void* first. You can static_cast any pointer type to void* and from void* to any pointer type.

like image 119
ComicSansMS Avatar answered Sep 19 '22 23:09

ComicSansMS


This is the important bit:

intPtr1 = &intArray[1];
intPtr2 = &intArray[2];

This creates two pointers to adjacent ints in the array. The distance between these two pointers is the size of an integer that you're trying to retrieve. However the way that pointer arithmetic works is that if you subtract these two then the compiler will return you the size in terms of ints, which will always be 1.

So what you're doing next is re-casting these as character pointers. Characters are (or de-facto are) 1 byte each, so the difference between these two pointers as character pointers will give you an answer in bytes. That's why you're casting to character pointers and subtracting.

As for via void* - this is to avoid having to use reinterpret_cast. You're not allowed to cast directly from a int* to a char* with static_cast<>, but going via void* removes this restriction since the compiler no longer knows it started with an int*. You could also just use a C-style cast instead, (char*)(intPtr1).

like image 45
Rup Avatar answered Sep 18 '22 23:09

Rup