Void pointers

P

pauldepstein

This code comes from cpluplus.com

/* memcpy example */
#include <stdio.h>
#include <string.h>

int main ()
{
char str1[]="Sample string";
char str2[40];
char str3[40];
memcpy (str2,str1,strlen(str1)+1);
memcpy (str3,"copy successful",16);
printf ("str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3);
return 0;
}

I'm sure it's ok but I'm concerned about the fact that memcpy takes
void* (and const void*) parameters.

Why is it unnecessary to cast str1, str2 and str3 to type char* before
the final printf statement?

For example, I read this on another website: "One of the problems with
using void * is that as soon as you convert something to a void * all
information about what type of thing the pointer points at is lost.
There is absolutely _no_ guarantee that the pointer type the void * is
cast back to matches that from which it was created."

But, to me, the above code seems to assume that a void pointer points
to char.

Thank you,

Paul Epstein
 
M

Marcel Müller

This code comes from cpluplus.com

What a shame. It does not contain even a single character that makes use
of C++. It is C code.
/* memcpy example */
#include <stdio.h>
#include <string.h>

int main ()
{
char str1[]="Sample string";
char str2[40];
char str3[40];
memcpy (str2,str1,strlen(str1)+1);
memcpy (str3,"copy successful",16);
printf ("str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3);
return 0;
}

I'm sure it's ok but I'm concerned about the fact that memcpy takes
void* (and const void*) parameters.

Why is it unnecessary to cast str1, str2 and str3 to type char* before
the final printf statement?

? - they /are/ of type char*, so why you want to cast?

memcpy copies memory. No less, no more. And for PODs this is usually
sufficient.
It does not change the type of variables. How should it, since the type
is compile time information (in C) and memcpy operates at runtime.

For example, I read this on another website: "One of the problems with
using void * is that as soon as you convert something to a void * all
information about what type of thing the pointer points at is lost.
There is absolutely _no_ guarantee that the pointer type the void * is
cast back to matches that from which it was created."

No one casts a void* to something else in your code. Except the internal
cast to char* in the C runtime at memcpy, of course.

But, to me, the above code seems to assume that a void pointer points
to char.

There are no void pointers in the code, except for the ones in the C
runtime.

All assumtions made by the above code are satisfied. And there are many.
E.g. that the arrays str2 and str3 are at least as large as str1.


If you want an advice: throw this code away and forget about it. No C++
application should contain code like that. Dealing with raw memory is
known to be fault-prone, and C++ has dozens of ways to avoid that.
In particular do not use char*. It is likely to become the next
generation virus remote installer. Simply don't do that. Use std::string
instead.
And replace printf either by the streaming operators '<<' (ugly) or use
boost::format (similar to printf, but type safe).


Marcel
 
A

Alf P. Steinbach

* Marcel Müller:
This code comes from cpluplus.com

What a shame. It does not contain even a single character that makes use
of C++. It is C code.
/* memcpy example */
#include <stdio.h>
#include <string.h>

int main ()
{
char str1[]="Sample string";
char str2[40];
char str3[40];
memcpy (str2,str1,strlen(str1)+1);
memcpy (str3,"copy successful",16);
printf ("str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3);
return 0;
}

I'm sure it's ok but I'm concerned about the fact that memcpy takes
void* (and const void*) parameters.

Why is it unnecessary to cast str1, str2 and str3 to type char* before
the final printf statement?

? - they /are/ of type char*, so why you want to cast?

ITYM that the expressions effectively are of type 'char*' in this context.

Since that can be easily misinterpreted: the variables str1, str2 and str3 are
themselves of array type, e.g. str2 and str2 are of type 'char[40]'.

But when an array of T is used where a pointer is expected then there is an
automatic conversion to 'T*', producing a pointer to the first element.

In C that conversion always kicks in for use of an array in a run time
expression, although even in C you can do sizeof(str2) and get 40, not e.g. 4.

In C++ the conversion only kicks in where the expected type is a pointer (that
includes a simple indexing expression, and generally all C expressions that
involve arrays, since the point is to keep C compatbility), e.g. there's /no
conversion/ to 'char*' when passing str2 as argument to

void foo( char (&t)[40 ) {}

which among other things means that if you overload foo with various array sizes
then which one's called (if any) depends on the array.


[snip]
If you want an advice: throw this code away and forget about it. No C++
application should contain code like that. Dealing with raw memory is
known to be fault-prone, and C++ has dozens of ways to avoid that.
In particular do not use char*. It is likely to become the next
generation virus remote installer. Simply don't do that. Use std::string
instead.
And replace printf either by the streaming operators '<<' (ugly) or use
boost::format (similar to printf, but type safe).

Yes. :)


Cheers,

- Alf
 
J

Juha Nieminen

Marcel said:
And replace printf either by the streaming operators '<<' (ugly) or use
boost::format (similar to printf, but type safe).

"Ugly" is in the eye of the beholder. Personally I only find it "ugly"
when you need special formatting parameters, but otherwise it's less
obfuscated than printf format strings. Also, the operator<< is more
versatile. You can do things like:

MyClass anObject;
std::cout << anObject;
// when a proper operator<< has been implemented previously

I must confess I don't know if you can do that with boost::format, as
I haven't studied it too closely. Can you?
 
J

James Kanze

* Marcel Müller:
This code comes from cpluplus.com
What a shame. It does not contain even a single character
that makes use of C++. It is C code.
/* memcpy example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[]="Sample string";
char str2[40];
char str3[40];
memcpy (str2,str1,strlen(str1)+1);
memcpy (str3,"copy successful",16);
printf ("str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3);
return 0;
}
I'm sure it's ok but I'm concerned about the fact that
memcpy takes void* (and const void*) parameters.
Why is it unnecessary to cast str1, str2 and str3 to type
char* before the final printf statement?
? - they /are/ of type char*, so why you want to cast?
ITYM that the expressions effectively are of type 'char*' in
this context.
Since that can be easily misinterpreted: the variables str1,
str2 and str3 are themselves of array type, e.g. str2 and str2
are of type 'char[40]'.
But when an array of T is used where a pointer is expected
then there is an automatic conversion to 'T*', producing a
pointer to the first element.

That's fully correct.
In C that conversion always kicks in for use of an array in a
run time expression, although even in C you can do
sizeof(str2) and get 40, not e.g. 4.

But that isn't. In C, sizeof isn't (always) a compile time
expression. More generally, the correct rule (both in C and in
C++) is the one you stated above: the conversion occurs in
context where a pointer is expected (or perhaps one should say
required). Sizeof doesn't require a pointer; it can be applied
to any type, so the conversion doesn't occur.
In C++ the conversion only kicks in where the expected type is
a pointer (that includes a simple indexing expression, and
generally all C expressions that involve arrays, since the
point is to keep C compatbility), e.g. there's /no conversion/
to 'char*' when passing str2 as argument to
void foo( char (&t)[40 ) {}
which among other things means that if you overload foo with
various array sizes then which one's called (if any) depends
on the array.

I understand that you're trying to express things simply, but
there's such a thing as being too simple. The reason the
conversion doesn't occur when calling the above function is
because the function doesn't expect a pointer. The reason it
occurs when calling

void f( char a[ 40 ] ) ;

is because despite appearances, this is actually:

void f( char* ) ;

.. Which expects a pointer.
 
J

James Kanze

"Ugly" is in the eye of the beholder. Personally I only find
it "ugly" when you need special formatting parameters, but
otherwise it's less obfuscated than printf format strings.

"Ugly" is an esthetic judgement; I don't find the streaming
operators particularly beautiful. On the other hand, as you
say, printf is horribly obfuscated. Especially when you need
special formatting parameters; compare:
std::cout << percent << 100.0 * someDouble ;
to
printf( "%.1f%%", 100.0 * someDouble ) ;
And what happens when you have to change the precision of the
percentages output by your code (in many different locations)?
Also, the operator<< is more versatile. You can do things
like:
MyClass anObject;
std::cout << anObject;
// when a proper operator<< has been implemented previously
I must confess I don't know if you can do that with
boost::format, as I haven't studied it too closely. Can you?

Yes. You can also leave out the format specifiers, and if I
understand it correctly, you can also use custom manipulators,
like percent, with it. Regretfully, it uses the '%' operator,
rather than '<<', which means that expressions using it are
fairly unreadable. But that shouldn't be too hard to fix.
 
A

Alf P. Steinbach

* James Kanze:
* Marcel Müller:
(e-mail address removed) wrote:
This code comes from cpluplus.com
What a shame. It does not contain even a single character
that makes use of C++. It is C code.
/* memcpy example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[]="Sample string";
char str2[40];
char str3[40];
memcpy (str2,str1,strlen(str1)+1);
memcpy (str3,"copy successful",16);
printf ("str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3);
return 0;
}
I'm sure it's ok but I'm concerned about the fact that
memcpy takes void* (and const void*) parameters.
Why is it unnecessary to cast str1, str2 and str3 to type
char* before the final printf statement?
? - they /are/ of type char*, so why you want to cast?
ITYM that the expressions effectively are of type 'char*' in
this context.
Since that can be easily misinterpreted: the variables str1,
str2 and str3 are themselves of array type, e.g. str2 and str2
are of type 'char[40]'.
But when an array of T is used where a pointer is expected
then there is an automatic conversion to 'T*', producing a
pointer to the first element.

That's fully correct.
Right.

In C that conversion always kicks in for use of an array in a
run time expression, although even in C you can do
sizeof(str2) and get 40, not e.g. 4.

But that isn't.

Wrong. Or just plain actively misleading for the case of sizeof a VLA in C99.
C++ does not have VLAs. And C99 is not what's ordinarily meant by "C", at least
in the context of ordinary programming and in the context of C++ (built on C89).

And one reason that you actively don't mention what you're talking about is,
likely, that anyone would then see that it's completely irrelevant: C99 VLAs, huh?

And another reason that you actively don't mention what you're talking about is,
likely, that you're hoping to trick someone who doesn't immediately think of
your irrelevant word-games level detail (by misinterpreting both "C" and
"array"). If so, if you're trying to trick the reader, which I consider very
likely given your other postings, then it's an extremely stupid thing to do when
talking to any regular in this group. It's almost as if you've lost your memory.

In C, sizeof isn't (always) a compile time expression.

Wrong for C (C89), right for the less common C99, but anyway actively misleading
and irrelevant. We're not talking about C99 VLAs. C++ does not have them, and
you know that, and you know or should know that "C" doesn't refer to C99 and
that "array" does not include non-C++ VLAs, in the context above.

More generally, the correct rule (both in C and in
C++) is the one you stated above: the conversion occurs in
context where a pointer is expected (or perhaps one should say
required).
Right.


Sizeof doesn't require a pointer; it can be applied
to any type, so the conversion doesn't occur.

Right but very misleading: it doesn't contradict anything you've quoted, it
reformulates what you've quoted.

I'm sure (and that's not a typo) that you trying to mislead the reader on purpose.

In C++ the conversion only kicks in where the expected type is
a pointer (that includes a simple indexing expression, and
generally all C expressions that involve arrays, since the
point is to keep C compatbility), e.g. there's /no conversion/
to 'char*' when passing str2 as argument to
void foo( char (&t)[40 ) {}
which among other things means that if you overload foo with
various array sizes then which one's called (if any) depends
on the array.

I understand that you're trying to express things simply, but
there's such a thing as being too simple.

Nah, apparently not.

The reason the
conversion doesn't occur when calling the above function is
because the function doesn't expect a pointer.

Yes, you've understood that correctly.

Namely, that "only kicks in where the expected type is a pointer" implies
"doesn't occur when [the context] doesn't expect a pointer".

So, it seems the explanation wasn't too hard or too simple, but spot on for your
current level, yes?

The reason it
occurs when calling

void f( char a[ 40 ] ) ;

is because despite appearances, this is actually:

void f( char* ) ;

Which expects a pointer.

Yes, that's right.


- Alf
 
M

Marcel Müller

Juha said:
"Ugly" is in the eye of the beholder. Personally I only find it "ugly"
when you need special formatting parameters, but otherwise it's less
obfuscated than printf format strings. Also, the operator<< is more
versatile. You can do things like:

MyClass anObject;
std::cout << anObject;
// when a proper operator<< has been implemented previously

If you have more than a few insertions in a text output, the intended
text gets almost unreadable, because is intercepted by more or less
bulky expressions at many locations. And if you need NLS support the
streaming operators are out, because you would have to deal with dozens
of text fragments separately.


Marcel
 
J

James Kanze

If you have more than a few insertions in a text output, the
intended text gets almost unreadable, because is intercepted
by more or less bulky expressions at many locations.

The fact that the text is broken up is a problem, although not
as much of one as you seem to suggest, at least in well written
code. It's more a problem for automatic extraction of the
strings to be translated. But in practice, that doesn't help
much anyway.
And if you need NLS support the streaming operators are out,
because you would have to deal with dozens of text fragments
separately.

Which is a serious problem for the translator, I know. On the
other hand, printf is also out, because the translator will
fatally accidentally modify something in the formatting
specifier---that I know from experience.

In general, there is no really good solution for NLS support.
You need a different plugin for each language, which must be
written more or less depending on the particularities of that
language. Which means that the translation must be done by a
programmer. Rather than a professional translator, which means
in turn that it will not be a very good translation.

This is one case where you probably need some variant of pair
programming. With one of the pair a professional translator,
who may not even know C++, and the other a professional
programmer, fluent in the target language, and with a decent
understanding of the source language. That's an expensive
solution, but I don't know anything else that works. In this
case, the fact that the text isn't in a single block, and can't
be automatically extracted, isn't an issue. And the fact that
the original programmer has specified that the value in question
should be formatted as a percent, and not as a %.2f, is a
definite advantage.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,744
Latest member
CortneyMcK

Latest Threads

Top