Board index » cppbuilder » Question about const and pointers

Question about const and pointers


2005-07-05 10:27:14 AM
cppbuilder10
Suppose one declares as an argument to a method:
void MyClass:MyMethod(const char *SomeStrPtr);
The const applies to the pointer address passed in on SomeStrPtr?
Suppose one wanted to make the string pointed to unchangeable by the method. Would
one do what I show above or do something like:
void MyClass:MyMethod(char const *SomeStrPtr);
or maybe:
void MyClass:MyMethod(char *SomeStrPtr const);
??
Also suppose I want a method that returns a pointer to a string and I want any caller
of that method to not be able to modify the returned string. How to declare the
return type?
const char* myclass::GetString();
or
char const* myclass::GetString();
??
 
 

Re:Question about const and pointers

"Randall Parker" < XXXX@XXXXX.COM >wrote in
message news:42c9f002$ XXXX@XXXXX.COM ...
Quote
Suppose one declares as an argument to a method:

void MyClass:MyMethod(const char *SomeStrPtr);

The const applies to the pointer address passed in on SomeStrPtr?

Suppose one wanted to make the string pointed to unchangeable by the
method. Would one do what I show above or do something like:

void MyClass:MyMethod(char const *SomeStrPtr);

or maybe:

void MyClass:MyMethod(char *SomeStrPtr const);
Why not void MyClass::MyMethod( AnsiString SomeStr ); ?
Quote
Also suppose I want a method that returns a pointer to a string and I
want any caller of that method to not be able to modify the returned
string. How to declare the return type?

const char* myclass::GetString();

or

char const* myclass::GetString();
And const AnsiString myclass::GetString(); ?
Just curious.
Jonathan
 

Re:Question about const and pointers

Randall Parker < XXXX@XXXXX.COM >writes:
Quote
Suppose one declares as an argument to a method:

void MyClass:MyMethod(const char *SomeStrPtr);
[I'll skip over the fact that the scope operator has two colons and
that a member function declaration like this one is never correct. For
the sake of simplicity, let's assume that we are discussing the
declaration of a non-member function MyMethod.]
Quote
The const applies to the pointer address passed in on SomeStrPtr?
No.
const applies to the thing immediately to its left. If there isn't
anything to its left (as in the above case), it applies to the thing
immediately on its right.
In English, "const char *SomeStrPtr" thus reads "SomeStrPtr is a
pointer to const char".
Quote
Suppose one wanted to make the string pointed to unchangeable by the
method. Would one do what I show above or do something like:

void MyClass:MyMethod(char const *SomeStrPtr);
Yes. Because this declaration and the one quote above are
synonyms. This time, there is something on the left of the const, so
the const applies to it.
Quote
or maybe:

void MyClass:MyMethod(char *SomeStrPtr const);
No. That's a syntax error, I think. Do you think of
void MyMethod(char * const SomeStrPtr);
That would mean that SomeStrPtr is a const pointer to (non-const)
char.
In a function declaration that isn't a definition, "top-level consts"
like this one are mere comments. After all, these declarations are
there to allow the compiler to match a call to the right function and
to give the caller some guarantees about the function's behavior.
Now the caller couldn't care less if MyMethod modifies the pointer
SomeStrPtr.
Quote
Also suppose I want a method that returns a pointer to a string and
I want any caller of that method to not be able to modify the
returned string. How to declare the return type?

const char* myclass::GetString();

or

char const* myclass::GetString();
These are synonyms.
Apart from all this, you should seriously consider using std::string
(or some other string class) rather than raw pointers to char.
 

{smallsort}

Re:Question about const and pointers

"Jonathan Benedicto" < XXXX@XXXXX.COM >wrote in message
Quote
Why not void MyClass::MyMethod( AnsiString SomeStr ); ?
AnsiString is a VCL class, and as such the VCL must be included in the
project. Not all projects that BCB is capable of making require the VCL,
and there are cases where VCL projects have non-VCL interfaces.
Gambit
 

Re:Question about const and pointers

To get it complete:
char *const str1 = "Hello, world"; // A constant pointer
char const *str2 = "Hello, world"; // A pointer to a constant character
string.
str1 = "Hi, there!" // is illegal, because it points str1 to something else.
str2 = "Hi, there!" // is okay, because you change the pointer, but not
the const string
But:
str1[0] = 'B'; // is okay, because the string is non-constant and you're
not trying to change the pointer
str2[0] = 'B'; // is illegal, because you try to change the constant string
Right?
Best regards
Matthias
 

Re:Question about const and pointers

Matthias Paetzold < XXXX@XXXXX.COM >wrote:
Quote
char *const str1 = "Hello, world"; // A constant pointer
Yes ... but never write this.
Quote
str1 = "Hi, there!" // is illegal, because it points str1 to something else.
Yes
Quote
str1[0] = 'B'; // is okay, because the string is non-constant and you're
not trying to change the pointer
Actually, not really okay. It's just something the compiler (probably)
won't notice, and which may make your program crash and burn. The
problem being that the language allows your first line, when it
shouldn't, since "Hello, world" is really a array of const char.
A better example would have been to use
char *const str1 = strdup("Hello, world");
Alan Bellingham
--
ACCU Conference 2006 - 19-22 April, Randolph Hotel, Oxford, UK
 

Re:Question about const and pointers

Matthias Paetzold < XXXX@XXXXX.COM >writes:
Quote
To get it complete:
char *const str1 = "Hello, world"; // A constant pointer
char const *str2 = "Hello, world"; // A pointer to a constant
character string.
A few points: a string literal "Hello, world" is an array of const
characters, but C++ allows the silent removal of const while
initializing a pointer for backwards compatibility.
For example:
// Deprecated conversion. Changing the data pointed to by c results
// in undefined behavior:
char * c = "hello world";
const char * c2 = "hello world"; // ok, c2 points to const data
char const * c3 = "hello world"; // ok, c3 points to const data
// Note, c2 and c3 are EXACTLY the same type.
// deprecated conversion. c4 is const, so the pointer can't change,
// but the data to which it points is not const. Since in reality the
// string literal is const, modifying it is undefined behavior.
char * const c4 = "hello world";
char const * const c5 = "hello world"; // ok
const char * const c6 = "hello world"; // ok (same as c5)
Quote
str1 = "Hi, there!" // is illegal, because it points str1 to something else.
No, it's invalid because str1 is const.
Quote
str2 = "Hi, there!" // is okay, because you change the pointer, but
not the const string
Correct, and because str2 points to const data, the assignment is allowed.
Quote

But:
str1[0] = 'B'; // is okay, because the string is non-constant and
you're not trying to change the pointer
NO! It compiles, but it's not ok, because str1 points to a string
literal, which is const data. Modifying a string literal is undefined
behavior.
This is specifically the reason that the conversion from const to
non-const data is generally not allowed in C++. Once you lose
const-ness in your type system, the compiler can't help you detect
such mistakes.
Quote
str2[0] = 'B'; // is illegal, because you try to change the constant string
This won't compile because the C++ type system will catch it. Trying
to modify const data shouldn't compile.
--
Chris (TeamB);
 

Re:Question about const and pointers

"Remy Lebeau (TeamB)" < XXXX@XXXXX.COM >wrote in message
Quote
AnsiString is a VCL class, and as such the VCL must be included in the
project. Not all projects that BCB is capable of making require the VCL,
and there are cases where VCL projects have non-VCL interfaces.
Thank you. Isn't the best part about BCB the VCL ? If BCB provides such a
useful library, isn't it better to use it ?
I guess the OP could use std::string instead.
Recently I read a book on secure programming, and it has taught me to be
careful about things like using char*'s like that.
Jonathan
 

Re:Question about const and pointers

"Jonathan Benedicto" < XXXX@XXXXX.COM >writes:
Quote
Thank you. Isn't the best part about BCB the VCL ? If BCB provides such a
useful library, isn't it better to use it ?
The goal of programming is to minimize dependencies. If only part of
your application really needs to depend on the VCL, your codebase is
better to only have that part of your application actually use the
VCL.
Yes, the VCL is good, but it's non-standard and only portable to
platforms that Borland supports. Currently, for C++, that's just
windows.
If you have to compile on, say, Mac OSX, and your whole codebase
depends on VCL, you have to rewrite your whole codebase to use
something else. If only part of your application uses VCL, you only
have to rewrite part of your application. Etc.
Quote
I guess the OP could use std::string instead.
Standardization has a lot of benefits, and using what comes with every
C++ commiler aides in portability and on finding other C++ programmers
that will know what your code does.
--
Chris (TeamB);
 

Re:Question about const and pointers

"Chris Uzdavinis (TeamB)" < XXXX@XXXXX.COM >wrote in message
Quote
The goal of programming is to minimize dependencies. If only part of
your application really needs to depend on the VCL, your codebase is
better to only have that part of your application actually use the
VCL.

Yes, the VCL is good, but it's non-standard and only portable to
platforms that Borland supports. Currently, for C++, that's just
windows.

If you have to compile on, say, Mac OSX, and your whole codebase
depends on VCL, you have to rewrite your whole codebase to use
something else. If only part of your application uses VCL, you only
have to rewrite part of your application. Etc.
Standardization has a lot of benefits, and using what comes with every
C++ commiler aides in portability and on finding other C++ programmers
that will know what your code does.
Thank you.
Jonathan
 

Re:Question about const and pointers

Thanks Chris!
One remark:
These lines
Quote
>char *const str1 = "Hello, world"; // A constant pointer
>char const *str2 = "Hello, world"; // A pointer to a constant
>character string.
are copied from the Borland BCB5 Help file
as well as
Quote
>str1 = "Hi, there!" // is illegal, because it points str1 to something else.
>str2 = "Hi, there!" // is okay, because you change the pointer, but
>not the const string
. You can check that for the entry "const".
Quote
// deprecated conversion. c4 is const, so the pointer can't change,
// but the data to which it points is not const. Since in reality the
// string literal is const, modifying it is undefined behavior.
char * const c4 = "hello world";

char const * const c5 = "hello world"; // ok
const char * const c6 = "hello world"; // ok (same as c5)
So the string "hello world" is in itself const?
If I define
char * const c4 = {'h','e','l','l','o',' ','w','o','r','l','d'};
is this different, or also a const literal string?
Are the only possibilities to get valid and defined compiler results
char c4[20] = "hello world";
char * const c5 = c4; // since the array identifier is a pointer
or as Allan suggested
char *const c6 = strdup("hello world");
to produce a const pointer to a non-const string, which in turn I can
modify by
c5[0] = 'b';
c6[0] = 'b';
?
Best regards
Matthias
 

Re:Question about const and pointers

Matthias Paetzold < XXXX@XXXXX.COM >writes:
Quote
Thanks Chris!
One remark:
These lines
>>char *const str1 = "Hello, world"; // A constant pointer
>>char const *str2 = "Hello, world"; // A pointer to a constant
>>character string.
are copied from the Borland BCB5 Help file
Those descriptions are correct, but incomplete, since it doesn't say
what str1 points to nor what type of pointer str2 is. A better
descriptoin would be that str1 is a const pointer to non-const data,
and str2 is a non-const pointer to const data.
Quote
as well as
>>str1 = "Hi, there!" // is illegal, because it points str1 to something else.
This description is just wrong. This is trying to re-assign the
address to which str1 points. That is, it's trying to change the
pointer, but since the pointer is const we're not allowed to change
it.
Quote
>>str2 = "Hi, there!" // is okay, because you change the pointer, but
>>not the const string
This is correct.
Quote
. You can check that for the entry "const".
My machine with BCB installed is at home right now, and I'm in the
office...
...
Quote
So the string "hello world" is in itself const?
Yes, it's an array of const characters.
Quote
If I define
char * const c4 = {'h','e','l','l','o',' ','w','o','r','l','d'};
is this different, or also a const literal string?
This is invalid C++. You can only use aggregate initialization for
arrays or for structs/classes, but you're trying to initialize a
pointer. Had you done this:
char const c4[] = {'h','e','l','l','o',' ','w','o','r','l','d'};
It would compile, but has different meaning. Now c4 is an array that
has its own memory reserved, and that memory is initialized with the
string "hello world". That is, it's YOUR memory, and if you didn't
declare c4 to be const, you'd be allowed to change the data without
any memory corruption. Consequently, the sizeof(c4) will be the
number of bytes it takes to hold the string literal, plus one for a
NULL termination.
Constrast that with:
char const * c5 = "hello world";
c5 is just a pointer, and it holds the address of the string literal.
That string literal must live somewhere, but it's not in memory that
your code explicitly declared or allocated. Therefore, it must be
treated as "someone else's" memory, and your program is allowed to
know the address of it but is NOT allowed to modify it. The
sizeof(c5) is just the sizeof a pointer.
Quote
Are the only possibilities to get valid and defined compiler results
char c4[20] = "hello world";
This is ok, as it's copying "hellow world" into your array. From that
point on, you're not accessing the string literal, but only a copy of
it that is stored in your array.
Quote
char * const c5 = c4; // since the array identifier is a pointer
This is ok too, as it makes a pointer that points to your array.
The pointer cannot be changed later since it's const, but you can
change the data inside the array through this pointer.
Quote
or as Allan suggested
char *const c6 = strdup("hello world");
I really dislike strdup in a C++ application, because most memory is
allocated with "new" in C++, but strdup uses malloc. That complicates
memory management because you have to keep trace of which pointers
were allocated by which function, because you MUST deallocate them
with the proper deallocaion function. For example, you must use
"free" to deallocate buffers allocated with malloc, and you must use
"delete" to deallocate memory allocated with new. Mixing these up
only increases the chances of mismatching the allocation and
deallocation routines (which results in undefined behavior, but
usually it'll eventually result in memory corruption and a crash.)
Quote
to produce a const pointer to a non-const string, which in turn I can
modify by
c5[0] = 'b';
c6[0] = 'b';
?
You can do that, but why? What exactly do you really need to do? I
suggest always trying to minimize the amount of dynamically allocated
memory when it's possible, to reduce program complexity and reduce the
chances of errors.
--
Chris (TeamB);
 

Re:Question about const and pointers

Thanks again for your extensive remarks, Chris.
Quote
>>>str1 = "Hi, there!" // is illegal, because it points str1 to something else.

This description is just wrong. This is trying to re-assign the
address to which str1 points. That is, it's trying to change the
pointer, but since the pointer is const we're not allowed to change
it.
Well, I understood it exactly that way.
Quote
Constrast that with:

char const * c5 = "hello world";

c5 is just a pointer, and it holds the address of the string literal.
That string literal must live somewhere, but it's not in memory that
your code explicitly declared or allocated. Therefore, it must be
treated as "someone else's" memory, and your program is allowed to
know the address of it but is NOT allowed to modify it. The
sizeof(c5) is just the sizeof a pointer.
Now, if it's not my program that allocates the memory for the string
literal and if it's also not responsible for allocating and freeing it,
who is? And where is it allocated? What's the state of this part of
memory? What are the conditions for the (automatic) allocation and
freeing, especially up to which point you are able to access it and not
end up undefined?
Sometimes I get the warning "Temporary used for parameter ... " when
using a string literal as argument in a function call. I tend to ignore
it, because it doesn't seem to produce any unusual behaviour. But I'm
not sure about it.
On the other hand I noted that the string class (basic_string) began to
behave strangely, so that I had to make up my own functions for
assigning and appending to get this done properly. I tracked the
misbehaviour down to the basic 'replace' routine, but this doesn't exist
as source code, so I'm not able to analyse what really happens and what
I've done wrong to produce this misbehaviour. So some better
understanding of string types might help to solve that riddle.
Quote
I really dislike strdup in a C++ application, because most memory is
allocated with "new" in C++, but strdup uses malloc. That complicates
memory management because you have to keep trace of which pointers
were allocated by which function, because you MUST deallocate them
with the proper deallocaion function. For example, you must use
"free" to deallocate buffers allocated with malloc, and you must use
"delete" to deallocate memory allocated with new. Mixing these up
only increases the chances of mismatching the allocation and
deallocation routines (which results in undefined behavior, but
usually it'll eventually result in memory corruption and a crash.)
Isn't 'new' just a polymorphic way to choose the right malloc function
with the proper parameters for the respective type and to include a type
check also? When using 'new' and 'delete' you don't have to care about
how to use 'malloc' and 'free'. But in the background they're still
called by 'new' and 'delete', aren't they?
Quote
You can do that, but why? What exactly do you really need to do? I
suggest always trying to minimize the amount of dynamically allocated
memory when it's possible, to reduce program complexity and reduce the
chances of errors.
There's nothing specific about it. The discussion was just about what's
possible and valid and what's not.
Best regards
Matthias
 

Re:Question about const and pointers

Matthias Paetzold < XXXX@XXXXX.COM >writes:
Quote
Now, if it's not my program that allocates the memory for the string
literal and if it's also not responsible for allocating and freeing
it, who is?
It's your program, but not your code. That is, you're just "using"
the string literal, and it just appears magically because you use it.
The compiler generates code to make it exist. The compiler generates
your application to do what you say, but it also adds its own code to
do some things you don't say. Since this memory is in your
application, it's really yours, but since you yourself didn't
explicitly write the code to make the memory writeable, it isn't.
The compiler can put it in the data section of your application, and
it can be loaded into an OS page with the "readonly" flag marked. If
you try to write to it, the OS may send your application a signal,
access violation, or possibly allow you to corrupt memory and go on
your merry way. In all cases, it's undefined behavior.
Quote
And where is it allocated? What's the state of this part of memory?
What are the conditions for the (automatic) allocation and freeing,
especially up to which point you are able to access it and not end
up undefined?
If you declare the variable, then you can write into the memory used
by that variable. For example:
int main()
{
int x;
}
You are allowed to write into the variable x because it is something
you allocated (on the stack) with automatic storage. Since ints are 4
bytes in this case, you can write 4 bytes into x, but no more.
If you allocate memory with malloc or new[], you can also write into
those bytes, but no more. With a dynamically alloated piece of
memory, you hold onto it with a pointer, and you can change both the
pointer and the data to which it points.
For a string literal, you didn't create a variable, you just use the
string literal directly. The compiler itself has to make space for
this data, and when you initialize a pointer to point to this space,
you're looking at memory that you yourself didn't allocate. (Thank
the compiler.)
If you declare an array and initalize it with a string literal, your
array has space, and the string literal is copied into that space.
Afterwords, if you read or write your array, you're accessing your
memory, and not the memory that was in the string literal that was
used to initialize it.
Quote
Sometimes I get the warning "Temporary used for parameter ... " when
using a string literal as argument in a function call.
Hmmm, usually that occurs when you have references and conversions.
In several cases Borland's compiler allows you to bind temporary
variables to references-to-nonconst-data, which should not be allowed.
Quote
I tend to ignore it, because it doesn't seem to produce any unusual
behaviour.
The problem could be if you modify the argument, you're modifying a
copy, and not the original. If you expect the caller's variable to be
changed, you might not see the change take place once the function
returns.
Quote
But I'm not sure about it. On the other hand I noted that the
string class (basic_string) began to behave strangely, so that I had
to make up my own functions for assigning and appending to get this
done properly.
Do you have any examples? What do you mean by "began to behave
strangely"?
Quote
I tracked the misbehaviour down to the basic 'replace' routine, but
this doesn't exist as source code, so I'm not able to analyse what
really happens and what I've done wrong to produce this
misbehaviour. So some better understanding of string types might
help to solve that riddle.
I'm curious what you're seeing.
Quote
Isn't 'new' just a polymorphic way to choose the right malloc function
with the proper parameters for the respective type and to include a
type check also?
No, it is a totally seperate way to allocate memory. It's typesafe,
yes, but it also may invoke constructors of objects, whereas malloc
does not. Further, new can be overloaded either globally or on a
per-class type. So you can have radically different behavior from
using malloc and new. (I can pre-allocate a million objects, for
example, and then store them in a fast-to-access list. Then when I
invoke new on my special object, rather than using the normal memory
manager it may just select one of the pre-allocated objects from my
memory pool, for "lightning-fast" allocations.)
I can then override delete to put the memory back on this freelist.
For example.
Quote
When using 'new' and 'delete' you don't have to care about how to
use 'malloc' and 'free'. But in the background they're still called
by 'new' and 'delete', aren't they?
Maybe sometimes, but not always. You don't know, it's not specified,
and is an implementation detail. Since I can overload new to not use
malloc at all, I can guarantee that it does not always use malloc.
Further, malloc does not invoke constructors... a big mistake if you
use an object before it has been created. The constructor must
successfully complete *before* the object's lifetime officially
begins. Before it completes, it's just "memory". And vice-versa for
destructors, their lifetime has ended the moment we enter a
destructor. Calling free() does not invoke destructors. And if the
memory was allocated with new in a way that doesn't use malloc, but
you call free on the object, not only do you lose your destructor
calls, but you're trying to put memory on the memory manager's
freelist that didn't originate there. A serious bug, and certain
disaster.
--
Chris (TeamB);
 

Re:Question about const and pointers

Jonathan Benedicto wrote:
Quote
>void MyClass:MyMethod(char *SomeStrPtr const);


Why not void MyClass::MyMethod( AnsiString SomeStr ); ?
Mostly because I avoid using Borland's classes unless necessary.
However, the exercise is manufactured to ask the question. I've always wondered about
this but was never clear on it.
Quote
And const AnsiString myclass::GetString(); ?
But doesn't that cause a copy of the whole object to be placed on the stack?
Quote

Just curious.

Jonathan