Why strcpy takes const char* for src instead of char *? - c

I implemented my own strcpys to find if there is any difference between src as const char* & char *, but don't find any difference between the following 2 & both worked the same.
char * my_strcpy(char*dest, char* src)
{
while ('\0' != *src)
*dest++ = *src++;
*dest++ = '\0';
return dest;
}
char * my_strcpy2(char*dest, const char* src)
{
while ('\0' != *src)
*dest++ = *src++;
*dest++ = '\0';
return dest;
}
Is there any reason that the strcpy takes the source pointer as const char* instead of char*?

Is there any reason that the strcpy takes the source pointer as char* instead of const char*?
The source pointer should be const char *. The reason is common for all functions (not just strcpy) that do not intend to change the source accidentally inside the function.
The practice applies to both library functions like strcpy or your own custom functions. As with library function like strcpy there is no chance that the source is accidentially changed. But for your own (or everyone else) custom function, anything can happen. And if you do modify it accidentially, then you would get a compile error telling you so. And that's when the const is making a difference.

"don't find any difference between the following 2" -- what could the differences be, in your wildest dreams?
The only difference the const declaration has is on the caller side. By the const parameter declaration the function promises not to change the memory accessed through the pointer. It promises only to read through it, which is consistent with the semantics of strcpy(). (Note: Whether the function actually does not write through the pointer is not guaranteed at all. But it promises.)
The caller can therefore call the function with a pointer to constant data and assume that the function will not attempt to write to it.
That is important syntactically, logically and materially:
The language permits the caller to provide a pointer to const data as the argument (it would not allow that for your first non-const version).
The caller can be sure that sensitive data is not altered inside the function as a side effect; imagine a pointer into kernel data structures here. (Counter-example: strtok() writes to the original string, which is therefore not declared const!)
The data may be physically read-only (like, it may be burned into the ROM of a controller), so that an attempt to write to it would cause, well, to quote the excellent Max Barry: a "catastrophic system failure. (...) I'm not saying it's a big deal."

For starters function strcpy returns pointer to the destination string. Your function should also return pointer to the destination string. It is a common convention for functions that process strings in the C Standard.
So the function can look the following way
char * my_strcpy( char *dest, const char *src )
{
char *p = dest;
while ( *p++ = *src++ );
return dest;
}
In this case it can be called for example the following way
const char *hello = "Hello, World!";
char s[14];
puts( s, hello );
Specifying the second parameter of the function as a pointer to a constant character string means 1) that the function guarantees that it will not change the pointed string and 2) allows to pass to the function constant strings.
A pointer to a non-constant object can be implicitly converted to a pointer to constant object. The reverse operation is not allowed without explicit casting.
So if the parameter is declared like pointer to a constant string then you can pass non-constant strings along with constant strings to the same function as arguments. Otherwise the function can not be called with constant strings because the compiler will issue an error saying that it unable to convert an object of type const char * to an object of type char *.

Related

Why in the prototype of strcpy() function one parameter is of constant type? [duplicate]

I implemented my own strcpys to find if there is any difference between src as const char* & char *, but don't find any difference between the following 2 & both worked the same.
char * my_strcpy(char*dest, char* src)
{
while ('\0' != *src)
*dest++ = *src++;
*dest++ = '\0';
return dest;
}
char * my_strcpy2(char*dest, const char* src)
{
while ('\0' != *src)
*dest++ = *src++;
*dest++ = '\0';
return dest;
}
Is there any reason that the strcpy takes the source pointer as const char* instead of char*?
Is there any reason that the strcpy takes the source pointer as char* instead of const char*?
The source pointer should be const char *. The reason is common for all functions (not just strcpy) that do not intend to change the source accidentally inside the function.
The practice applies to both library functions like strcpy or your own custom functions. As with library function like strcpy there is no chance that the source is accidentially changed. But for your own (or everyone else) custom function, anything can happen. And if you do modify it accidentially, then you would get a compile error telling you so. And that's when the const is making a difference.
"don't find any difference between the following 2" -- what could the differences be, in your wildest dreams?
The only difference the const declaration has is on the caller side. By the const parameter declaration the function promises not to change the memory accessed through the pointer. It promises only to read through it, which is consistent with the semantics of strcpy(). (Note: Whether the function actually does not write through the pointer is not guaranteed at all. But it promises.)
The caller can therefore call the function with a pointer to constant data and assume that the function will not attempt to write to it.
That is important syntactically, logically and materially:
The language permits the caller to provide a pointer to const data as the argument (it would not allow that for your first non-const version).
The caller can be sure that sensitive data is not altered inside the function as a side effect; imagine a pointer into kernel data structures here. (Counter-example: strtok() writes to the original string, which is therefore not declared const!)
The data may be physically read-only (like, it may be burned into the ROM of a controller), so that an attempt to write to it would cause, well, to quote the excellent Max Barry: a "catastrophic system failure. (...) I'm not saying it's a big deal."
For starters function strcpy returns pointer to the destination string. Your function should also return pointer to the destination string. It is a common convention for functions that process strings in the C Standard.
So the function can look the following way
char * my_strcpy( char *dest, const char *src )
{
char *p = dest;
while ( *p++ = *src++ );
return dest;
}
In this case it can be called for example the following way
const char *hello = "Hello, World!";
char s[14];
puts( s, hello );
Specifying the second parameter of the function as a pointer to a constant character string means 1) that the function guarantees that it will not change the pointed string and 2) allows to pass to the function constant strings.
A pointer to a non-constant object can be implicitly converted to a pointer to constant object. The reverse operation is not allowed without explicit casting.
So if the parameter is declared like pointer to a constant string then you can pass non-constant strings along with constant strings to the same function as arguments. Otherwise the function can not be called with constant strings because the compiler will issue an error saying that it unable to convert an object of type const char * to an object of type char *.

Is it Undefined Behaviour to cast away the constness of a function parameter?

Imagine I have this C function (and the corresponding prototype in a header file)
void clearstring(const char *data) {
char *dst = (char *)data;
*dst = 0;
}
Is there Undefined Behaviour in the above code, casting the const away, or is it just a terribly bad programming practice?
Suppose there are no const-qualified objects used
char name[] = "pmg";
clearstring(name);
The attempt to write to *dst is UB if the caller passes you a pointer to a const object, or a pointer to a string literal.
But if the caller passes you a pointer to data that in fact is mutable, then behavior is defined. Creating a const char* that points to a modifiable char doesn't make that char immutable.
So:
char c;
clearstring(&c); // OK, sets c to 0
char *p = malloc(100);
if (p) {
clearstring(p); // OK, p now points to an empty string
free(p);
}
const char d = 0;
clearstring(&d); // UB
clearstring("foo"); // UB
That is, your function is extremely ill-advised, because it is so easy for a caller to cause UB. But it is in fact possible to use it with defined behavior.
Consider a function like strstr which, if given a pointer to a part of an object containing a string, with return a pointer to a possibly-different part of the same object. If the method is passed a pointer to a read-only area of memory, it will return a pointer to a read-only area of memory; likewise if it is given a pointer to a writable area, it will return a pointer to a writable area.
There is no way in C to have a function return a const char * when given a const char *, and return an ordinary char * when given an ordinary char *. In order to be compatible with the way strstr worked before the idea of a const char * was added to the language, it has to convert a const-qualified pointer into a non-const-qualified pointer. While it's true that as a library function strstr might be entitled to do such a cast even if user code could not, the same pattern comes up often enough in user code that it would be practical to forbid it.

How to properly initialize a string

How would I go about defining the following string for the following function?
As of now I get the warning:
C4047: '=' : 'const char' differs in levels of indirection from 'char [4]'
and the error:
C2166: l-value specifies const object.
Both in the third line of the code below:
uint8_t *buffer= (uint8_t *) malloc(sizeof(uint32_t));
const char *stringaling= (const char *) malloc(sizeof(uint32_t));
*stringaling = "fun";
newval = protobuf_writeString (buffer, stringaling);
uint32_t protobuf_writeString(uint8_t *out,const char * str)
{
if (str == NULL)
{
out[0] = 0;
return 1;
}
else
{
size_t len = strlen (str);
size_t rv = uint32_pack (len, out);
memcpy (out + rv, str, len);
return rv + len;
}
}
const char *stringaling= (const char *) malloc(sizeof(uint32_t));
*stringaling = "fun";
This is not valid code. You are trying to assign to a const variable, which is illegal. Then you are trying to assign an array of characters to a character. And finally, even if you had a non-const array of characters of the right size, you still can't assign arrays, because they're not first-class values.
Try using
char *stringaling = malloc(sizeof(uint32_t));
strcpy(stringaling, "fun");
...instead, and see if that doesn't work better. Note, however, that it's pretty much accidental that (at least usually) sizeof(uint32_t) happens to be the right size to hold "fun". You normally don't want to do that.
Alternatively, you may want:
char const *stringaling = "fun";
or:
char stringaling[] = "fun";
The assignment you had won't work though -- C has only the very most minimal support for strings built into the language; most operations (including copying a string) are normally done via library functions such as strcpy.
"fun" is a string literal, which is essentially a const char *.
stringaling is also a const char *, so your third line is trying to assign a const char * to a const char, which is not going to fly.
If it's a constant string, you can just do this:
const char *stringaling = "fun";
If your input string is dynamic, you can do this:
char *stringaling= (char *) malloc(strlen(inputString)+1);
strcpy(stringaling, inputString);
Obviously, if you malloc it, you need to free it, or feel the wrath of a memory leak.
If you really want to initialize the char *, you could write this instead:
const char *stringaling = "fun";
And here's some reference.
without all the stuff you can also use:
newval = protobuf_writeString (buffer, "fun" );
First problem:
const char *stringaling= (const char *) malloc(sizeof(uint32_t));
Several problems on this line.
First of all, you don't want to declare stringaling as const char *; you will not be able to modify whatever stringaling points to (IOW, *stringaling will not be writable). This matters since you want to copy the contents of another string to the location pointed to by stringaling. Drop the const keyword.
Secondly, malloc(sizeof(uint32_t)) just happens to allocate enough bytes (4) for this particular string, but it's not clear that you meant to allocate 4 bytes. When allocating memory for an array (and strings are arrays), explicitly indicate the number of elements you intend to allocate.
Finally, casting the result of malloc is considered bad practice in C. The cast will suppress a useful diagnostic message if you forget to include stdlib.h or otherwise don't have a prototype for malloc in scope. As of the 1989 standard, malloc returns void *, which can be assigned to any other object pointer type without needing to cast. This isn't true in C++, so a cast is required there, but if you're writing C++ you should be using new instead of malloc anyway.
So, change that line to read
char *stringaling = malloc(LEN); // or malloc(LEN * sizeof *stringaling), but
// in this case that's redundant since
// sizeof (char) == 1
where LEN is the number of chars you want to allocate.
The general form for a malloc call is
T *p = malloc (N * sizeof *p);
where T is the base type (int, char, float, struct ..., etc.), and N is the number of elements of type T you want to allocate. Since the type of the expression *p is T, sizeof *p == sizeof(T); if you ever change the type of p, you don't have to replicate that change in the malloc call itself.
Second problem:
*stringaling = "fun";
Again, there are several issues at play. First, you cannot assign string values using the = operator. String literals are array expressions, and in most contexts array expressions have their type implicitly converted ("decay") from "N-element array of T" to "pointer to T". Instead of copying the contents of the string literal, you would be simply assigning a pointer to the first character in the string.
Which would "work" (see below), except that you're dereferencing stringaling in the assignment; the type of the expression *stringaling is const char (char after making the change I indicated above), which is not compatible for assignment with type char *. If you drop the dereference operator and write
stringaling = "fun";
you'd fix the compile-time error, but now you have another problem; as mentioned above, you haven't copied the contents of the string literal "fun" to the memory block you allocated with malloc; instead, you've simply copied the address of the string literal to the variable stringaling. By doing so, you lose track of the dynamically-allocated block, causing a memory leak.
In order to copy the string contents from one place to another, you'll have to use a library function like strcpy or strncpy or memcpy, like so:
strcpy(stringaling, "fun");
If stringaling doesn't need to live on the heap (for example, you're only using it within a single function and deallocating it before returning), you could avoid memory management completely by declaring it as a regular array of char and initializing it with "fun":
char stringaling[] = "fun";
This is a special case of initializing an array in a declaration, not an assignment expression, so the = does copy the contents of the string literal to the stringaling array. This only works in an array declaration, however. You can later modify the array with other string values (up to 3 characters plus the 0 terminator), but you'd have to use strcpy again:
strcpy(stringaling, "one");
If you don't need to modify the contents of stringaling, you could just do
const char *stringaling = "fun";
This copies the address of the string literal "fun" to the variable stringaling. And since attempting to modify the contents of a string literal invokes undefined behavior, we do want to declare stringaling as const char * in this case; that will prevent you from accidentally modifying the string literal.

char* vs const char* as a parameter

There are many times that I get compile errors when I use char* instead of const char*. So, I am not sure the actual difference, the syntax and the compile mechanism.
If you're after the difference between the two, just think of them as:
char* is a pointer that points to a location containing a value of type char that can also be changed. The pointer's value can be changed, i.e. the pointer can be modified to point to different locations.
const char* is a pointer, whose value can be also changed, that points to a location containing a value of type char that cannot be changed.
const char * means "pointer to an unmodifiable character." It's usually used for strings of characters that shouldn't be modified.
Say you're writing this function:
int checkForMatch(const char * pstr)
You've promised (through the function signature) that you will not change the thing pointed to by pstr. Now say part of checking for a match would involve ignore the case of letters, and you tried to do it by converting the string to upper case before doing your other checks:
strupr(pstr);
You'll get an error saying you can't do that, because strupr is declared as:
char * strupr(char* str);
...and that means it wants to be able to write to the string. You can't write to the characters in a const char * (that's what the const is for).
In general, you can pass a char * into something that expects a const char * without an explicit cast because that's a safe thing to do (give something modifiable to something that doesn't intend to modify it), but you can't pass a const char * into something expecting a char * (without an explicit cast) because that's not a safe thing to do (passing something that isn't meant to be modified into something that may modify it).
Of course, this is C, and you can do just about anything in C, including explicitly casting a const char * to a char * — but that would be a really, really bad idea because there is (presumably) some reason that the thing being pointed to by the pointer is const.
char *       : non-constant pointer to non-constant character
const char *    : non-constant pointer to constant   character
char *const     : constant   pointer to non-constant character
const char * const : constant   pointer to constant   character
Reference [link]
I always try to define parameters with const char* not char* because converting from std::string to conts char* is easy by .c_str() method. However converting std::string to char* is not that easy.
Probably I'm too picky. In my book, the character(s) pointed to by a const char* can possibly be changed but not via the const char*. A const char * can point to modifiable storage. Example:
char a[] = "abracadabra";
const char * ccp = &a[0]; // ccp points to modifiable storage.
*&a[0] = 'o'; // This writes to a location pointed to by const char* ccp
So, my wording is:
A char * is a pointer that be changed and that also allows writing through it when dereferenced via * or [].
A const char * is a pointer that be changed and that does not allow writing through it when dereferenced via * or [].

Why am I able to change the contents of const char *ptr?

I passed a pointer ptr to a function whose prototype takes it as const.
foo( const char *str );
Which according to my understanding means that it will not be able to change the contents of ptr passed. Like in the case of foo( const int i ). If foo() tries to chnage the value of i, compiler gives error.
But here I see that it can change the contents of ptr easily.
Please have a look at the following code
foo( const char *str )
{
strcpy( str, "ABC" ) ;
printf( "%s(): %s\n" , __func__ , str ) ;
}
main()
{
char ptr[ ] = "Its just to fill the space" ;
printf( "%s(): %s\n" , __func__ , ptr ) ;
foo( const ptr ) ;
printf( "%s(): %s\n" , __func__ , ptr ) ;
return;
}
On compilation, I only get a warning, no error:
warning: passing argument 1 of ‘strcpy’ discards qualifiers from pointer target type
and when I run it, I get an output instead of Segmentation Fault
main(): Its just to fill the space
foo(): ABC
main(): ABC
Now, my questions is
1- What does const char *str in prototype actually means?
Does this mean that function cannot change the contents of str? If that is so then how come the above program changes the value?
2- How can I make sure that the contents of the pointer I have passed will not be changed?
From "contents of the pointer" in the above stated question, I mean "contents of the memory pointed at by the pointer", not "address contained in the pointer".
Edit
Most replies say that this is because of strcpy and C implicit type conversion. But now I tried this
foo( const char *str )
{
str = "Tim" ;
// strcpy( str, "ABC" ) ;
printf( "%s(): %s\n" , __func__ , str ) ;
}
This time the output is, with no warning from compiler
main(): Its just to fill the space
foo(): Tim
main(): Its just to fill the space
So apparently, memory pointed to by str is changed to the memory location containing "Tim" while its in foo(). Although I didn't use strcpy() this time.
Is not const supposed to stop this? or my understanding is wrong?
To me it seems that even with const, I can change the memory reference and the contents of memory reference too. Then what is the use?
Can you give me an example where complier will give me error that I am trying to change a const pointer?
Thanks to all of you for your time and effort.
Your understanding is correct, const char* is a contract that means you can't change memory through this particular pointer.
The problem is that C is very lax with type conversions. strcpy takes a pointer to non-const char, and it is implicitly converted from const char* to char* (as compiler helpfully tells you). You could as easily pass an integer instead of pointer. As a result, your function can't change content pointed by ptr, but strcpy can, because it sees a non-const pointer. You don't get a crash, because in your case, the pointer points to an actual buffer of sufficient size, not a read-only string literal.
To avoid this, look for compiler warnings, or compile, for example, with -Wall -Werror (if you are using gcc).
This behaviour is specific to C. C++, for example, does not allow that, and requires an explicit cast (C-style cast or a const_cast) to strip const qualifier, as you would reasonably expect.
Answer to the extended question
You are assigning a string literal into a non-const char, which, unfortunately, is legal in C and even C++! It is implicitly converted to char*, even though writing through this pointer will now result in undefined behaviour. It is a deprecated feature, and only C++0x so far does not allow this to happen.
With that said, In order to stop changing the pointer itself, you have to declare it as const pointer to char (char *const). Or, if you want to make it that both the contents pointed by it and the pointer itself don't change, use a const pointer to const char (const char * const).
Examples:
void foo (
char *a,
const char *b,
char *const c,
const char *const d)
{
char buf[10];
a = buf; /* OK, changing the pointer */
*a = 'a'; /* OK, changing contents pointed by pointer */
b = buf; /* OK, changing the pointer */
*b = 'b'; /* error, changing contents pointed by pointer */
c = buf; /* error, changing pointer */
*c = 'c'; /* OK, changing contents pointed by pointer */
d = buf; /* error, changing pointer */
*d = 'd'; /* error, changing contents pointed by pointer */
}
For all error lines GCC gives me "error: assignment of read-only location".
"const" is really a compile-time thing, so don't expect a segfault unless the pointer points to some invalid memory. When using const pointers in a way that could potentially alter whatever they point to (in this case passing it to strcpy which accepts non-const), will generate a warning.
1- What does const char *str in prototype actually means?
Does this mean that function cannot change the contents of str?
Yes! This means we cannot change the contents of something(either a char or an array of chars) pointed to by str.
If that is so then how come the above program changes the value?
Thats because strcpy()'s prototype is char * strcpy ( char * destination, const char * source );
In your code there is an implicit conversion from const char* to char* type because strcpy() requires its first argument to be of the type char*.
Technically speaking your code is incorrect rather dangerous. If you try the same code in C++ you'll surely get an error. C++ doesn't allow such implicit conversions but C does.
IIRC the const means that the value of the parameter may not be changed. I your case, the value is the address the pointer points to. strcpy doesn't change the address the pointer points to, but the memory the pointer points to.
Using const here makes sure that the memory reference (address) isn't changed. The memory referenced may be changed, however. This is what's happening here. Assigning a new address to the pointer would result in an error.
SIDE NOTE
I'd consider your foo method insecure. You'd better pass the maximum length of str as well and perform a length check, otherwise you'll be open for buffer overflows.
EDIT
I took the following from this site:
The const char *Str tells the compiler
that the DATA the pointer points too
is const. This means, Str can be
changed within Func, but *Str cannot.
As a copy of the pointer is passed to
Func, any changes made to Str are not
seen by main....
const char* is the same as char const* and not char* const. So this means a pointer to something you can't change.
Elaborating after your edit. The first form inhibits the data to be changed (unless you cast implicitly or explicitly) the second inhibits the pointer itself to be changed.
What you do in your edited version is to change the pointer. This is legal here. If you'd inhibit both you'd have to write char const* const.

Resources