C: const vs no const ..how come this compiles? - c

I have a simple C function which I declare as:
int strLen(const char* str_)
{
str_ = (char*) 0;
return 0;
}
I was very surprised that that compiles! Why is that?
Whereas this onedoesn't compile (which makes sense):
int str(const int i_)
{
i_ = 0;
return 0;
}

Because const char* str_ says that str_ is a pointer to const characters.
You can change str_ itself, but not what it points to.
int strLen(const char* str_)
{
str_ = (char*) 0; /* allowed */
str_[0] = '\0'; /* not allowed */
return 0;
}

In the first one, you are changing what the pointer str_ points to; the pointer str_ is not const; it just points to const data. You can make the pointer const as follows:
int strLen(const char* const str_)

This follows the conventions of C declarations,
char *str;
means that *str will give you a char (or in other words, str is a pointer-to-a-char). Similarly,
const char *str;
means that *str will give you a const char, or in other words, str is a pointer-to-a-constant-char. So, you are not allowed to change *str, but you may change str.
If you want to make str itself const, use the following syntax:
char *const str;

Related

Why am I able to modify char * in this example?

I have problems understanding how char* works.
In the example below, the struses is called by main().
I created a buf to store the const variable because I want to make a modifiable copy of s1, then I just call the sortString().
This version makes sense to me as I understand that char[] can be modified:
#include "common.h"
#include <stdbool.h>
void sortString(char string[50]);
bool struses(const char *s1, const char *s2)
{
char buf[50];
strcpy(buf, s1); // <===== input = "perpetuity";
sortString(buf);
printf("%s\n", buf); // prints "eeipprttuy"
return true;
}
void sortString(char string[50])
{
char temp;
int n = strlen(string);
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
if (string[i] > string[j])
{
temp = string[i];
string[i] = string[j];
string[j] = temp;
}
}
}
}
However, in this version I deliberately changed the type to char* which is supposed to be read-only. Why do I still get the same result?
#include "common.h"
#include <stdbool.h>
void sortString(char *string);
bool struses(const char *s1, const char *s2)
{
char buf[50];
strcpy(buf, s1);
sortString(buf);
printf("%s\n", buf);
return true;
}
void sortString(char *string) // <==== changed the type
{
char temp;
int n = strlen(string);
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
if (string[i] > string[j])
{
temp = string[i];
string[i] = string[j];
string[j] = temp;
}
}
}
}
This is why I think char * is read only. I get a bus error after trying to to modify read[0]:
char * read = "Hello";
read[0]='B';// <=== Bus error
printf("%s\n", read);
The compiler adjusts the type of the parameter having an array type of this function declaration
void sortString(char string[50]);
to pointer to the element type
void sortString(char *string);
So for example these function declarations are equivalent and declare the same one function
void sortString(char string[100]);
void sortString(char string[50]);
void sortString(char string[]);
void sortString(char *string);
Within this function
void sortString(char *string)
there is used the character array buf that stores the copy of the passed array (or of the passed string literal through a pointer to it)
char buf[50];
strcpy(buf, s1);
sortString(buf);
So there is no problem. s1 can be a pointer to a string literal. But the content of the string literal is copied in the character array buf that is being changed
As for this code snippet
char * read = "Hello";
read[0]='B';
printf("%s\n", read); <=== still prints "Hello"
then it has undefined behavior because you may not change a string literal.
From the C Standard (6.4.5 String literals)
7 It is unspecified whether these arrays are distinct provided their
elements have the appropriate values. If the program attempts to
modify such an array, the behavior is undefined.
Pay attention to that in C++ opposite to C string literals have types of constant character arrays. It is advisable also in C to declare pointers to string literals with the qualifier const to avoid undefined behavior as for example
const char * read = "Hello";
By the way the function sortString has redundant swappings of elements in the passed string. It is better to declare and define it the following way
// Selection sort
char * sortString( char *s )
{
for ( size_t i = 0, n = strlen( s ); i != n; i++ )
{
size_t min_i = i;
for ( size_t j = i + 1; j != n; j++ )
{
if ( s[j] < s[min_i] )
{
min_i = j;
}
}
if ( i != min_i )
{
char c = s[i];
s[i] = s[min_i];
s[min_i] = c;
}
}
return s;
}
char * does not mean read-only. char * simply means pointer to char.
You have likely been taught that string literals, such as "Hello", may not be modified. That is not quite true; a correct statement is that the C standard does not define what happens when you attempt to modify a string literal, and C implementations commonly place string literals in read-only memory.
We can define objects with the const qualifier to say we intend not to modify them and to allow the compiler to place them in read-only memory (although it is not obligated to). If we were defining the C language from scratch, we would specify that string literals are const-qualified, the pointers that come from string literals would be const char *.
However, when C was first developed, there was no const, and string literals produced pointers that were just char *. The const qualifier came later, and it is too late the change string literals to be const-qualified because of all the old code using char *.
Because of this, it is possible that a char * points to characters in a string literal that should not be modified (because the behavior is not defined). But char * in general does not mean read-only.
Your premise that the area pointed by a char* isn't modifiable is false. This is perfectly line:
char s[] = "abc"; // Same as: char s[4] = { 'a', 'b', 'c', 0 };
char *p = s; // Same as: char *p = &(s[0]);
*p = 'A';
printf("%s\n", p); // Abc
Demo
The reason you had a fault is because you tried to modify the string created by a string literal. This is undefined behaviour:
char *p = "abc";
*p = 'A'; // Undefined behaviour
printf("%s\n", p);
One would normally use a const char * for such strings.
const char *p = "abc";
*p = 'A'; // Compilation error.
printf("%s\n", p);
Demo
Regarding
char * read = "Hello";
read[0]='B';
printf("%s\n", read); // still prints "Hello"
you have tripped over a backward compatibility wart in the C specification.
String constants are read-only. char *, however, is a pointer to modifiable data.
The type of a string constant ought to be const char [N] where N is the number of chars given by the contents of the constant, plus one. However, const did not exist in the original C language (prior to C89). So there was, in 1989, a whole lot of code that used char * to point to string constants. So the C committee made the type of string constants be char [N], even though they are read-only, to keep that code working.
Writing through a char * that points to a string constant triggers undefined behavior; anything can happen. I would have expected a crash, but the write getting discarded is not terribly surprising either.
In C++ the type of string constants is in fact const char [N] and the above fragment would have failed to compile. Some C compilers have an optional mode you can turn on that changes the type of string constants to const char [N]; for instance, GCC and clang have the -Wwrite-strings command line option. Using this mode for new programs is a good idea.
Yout long examples can be reduced to your last question.
This is why I think char * is read only, get bus error after attempt
to modify read[0]
char * read = "Hello";
read[0]='B';
printf("%s\n", read); <=== Bus error
"Hello" is a string literal . Attempt to modify the string literal manifested itself by the Bus Error.
Your pointer is referencing the memory which should not be modified.
How to sort it out? You need to define pointer referencing the modifiable object
char * read = (char []){"Hello"};
read[0]='B';
printf("%s\n", read);
So as you see declaring it as modifiable is not making it modifiable.

Passing a C string and using it with a local pointer

Looking at this code from The Evils of Arduino Strings
void PrintString(const char *str) {
const char *p;
p = str;
while (*p) {
Serial.print(*p);
p++;
}
}
I was looking at reducing/compacting it. Firstly, this seems to be equivalent:
void PrintString(const char *str) {
const char *p;
p = str;
while (*p)
Serial.print(*p++);
}
Now, looking at the first two lines, how can I combine the following two lines, is it possible?
const char *p;
p = str;
Can I do this?
const char *p = str;
This would seem likely but looks unbalanced, in that there is a lack of an asterisk on the right hand side. I tried it and it seems to work but I was wondering whether it was correct, and worried that I would end up with some hard-to-track-down run-time error later on, further down the line.
However, this line below is clearly wrong (as it would change the pointer p to point to a location given by the value of the first character of the C string str):
const char *p = *str;
This is obfuscation. The original code is perfectly clear, there is no need to change anything. Your changes make it less readable.
What you can do to increase readability slightly, and this is a cosmetic change, is to use a for loop instead:
void PrintString (const char *str)
{
for(const char* p=str; *p != '\0'; p++)
{
Serial.print(*p);
}
}
Alternatively:
void PrintString (const char *str)
{
for(size_t i=0; str[i] != '\0'; i++)
{
Serial.print(str[i]);
}
}
const char *p = str;
This would seem likely but looks unbalanced, in that there is a lack of an asterisk on the right hand side.
If you separated the elements of the above snippet correctly it would not look "unbalanced" at all:
const char* is the type (a pointer to const characters)
p and str are the variables, both pointer (to const char)
You could write the above using some unconventional layout like this:
const char * /* Note the lack of a semicolon */
p = str;
The above code ends up with p being defined as a pointer to char and carrying the value of str.
You get same "result" for the below code
const char *p;
p = str;
Note that the former is called "initialisation", whereas the latter is called "assignment".
You can omit the use of the p pointer, use directly the str pointer argument, since it is a copy of the pointer to the constant content.
The changes made on str pointer don't affect the pointer used when calling the function, since the pointer is passed by value.
void PrintString(const char *str) {
while (*str) Serial.print(*str++);
}
How about replacing the while with for. Here's my take:
void PrintString(const char *str)
{
for(const char* p = str; *p; p++)
Serial.print(*p);
}
I think this could work, too:
void PrintString(const char *str) {
for(const char* p = str; *p; Serial.print(*(++p)))
;
}
As others have pointed out, we can replace p with str, since str is a local copy, just like any other variable passed as function argument.
void PrintString(const char* str) {
for( ; *str; Serial.print(*(++str)))
;
}

How do I pass an anonymous string literal to a function?

Since a string literal can be initialized like
char myString [] = "somestring";
it seems logical to be able to use a function like
int stringLength ( char * str )
{
char * c2 = str;
while (*c2++);
return (c2-str);
}
and call it like
printf("%d",stringLength("somestring"));
However, I get an error when I do that.
Is there some way I can "cast" "something" into something proper to input into the function, or do I always have use an extra line like
char str [] = "somestring";
printf("%d",stringLength(str));
or is there something else I should be doing altogether?
char myString [] = "somestring";
creates an array of characters that are modifiable.
When you use
printf("%d",stringLength("somestring"));
it is analogous to:
char const* temp = "somestring";
printf("%d",stringLength(temp));
That's why the compiler does not like it.
You can change the function to use a char const* as argument to be able to use that.
int stringLength ( char const* str )
{
char const* c2 = str;
while (*c2++);
return (c2-str);
}
You know that your function "stringLength" will never mutate/change the contents of the string right so make that function so
int somefunc(const char* ptr)
{
const char* base=ptr;
while(*(ptr++));
return (ptr-base);
}
int main()
{
printf("%d",somefunc("HELLO"));
return 0;
}

strcpy using pointers

I'm trying to write strcpy on my own using pointers and I get an error during runtime.
void str_cpy(char **destination, const char *source) {
// char *s1 = *destination;
while (*source != '\0') {
**destination++ = *source++; //Get an error here
}
**destination = '\0';
}
I call the function as follows:
char *str = NULL;
str_cpy(&str, "String");
Is it not OK?
Thanks!
No, it's not okay. Why? Because str is a NULL pointer. It's pointing to nothing. When you try to write values into it, where will they go? It's not pointing to any allocated memory!
You first have to allocate memory for str. You can do:
char *str = malloc(strlen("String") + 1); // + 1 for the '\0' character at the end of C-style strings
Or you can do:
char str[256]; // make str large enough to hold 256 chars. Note that this is not as safe as the above version!
Also, destination should be a single pointer, not a double pointer. Well, it's not technically wrong to use a double pointer, it's just unnecessary.
Optionally, you can allocate the memory in the str_cpy function, like so:
void str_cpy(char **destination, const char *source) {
*destination = malloc(strlen(source) + 1);
// ... continue as normal
For simplicity's sake, this can be done in one line in a function.
void mystrcpy(char *dest, const char *src) {
while (*dest++ = *src++);
}
This being said, you do need to allocate memory for dest beforehand using malloc or just simply by having a character array like char dest[256].
I don't see any need to pass a pointer-to-pointer:
void str_cpy(char *dst, const char *src) {
while (*src != '\0') {
*dst++ = *src++;
}
*dst = '\0';
}
And you need to allocate memory for dst before passing:
const char *src = "String";
char *str = malloc(strlen(src)+1); //plus one for null byte
str_cpy(dst, src);
You should likely allocate some memory for that pointer before passing it off to a function that fills what it points to (which in this case, is NULL).
Example:
char *str = malloc(128);
if (str)
{
str_cpy(&str, "String");
free(str);
str = NULL;
}
I advise not doing this without also providing target-buffer size information (i.e. if you're writing your own, then boundary-check the target buffer, otherwise your version has the same security flaws as strcpy() which are bad enough as it is).
Note: Unless you're planning on changing the address held by the pointer passed as the target, you need not use a double pointer either. The double pointer usage you have prevents the traditional strcpy() usage pattern of:
char str[128];
str_cpy(&str, "Hello"); // error.
An array address cannot be passed as a pointer-to-pointer, so your code cannot fill a static array without an intermediate pointer:
char str[128];
char *p = str;
str_cpy(&p, "Hello"); //ok. passing address of pointer.
If this is not intentional (and I don't see why it could be unless you have ideas of internally emulating strdup() on a NULL pointer passage) You should address this.
Here is a complete implementation.
Good article from here. Describes timing and performance. I did not measure myself though.
http://www.howstuffworks.com/c35.htm
char* mystrcpy(char *dst, const char *src) {
char *ptr = dst;
while ((*dst++ = *src++) ) ;
return ptr;
}
int main(int argc, char *argv[]) {
const char *src = "This is C.\0";
char *dst = malloc(sizeof(char)*(strlen(src)+1)); //+1 for the null character
dst = mystrcpy(dst, src);
printf("%s",dst);
return 1;
}
Recently I faced same problem of above one using double pointer strcpy implementation
It might helpful to others below code
void strcpy_i( char **dst, const char *src )
{
*dst=(char *)malloc((strlen(src)+1)*sizeof(char));
char *tmp=*dst;
if(tmp == NULL || src == NULL)
return ;
while((*tmp++=*src++)!='\0');
}
int main()
{
char v[]="Vinay Hunachyal";
char *d=NULL;
strcpy_i(&d,v);
printf("%s",d);
return 0;
}
#include<stdio.h>
void main()
{
void mystrcpy(char *,char *);
char s1[100],s2[100];
char *p1;
char *p2;
p1=s1;
p2=s2;
printf("Enter the string to copy to s2...?\n");
scanf("%s",p1);
mystrcpy(p2,p1);
printf("S2 after copying = %s",p2);
}
void mystrcpy(char *p2,char *p1)
{
while(*p1!='\0')
{
*p2=*p1;
p2++;
p1++;
}
*p2='\0';
}
Its my solution..Simple to understand..

How to put values to const char **?

If I declare a variable const char ** stringTable, how should I be able to put values to it if it is a const? (It has to be const because a function I am supposed to use takes const char ** as a parameter.)
Edit:
No you cannot convert from char ** to const char ** implicitly. Compiler complains:
cannot convert parameter 3 from 'char **' to 'const char **'
Wow, I'm surprised nobody got this! Maybe I can get a necromancer badge. Implicit const casting only scans one level deep. So a char* can become a const char* but it won't dig deep enough inside the a char** type to find what needs to be changed to make it a const char**.
#include <iostream>
using namespace std;
void print3( const char **three ) {
for ( int x = 0; x < 3; ++ x ) {
cerr << three[x];
}
}
int main() {
// "three" holds pointers to chars that can't be changed
const char **three = (const char**) malloc( sizeof( char** ) * 3 );
char a[5], b[5], c[5]; // strings on the stack can be changed
strcpy( a, "abc" ); // copy const string into non-const string
strcpy( b, "def" );
strcpy( c, "efg" );
three[0] = a; // ok: we won't change a through three
three[1] = b; // and the (char*) to (const char*) conversion
three[2] = c; // is just one level deep
print3( three ); // print3 gets the type it wants
cerr << endl;
return 0;
}
Apart from other mentions that you can pass char** into function that takes const char **,
const char** is a non-const pointer to const char*, you can declare it and freely put values of type const char* in it.
On the other hand, you would not be able to do it, if you declared it as const char * const * or const char * const * const.
yourfunc(const char **p);
...
const char *array_str[10];
array_str[0] = "foo"; /* OK, literal is a const char[] */
yourfunc(array_str);
Here is what cdecl says:
cdecl> explain const char **table
declare table as pointer to pointer to const char
cdecl> explain const char * const *table
declare table as pointer to const pointer to const char
cdecl> explain const char * const * const table
declare table as const pointer to const pointer to const char
That const declaration is a quarantee of the function, you dont have to fullfill it. That means the function will keep your array untouched (it will just read). So you can pass a nonconst variable to a function expecting const.
You can pass a char ** to a function declared as taking a const char ** -- Might be worth taking a look at the documentation for const on MSDN
char ** can be converted to const char **, so if you want to call a function which takes a const char ** as a parameter, just supply your char ** and it'll be implicitly converted.
If you want to write a function which takes a const char ** as parameter and then modifies the char data it references, you're breaking the contract with your compiler, even if you might get it to work via casts!
With my compiler (gcc version 3.4.4 in cygwin), I found that I could pass char * to const char *, but not char ** to const char **, unlike what most of the answers are saying.
Here is one way you can build something up that works; maybe it will help you.
void printstring( const char **s ) {
printf( "%s\n", *s );
}
int main( int argc, char** argv ) {
char *x = "foo"; // here you have a regular mutable string
const char *x2 = x; // you can convert that to a constant string
const char **y = &x2; // you can assign the address of the const char *
printstring(y);
}
you can un-const char* by using a cast operator: (char*)
void do_something(const char* s)
{
char* p=(char*)s;
p[0]='A';
}
use the same idea with the arrays char**
const char ** indicates that the underlying character is constant. So, while you can't do something like this:
const char **foo = ...;
**foo = 'a'; // not legal
but there is nothing preventing you from manipulating the pointer itself:
// all the following is legal
const char **foo = 0;
foo = (const char **)calloc(10, sizeof(const char *));
foo[0] = strdup("foo");
foo[1] = strdup("baz");
That said, if you did want to modify the actual character data, you could use a non-const pointer and cast it:
char **foo = ...;
func((const char **)foo);

Resources