c++pointersconstantsmemory-address

Char array has different address when copied by pointer


What I'm trying to do is cheat the const-ness of the member variables of the class. So I have the following code:

"StringView.hh":

#pragma once
#include <cstring>

class StringView {
public:
  const char* _begin = nullptr;
  const char* _end = nullptr;

  explicit StringView(const char* str) : _begin(str), _end(str + strlen(str)) {}
};

main.cpp:

#include <iostream>
#include "StringView.hh"

int main() {
  char str[] = "cat";
  StringView sv(str);

  std::cout << str << '\n';

  void *pointer = reinterpret_cast<void *>(&sv);
//  *(char *) pointer = 'b'; // I would also like to set it to a whole string, but that doesn't work, it would work if it was std::string, not char *, maybe char ** would work?
//  std::cout << str << '\n';

  std::cout << "Pointer to StringView: " << pointer << "\n";
  std::cout << "String: " << (void *)str << "\n";
  std::cout << "String from StringView _begin: " << (void *)sv._begin << "\n";

  return 0;
}

Result:

cat
Pointer to StringView: 0xe25bffde0
String: 0xe25bffdf4
String from StringView _begin: 0xe25bffdf4

So the string and the string from StringView have the exact same address. This happens both on cmake and gcc, however the pointer in main.cpp doesn't. Why is that?

When I uncomment the lines of code I get this:

cat
cat
Pointer to StringView: 0x81491ffc50
String: 0x81491ffc64
String from StringView _begin: 0x81491ffc62

Now all 3 have different addresses. Why is that? Is there any way I can cheat this so I can go through the const qualifier without using the obvious const_cast<>()? This also doesn't works in c(using struct, removing explicit, using printf, etc).

I know that const strings that aren't in an object (such as StringView sv) are stored in some read-only memory that is different from the variables memory. It's like in the global variables/ static variables space, so that's why I made str not a const char[] and it still is in some other memory that changes based on whether I change that memory and other stuff. Also I'm not using std::string, because it has an overloaded operator =, and that breaks the const-ness, because it's different in this case.

Also by default _begin and _end should be private I just made them public to check the addresses.

Code in c:

#include <stdio.h>
#include <string.h>

struct StringView {
  const char* _begin;
  const char* _end;
};

struct StringView create(const char *str) {
  struct StringView a = {str, str + strlen(str)};
  return a;
}

int main() {
  char str[] = "cat";
  struct StringView sv = create(str);

  printf("%s\n", str);

  void *pointer = (void *) &sv;
  *(char *) pointer = 'b';
  printf("%s\n", str);

  printf("Pointer to StringView: %p\n", pointer);
  printf("String: %p\n", (void *)str);
  printf("String from StringView _begin: %p\n", (void *)sv._begin);

  return 0;
}

Solution

  • In your case, first member has indeed same address as the struct, so

    &sv == &sv._begin
    

    You cast in the wrong type, it should be

    void *pointer = (void *) &sv;
    **(char **) pointer = 'b'; // *const_cast<char*>(sv._begin) = 'b'
    

    Demo

    You actually change the value of the pointer, something like (depending of endianess)

    sv._begin = (sv._begin & 0xFFFFFF00) | 'b'; // or (sv._begin & 0x00FFFFFF) | ('b' << 24)