Weird behavior of pointer - c++

When I compile the given code it doesn't produce any error or warnings. My question here is shouldn't the compiler produce error when compiling the following line *err = "Error message"; because we are dereferencing a pointer to pointer to constant char and assigning a string to it.
Is it allowable to assign anything inside a pointer anything other than address and exactly what is happening in this given scenario?
#include <stdio.h>
void set_error(const char**);
int main(int argc, const char* argv[])
{
const char* err;
set_error(&err);
printf("%s",err);
return 0;
}
void set_error(const char** err1)
{
*err1 = "Error message";
}

const char** err1
That's a pointer to a non-constant pointer to a constant object. Dereferencing it gives a non-constant pointer (to a constant object), which can be assigned to.
To prevent assigning to the const char*, that would also have to be const:
const char * const * err1

"Error message" is not a std::string. It's a const char[]. All string literals in C++ are const char[]. In C, they're char[].

Is it allowable to assign anything inside a pointer anything other than address and exactly what is happening in this given scenario?
You can assign pointer to a pointer. You think about pointer as an address, that's fine to understand concept, but do not mix it with data type. Data type is a pointer, not address. For example to assign address in memory to a pointer you need to cast it to a pointer:
char *pointer = reinterpret_cast<char *>( 0xA000000 );
You may ask how this would compile?
int array[10];
int *ptr = array;
That comes from C - array can be implicitly converted to a pointer to the first element. So it is pointer to pointer assignment again. Now about string literal with double quotes. It is an array as well:
const char str[] = "str";
const char str[] = { 's', 't', 'r', '\0' };
These 2 statements are pretty much the same. And as array can be implicitly converted to pointer to the first element it is fine to assign it to const char *

Related

Why can't you have a non-const char* in C++?

Why does this work:
char foo[6] = "shock";`
while this does not work:
char* bar = "shock"; //error
Why does bar have to be const while foo doesn't? Arrays in C decay to pointers, so don't foo and bar technically have the same types?
Literals are held in reserved areas of memory that are not supposed to be changed by code. Changing the value held at the address storing that literal would mean that every time any other code tried to use that literal, it would find the wrong value in that memory. So it is illegal to modify that memory, and hence illegal to treat it as not constant.
Source
With this declaration:
char foo[6] = "shock";
Variable foo is type array of char and it containes 6 non-const chars. The string literal contains const chars which are copied into the array on initialization.
While with this declaration:
char* bar = "shock"; //error
Variable bar is type pointer to char. You are trying to make it point to the address of "shock" which is a string literal containing const char.
You can't point a pointer to non-const char at a const char.
So you must do this:
const char* bar = "shock";`
because "shock" is a constant, so a pointer to it must be const
for historical reasons C allows this (and causes many errors that lead to SO posts)
char* bar = "shock";
is roughly equivalent to
const char anonymousArray[6] = "shock";
char *bar = anonymousArray;
Arrays decays to pointers. That's not the same as actually being pointers.

Re-assigning a string to character pointer in C or C++

May be my concepts about pointer are not clear but I have a doubt. For integer pointer
# include <stdio.h>
int main()
{
int a = 4;
int *b = &a;
*b = 6;
printf("%d", *b);
}
prints
6
but for character pointer
char *str = "Hello";
*str = "Hlw";
gives error
but
char *str = "Hello";
str = "Hlw";
Works. So, why does it work like this?
A string literal is a const char[] array, which decays into a const char* pointer to the string's 1st char.
char *str = "Hello"; is legal in C, and in C++ up to C++03, but is illegal in C++11 and later. Modern versions do not allow a const char* pointer to be assigned to a non-const char* pointer (without an explicit type-cast, anyay).
Just as *b in your int* example accesses a single int, so too does *str access a single char. So *str = "Hlw"; is trying to assign a const char* pointer to a single char, which it why it fails to compile.
str = "Hlw"; is simply assigning a const char* pointer to str, making it point at a different memory address (if that were legal). In terms of your int* example, that would be the same as doing this:
int a;
int *b = &a;
int c;
b = &c; // <--
When you indirect through an int*, you get lvalue to int. An int is an integer.
When you indirect through a char*, you get lvalue to char. A char is not a string. It is a single character (assuming fixed width character encoding rather than unicode).
The core difference here is that strings are arrays of characters, while integers are not arrays.
str = "Hlw";
This is ill-formed in C++ because string literal is an array of const char, and such array does not implicitly convert to a pointer to non-const char.
Pointers do not have an information whether they point to a single object or to a member of an array.
In this code snippet
int *b = &a;
*b = 6;
the pointer p points to an object of the type int, so dereferencing the pointer you will get an access to this object.
In this code snippet
char *str = "Hello";
*str = "Hlw";
the pointer str points to an object of the type char. It points to the first character of the string literal "Hello". So dereferencing the pointer you will get an access to the first character of the string literal pointed to by the pointer.
Hence instead of this incorrect assignment
*str = "Hlw";
where the left hand side operand has the type char while the right hand side operand has the type char[4] in C and const char[4] in C++ you should write something like
*str = 'G';
that is you may assign a character literal to an object of the type char. You may not assign a string literal to an object of the type char like for example
char c = "Hello";
and the expression *str indeed has the type char.
In this code snippet
char *str = "Hello";
str = "Hlw";
you are reassigning the pointer str itself instead of the pointed by it object.
After the declaration the pointer str points to the first character of the string literal "Hello" that is to the character 'H'.
In the following statement the pointer str is reassigned by the address of the first character of the string literal "Hlw". That ijs now it points to the character 'H' of this string literal.
*b can be replaced by b[0] because, in C, a pointer to anything is also an array of one element of that type. Doing this way helps to understand the issue :
int a = 4;
int *b = &a;
b[0] = 6; //<=== this is correct because b is an array of int
char *str = "Hello";
str[0] = "Hlw"; //<=== but this is not as explained by other answers

Why can we say ' char *c = "Hello"; ' in C++

When I do-
#include <iostream>
int main(){
char *p = "Hello";
}
it works fine, but doing
#include <iostream>
int main(){
char *p = 'H';
// OR
int *x = 5;
}
gives an error: invalid conversion from 'char' to 'char*' [-fpermissive] / 'int' to 'int*'
The issue here is that C++ is a strongly typed language. You have to make sure the type on the right of an = is the same as the type on the left (or there is some well defined conversion that allows the compiler to convert between types).
So we need to know what types literals are: Using a double quote creates a string literal of type char const[] while using a single quote creates a character literal of type char const.
You will have to bear with me on the const part. That makes the discussion much more complicates so we will gloss over it initially.
We also need to know that when arrays are used in expressions they very easily decay into pointers. So it most situations the type of char const[] will decay into char const*.
This should work:
char const* p = "Hello"; // This is valid.
// Both left and right sides have the same type.
// After you consider the array decay into a pointer.
On the other hand
char const* p = 'H'; // The type on the right is `char const'
// While the type on the right has a pointer in it.
Now there are some automatic conversions at play here.
In the original C++03 the compiler was allowed to auto convert string literals from char const* to char*. This is a relic from the old C language that was not as strict on type checking as C++ is now. This allows for this:
char* p = "Hello"; // Because there is an auto conversion
// the compiler is allowed to make
// the conversion from one type to another
Note in later versions of C++ this conversion has been deprecated. SO the compiler will warn you that this is dangerous (because you have removed the const from the type you are allowed to modify it, but the underlying object can't be modified so if you try it will blow up the program).
So why can you assign char const to a char?
char x = 'X';
Here you are copying the original object char const into an object of type char this is perfectly valid. You can not alter or chance the literal but you are allowed to make copies of it. So you can easily remove the outer const in an assignment expression.
char const* const y = "Hello";
char const* z = y; // We remove the outer const
// from a pointer. But the inner
// const (of the object being pointed
// at) can not be removed so easily.
// This works because z is allowed to
// to be changed but hold a value a pointer
// to a value that can not be changed.
Looking at your comment:
#include <iostream>
void test(char *str)
{
std::cout << str << std::endl;
}
int main()
{
test("Hello"); // This is allowed in C++
// Deprecated still means valid.
// Just not a good idea
// There is a allowed conversion from
// char const* to char* for string literals.
char const* x = "test";
test(x); // This is NOT allowed.
// You can not cast away this const.
}
Note: Technically a string literal is char const[]. ie an array of const char. BUT when used in an expression arrays very easily decay into pointers and thus it is sometimes simpler to think of them as char const* but this thinking is abstract and you should know the underlying exact type.

Initializing char pointer

I have a function
ValArgument(char* ptr){
char str[] = "hello world";
ptr = &str[0];
}
In this function, I want to init a char array and add it to the char pointer ptr. I call the function like that:
char* ptr= NULL;
ValArgument(ptr);
The pointer returned still has the value NULL. Why? I expected that the pointer will point onto the char array str[].
The pointer returned still has the value NULL. Why?
Because you passed the pointer by value. That means that the function is given a separate copy of the pointer, and any changes it makes to the pointer will not affect the caller's copy.
You can either pass by reference:
void ValArgument(char *& ptr)
// ^
or return a value:
char * ValArgument();
I expected that the pointer will point onto the char array str[].
No; once you've fixed that problem, it will point to the undead husk of the local variable that was destroyed when the function returned. Any attempt to use the pointer will cause undefined behaviour.
Depending on what you need to do with the string, you might want:
a pointer to a string literal, char const * str = "hello world";. Note that this should be const, since string literals can't be modified.
a pointer to a static array, static char str[] = "hello world";. This means that there is only one string shared by everyone, so any modification will affect everyone.
a pointer to a dynamically allocated array. Don't go there.
a string object, std::string str = "hello world";. This is the least error-prone, since it can be passed around like a simple value.

Explain: Converting 'char **' to 'const char **', Conversion loses qualifiers [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Implicit cast from char** to const char**
Given the following code:
void foo( const char ** buffer );
void bar()
{
char * buffer;
foo( &buffer );
}
Why is it that if the foo() function has a const char * parameter the compiler doesn't complain when passing in a char * variable into it? But when using char **, it cannot convert it to const char **? Does the compiler add any const qualifiers in the former case?
I've read section 4.4 of the C++ standard and it just confused me further.
Yes, you cannot implicitly convert from a T ** to a const T **, because the compiler can no longer guarantee that the const-ness won't be violated.
Consider the following code (borrowed from the C FAQ question on exactly this topic: Why can't I pass a char ** to a function which expects a const char **?):
const char c = 'x';
char *p1;
const char **p2 = &p1; // 3
*p2 = &c;
*p1 = 'X'; // 5
If the compiler allowed line 3, then line 5 would end up writing to a const object.
Consider:
char const someText[] = "abcd";
void
foo( char const** buffer )
{
*buffer = someText;
}
void
bar()
{
char* buffer;
foo( &buffer );
*buffer = 'x';
}
If this were legal, it would be possible to modify a const object
without an intervening const_cast. The conversion is forbidden
because it violates const-ness.
You are probably confusing the level of indirection the const applies to.
A char** can be described as pointer to a pointer to a character whereas a const char** can be described as pointer to a pointer to a constant character.
So when we write this differently, we have pointer to A (where A = pointer to character) and we have pointer to B (where B = pointer to a constant character).
Clearly now (I hope) A and B are distinct types, as such a pointer to A can not be assigned toa a pointer to B (and vice versa).