0

Here's another 'reinventing-the-wheel' problem we were given in our Introduction to C++ classes:

Write a function that returns the position of the first occurrence of a sequence of characters in a string, i.e. a variation of the strstr function.

I started writing the function as follows:

int strstr2(const char *text, const char *pattern) {
    int pos = 0;
    char *temp;
    temp = text;
}

I thought I'd remember the address of the first character of the string for future use within the function, but the compiler said:

A value of type "const char*" cannot be assigned to an entity of type "char*".

I know that one cannot change a constant once it has been initialized, but why am I not able to assign a pointer to a constant char to another non-constant pointer?

I read several questions referring to pointers and constants, and it seems the bottom of the accepted answer to this post might answer my question, but I'm not a hundred percent sure, as the discussion is still at too advanced a level for me.

My second question is, what is the workaround? How can I define a pointer pointing at the beginning of the string?

Thanks.

John Allison
  • 966
  • 1
  • 10
  • 30
  • 4
    `text` isn't `const`. It's a non-`const` pointer to a `const char`. – François Andrieux Nov 19 '18 at 15:57
  • `const char* temp = text` – Peter Ruderman Nov 19 '18 at 15:58
  • 1
    These 'reinventing-the-wheel' problems are gold in introductory level, so I hope you didn't write that with a tone . . . :) – gsamaras Nov 19 '18 at 16:00
  • Short survival guide: 1. Write types "backwards". 2. Read types "backwards" (`char const *foo` --from-right-to-left--> "foo" is a pointer (`*`) to a `const`ant `char`acter.) – Swordfish Nov 19 '18 at 16:02
  • @FrançoisAndrieux, yes, I get that. That's what I meant in 'why am I not able to assign a pointer to a constant char to another non-constant pointer'. But that doesn't help. I'm trying to assign a pointer to a constant character to another pointer, i.e. initialize the 2nd pointer, and this doesn't happen, so why? – John Allison Nov 19 '18 at 16:02
  • 1
    As already mentioned, there are no `const` pointers here. There is pointer to `const char` and pointer to `char` (`temp`). Why assigning the former to the latter might be a bad idea should be rather obvious (you'd lose `const` qualifier on data which could lead to a very bad things if you attempt to modify it). – Dan M. Nov 19 '18 at 16:03
  • 1
    You are getting the error message because the types of `temp` and `text` are not compatible. – Swordfish Nov 19 '18 at 16:03
  • 1
    @DanM., is it because, if this was allowed, one could write something like `*temp = 'a'`, i.e. try to alter the constant char? – John Allison Nov 19 '18 at 16:06
  • 1
    Bingo Bingo Bingo – Swordfish Nov 19 '18 at 16:07
  • https://isocpp.org/wiki/faq/const-correctness#ptr-to-const – Ripi2 Nov 19 '18 at 16:07

3 Answers3

8

This has to do with const-correctness. const char *text means text is a pointer to a constant char. That means if you try to do something like

*text = 'a'

The compiler will issue an error since you are trying to modify a const object. If you could do

char *temp;
temp = text;

then you could do

*temp = 'a'

and there would be no error, even though you just modified a const object. This is why C++ requires you to use const_cast if you actually want to cast away const (there are a few use cases for this but they are by far not what you normally want to do).


Danger, there be dragons below. Be very, very careful if you decide to use const_cast

Lets say you have to deal with an old API call that only takes a char*, but it guarantees it wont modify, then you could use something like

int wrap_api(const char *text)
{
    return api_call(const_cast<char*>(text));
}

and this will be "okay" since api_call guarantees it wont modify the string. If on the other hand api_call could modify then this would only be legal if what text points to isn't actually const like

char foo[] = "test"
wrap_api(foo);

would be legal if foo gets modified but

const char foo* = "test"
wrap_api(foo);

would not be legal if foo gets modified and is undefined behavior.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • 4
    I would not even mention `const_cast` here. – SergeyA Nov 19 '18 at 16:07
  • 1
    Worth stressing that modifying `const` object after discarding the qualifier is an UB and a serious error. The given cod doesn't warrant any casts and could(should) be fixed differently instead. If you find yourself in need of `const_cast` chances are you are doing something wrong. – Dan M. Nov 19 '18 at 16:08
2

If the assignment would be allowed, you would be able to legally write:

*temp = 'x'; // write to char* is legal in general

But that would be bad in this case because you'd be writing to a constant.

The standard could have said that the temp = text assignment is legal and the *text = 'x' is undefined behavior, but that wouldn't make sense because the only difference between T* and const T* is whether you can write to it.

So it is only logical that C++ disallows assigning a T* type the value of a const T* type to save you from later doing something bad, and instead forces you to use const char* temp; temp = text; in this case.

palotasb
  • 4,108
  • 3
  • 24
  • 32
1

This is a part of const-correctness type safety. Since text is a pointer to const char, you can't modify the characters it points to through it. This is a good thing and a safety measure.

However, the whole safety would be invalidated if it would be allowed to assign a pointer to non-const character to it! Because than you would modify the character through said pointer and bypass the safety!

Because of that, this assignment is not allowed. To fix it, mark your temp as pointer to const char as well: const char* temp.

SergeyA
  • 61,605
  • 5
  • 78
  • 137