function (const char *) vs. function (char *)

A

andrew.fabbro

In a different newsgroup, I was told that a function I'd written that
looked like this:

void myfunc (char * somestring_ptr)

should instead be

void myfunc (const char * somestring_ptr)

When I asked why, I was told that it facilitated calling it as:

myfunc ("String");

instead of

strcpy (string, "What I want");
myfunc (string);

However, now that I experiment with it, I find that my original
prototype

void myfunc (char * somestring_ptr)

works perfectly well with either invocation.

So I'm curious why "const char *" would be desirable?

The only difference I see it makes is that it forbids modification of
the string within the function...of course, you can't modify the
original copy of the string, since C is call-by-value, but I guess
const char * means you can't modify the passed copy in the function
either...perhaps this is just an additional safeguard?

I'm curious if there is something I'm missing about "const char *" in
function prototypes. K&R doesn't mention anything about this (though
of course some of the library functions do use "const char *").

Thanks,

-AF
 
T

tedu

In a different newsgroup, I was told that a function I'd written that
looked like this:

void myfunc (char * somestring_ptr)

should instead be

void myfunc (const char * somestring_ptr)
....
So I'm curious why "const char *" would be desirable?

So that you don't accidentally modify the value. Also, it allows a
caller to pass a const char * as an argument. Not to mention it
demonstrates to anyone using the function that it's not going to modify
their string if they care about it.
The only difference I see it makes is that it forbids modification of
the string within the function...of course, you can't modify the
original copy of the string, since C is call-by-value, but I guess
const char * means you can't modify the passed copy in the function
either...perhaps this is just an additional safeguard?

The pointer value is passed by value, but the contents of the string,
the characters, are not. The const keyword applies to the chars, not
the pointer.
 
E

Eric Sosman

In a different newsgroup, I was told that a function I'd written that
looked like this:

void myfunc (char * somestring_ptr)

should instead be

void myfunc (const char * somestring_ptr)

When I asked why, I was told that it facilitated calling it as:

myfunc ("String");

instead of

strcpy (string, "What I want");
myfunc (string);

Whoever told you that didn't know C. The type of "String"
is `char*', not `const char*', so you can pass it to myfunc()
(either edition) without any trouble.

(Why isn't "String" a `const char*'? Historical reasons:
pre-Standard C had no `const', so all functions with string
arguments looked like your original no matter whether they
intended to modify the string argument or not. If the Standard
had made character literals `const*', all pre-existing functions
would have suddenly become unable to accept string literals as
arguments. This would have raised a serious barrier to the
adoption of the Standard -- and the writers of the Standard were,
naturally, not interested in raising such barriers.)
However, now that I experiment with it, I find that my original
prototype

void myfunc (char * somestring_ptr)

works perfectly well with either invocation.

As it should. Now that you know that "String" is not
const-qualified, you surely understand why.
So I'm curious why "const char *" would be desirable?

Mostly as documentation: A function that describes its argument
as `const char*' is promising not to modify the pointed-to string,
while a function that takes a plain `char*' either (1) leaves the
question open if it's "old code," or (2) suggests that it actually
may modify the string.

Unfortunately, the "promise" of `const char*' is violable. You
could perfectly well write a function like

void myfunc(const char *unmodifiable) {
char *gotcha = (char*)unmodifiable;
*gotcha = '\0';
}

.... thus in a sense lying to your clients. The language permits
such things -- it pretty much has to, if functions like strchr()
are to be written in C -- but, of course, You would Never do Such
A Thing. I hope. Use `conat char*' (more generally, `const T*')
whenever your function has no reason to want to modify the pointed-
to thing; use plain `char*' (`T*') when it might. The people who
write calls to your functions will thank you for your helpfulness.
The only difference I see it makes is that it forbids modification of
the string within the function...of course, you can't modify the
original copy of the string, since C is call-by-value, but I guess
const char * means you can't modify the passed copy in the function
either...perhaps this is just an additional safeguard?

Some of your language makes me a little queasy, and makes me
wonder if your grasp of C's argument-passing is as firm as it might
be. Remember always: The string itself is not "passed" to the
function, not ever. What the function receives is a pointer to the
first character of the string, while the string itself continues to
exist "outside." Since C uses call-by-value the function is free
to modify the pointer value it receives, and this modification will
not affect the caller. But even without modifying the pointer, the
function can use it to modify the pointed-to string -- except that
if the argument is `const'-qualified you've promised not to do so
(and the compiler will complain if you try, in the absence of the
sort of shenanigans shown above).
I'm curious if there is something I'm missing about "const char *" in
function prototypes. K&R doesn't mention anything about this (though
of course some of the library functions do use "const char *").

If you think `const' is hard to understand, just wait until
you encounter `restrict' ;-)
 
I

Ian

In a different newsgroup, I was told that a function I'd written that
looked like this:

void myfunc (char * somestring_ptr)

should instead be

void myfunc (const char * somestring_ptr)

When I asked why, I was told that it facilitated calling it as:

myfunc ("String");

instead of

strcpy (string, "What I want");
myfunc (string);

However, now that I experiment with it, I find that my original
prototype

void myfunc (char * somestring_ptr)

works perfectly well with either invocation.

So I'm curious why "const char *" would be desirable?

The only difference I see it makes is that it forbids modification of
the string within the function...of course, you can't modify the
original copy of the string, since C is call-by-value, but I guess
const char * means you can't modify the passed copy in the function
either...perhaps this is just an additional safeguard?
In these cases, you are passing a pointer by value, not what it points to!

So if you function takes a char*, it can and by implication, will modify
the data pointed to. This can give all sorts of undefined results if a
string literal is passed.

Ian
 
P

Peter Nilsson

In a different newsgroup, I was told that a function I'd written
that looked like this:

void myfunc (char * somestring_ptr)

should instead be

void myfunc (const char * somestring_ptr)

When I asked why, I was told that it facilitated calling it as:

myfunc ("String");

instead of

strcpy (string, "What I want");
myfunc (string);

However, now that I experiment with it, I find that my original
prototype

void myfunc (char * somestring_ptr)

works perfectly well with either invocation.

The cited reason is a poor one since (for historical reasons) the
type of string literals is char[], not const char[]. Modifying the
contents of a string literal invokes undefined behaviour, but
the sample will not illustrate this properly on some platforms.

A better example would be...

const char fixed[] = "This string won't or shouldn't change.";
myfunc(fixed);

If myfunc doesn't attempt to modify the character array, then
making the parameter point to a const qualified character will
make your code arguably more robust. Why? Because if you do
accidentally(?!) modify the array using your parameter pointer,
then the compiler is required to issue a diagnostic (warning/
error.) So the const potentially guards against some logic
errors.

That said, there are some arguments against using const at
all in C. [Google for 'const poisoning', and check out the
reasons why functions like strpbrk (et all) take a const
qualified pointer, but return a non-const pointer.]
 
K

kar1107

In a different newsgroup, I was told that a function I'd written that
looked like this:

void myfunc (char * somestring_ptr)

should instead be

void myfunc (const char * somestring_ptr)

When I asked why, I was told that it facilitated calling it as:

myfunc ("String");

instead of

strcpy (string, "What I want");
myfunc (string);

However, now that I experiment with it, I find that my original
prototype

void myfunc (char * somestring_ptr)

works perfectly well with either invocation.

So I'm curious why "const char *" would be desirable?

Like others pointed out there is really nothing wrong in not
using const. But gcc (assuming you are using it) will emit
warnings if you turn on -Wwrite-strings.
Google for "initialization discards qualifiers" and you can find more
info

Karthik
 
L

Lawrence Kirby

Whoever told you that didn't know C. The type of "String"
is `char*', not `const char*', so you can pass it to myfunc()
(either edition) without any trouble.

The type of "string" is char [7], it is an array of char not a pointer. Of
course like other arrays it is converted to a pointer to its first element
in most contexts including when used as a function argument.



(Why isn't "String" a `const char*'? Historical reasons:
pre-Standard C had no `const', so all functions with string
arguments looked like your original no matter whether they
intended to modify the string argument or not. If the Standard
had made character literals `const*', all pre-existing functions
would have suddenly become unable to accept string literals as
arguments. This would have raised a serious barrier to the
adoption of the Standard -- and the writers of the Standard were,
naturally, not interested in raising such barriers.)


As it should. Now that you know that "String" is not
const-qualified, you surely understand why.


Mostly as documentation: A function that describes its argument
as `const char*' is promising not to modify the pointed-to string,
while a function that takes a plain `char*' either (1) leaves the
question open if it's "old code," or (2) suggests that it actually
may modify the string.

But it is "documentation" that the compiler can check to some degree. If
you have a pointer variable in your function of type const char * you cn
pass it directly to a function that takes a const char * argument but not
to one that takes a char * argument. Similarly for a const array of char
although that isn't as common.
Unfortunately, the "promise" of `const char*' is violable. You
could perfectly well write a function like

void myfunc(const char *unmodifiable) {
char *gotcha = (char*)unmodifiable;
*gotcha = '\0';
}

... thus in a sense lying to your clients. The language permits
such things -- it pretty much has to, if functions like strchr()
are to be written in C -- but, of course, You would Never do Such
A Thing. I hope. Use `conat char*' (more generally, `const T*')
whenever your function has no reason to want to modify the pointed-
to thing; use plain `char*' (`T*') when it might. The people who
write calls to your functions will thank you for your helpfulness.

Right, it is a matter of the interface specification. It is a normal
convention that when a function takes a pointer to const argument it won't
attempt to modify what the pointer points at. If this is the convention
used and the called function breaks it then the called function is broken.
As you say const here is documentation, but it provides some enforcable
checks, although not complete guarantees. The called function typically
has to use a nasty/suspicious construct like a pointer cast to get around
it.

Because string literals are not modifiable passing one to a function
taking a pointer to non-const char should be considered suspicious but
this is something you have to check yourself because it is valid as far as
C is concerned, or you find a compiler or lint tol that check for it as
something extra.

Lawrence
 
T

Tim Rentsch

In a different newsgroup, I was told that a function I'd written that
looked like this:

void myfunc (char * somestring_ptr)

should instead be

void myfunc (const char * somestring_ptr)

When I asked why, I was told that it facilitated calling it as:

myfunc ("String");

instead of

strcpy (string, "What I want");
myfunc (string);

However, now that I experiment with it, I find that my original
prototype

void myfunc (char * somestring_ptr)

works perfectly well with either invocation.

So I'm curious why "const char *" would be desirable?

In cases where a parameter can be made 'const char *' rather
than just 'char *', and that doesn't affect the return type
of the function (or other similar kinds of ripple effects),
I almost always prefer to make the parameter the more
restrictive (that is, const) type.

The reason is, besides giving more guarantees to clients
about what will be done with what their pointer points to,
it makes it easier to crank up the compiler warning levels
so string literals may effectively be marked 'const' without
producing any diagnostics.

When there are ripple effects with function return type or
sometimes some other function return type, the decision
needs to be made on a case-by-case basis. Usually though
the cost of this sort of refactoring isn't too high, and it
results in finding defects reasonably often, so mostly using
'const' for pointer parameters seems like it's a win.
Naturally there are the occasional exceptions.
 
B

Bryan Donlan

In a different newsgroup, I was told that a function I'd written that
looked like this:

void myfunc (char * somestring_ptr)

should instead be

void myfunc (const char * somestring_ptr)

When I asked why, I was told that it facilitated calling it as:

myfunc ("String");

instead of

strcpy (string, "What I want");
myfunc (string);

However, now that I experiment with it, I find that my original
prototype

void myfunc (char * somestring_ptr)

works perfectly well with either invocation.

So I'm curious why "const char *" would be desirable?

The only difference I see it makes is that it forbids modification of
the string within the function...of course, you can't modify the
original copy of the string, since C is call-by-value, but I guess
const char * means you can't modify the passed copy in the function
either...perhaps this is just an additional safeguard?

C is pass-by-value, yes, but the value passed is a pointer to the string
data. So, for example if you did:

void myfunc(char *ptr) {
ptr[0] = 'b';
}

/* ... */
char buf[4];
strcpy(buf, "foo");
myfunc(buf);
puts(buf); // prints boo
myfunc("foo"); // Undefined behavior; maybe crash, maybe worse

With const char *, changing ptr[0] would be forbidden.
 

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,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top