问题
I was studying "C complete reference" by Herbert Schildt and got stuck on the "const" explanation due by the pointer * he used at the same time with the const explanation. here is the code he used:
#include <stdio.h>
void dash(const char *str);
int main()
{
dash("this is a test");
return 0;
}
void dash(const char *str)
{
while (*str)
{
if (*str == ' ')
{
printf("%c", '-');
}
else
{
printf("%c", *str);
}
str++;
}
}
I've tried to search about the pointer * and got some answers about adresses but why did he use it in this example? His book didn't explain this and i haven't found other examples with this kinda use of pointer *. Other question is, why is the loop "while (*str)" correct if it has no condition?
回答1:
const char *str
in a parameter declaration indicates that the function will not try to modify the values that the str
pointer points to. This means that you can call the function with a constant string. If you don't have const
in the declaration, it means that the function might modify the string, so you can only call it with writable strings.
As an example, a function like strcpy()
declares has const
on the second parameter (the source string), but not on the first parameter (the destination). It can (and usually does) modify the destination, but not the source.
回答2:
Many people are confused when start learning C
const char *ptr
It is a pointer which is referencing the const char. The pointer can be modified. But is you try to write to the referenced object the compiler will complain: https://godbolt.org/z/d9znF-
Example:
const char c;
const char *ptr = &c;
*ptr = 'p'; // -- illegal - the compiler will complain
ptr++; // -- legal
to declare the constant pointer to the not constant object:
char * const ptr;
now ptr
cannot be changed but the referenced object can: https://godbolt.org/z/h7WWex
char c;
char * const ptr = &c;
*ptr = 'p'; // -- legal
ptr++; // -- illegal - the compiler will complain
to declare const pointer to const object
const char * const ptr;
now the pointer and the referenced object cannot be modified: https://godbolt.org/z/x2xBcZ
const char c;
const char * const ptr = &c;
*ptr = 'p'; // -- illegal - the compiler will complain
ptr++; // -- illegal - the compiler will complain
回答3:
It's a way of promising that the content the pointer is pointing at will not be altered. It's also a way of suppressing warnings without explicit casts.
Consider this:
void dash(char *str) // Removed const
{
// Code
}
int main() {
const char p[] = "this is a test";
dash(p);
}
Now the compiler will emit this:
k.c: In function ‘main’:
k.c:23:10: warning: passing argument 1 of ‘dash’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
23 | dash(p);
| ^
k.c:4:17: note: expected ‘char *’ but argument is of type ‘const char *’
4 | void dash(char *str)
| ~~~~~~^~~
Since you're not writing to it, this warning is nothing to worry about. But it's good practice to avoid warnings. In this case, we have two alternatives. Either the function may modify the string or it may not. If there's no way it will modify it, then there's no reason to explain to the compiler and the reader that this indeed is the case.
Sidenote. String literals, like
"this is a test"
has undefined behavior if you modify them, so the program might crash (or not). However, their type is is of type(char*)
with no const. The reason is backwards compability. In C++, their type isconst char*
Note that the const
is a promise by convention, not by the compiler. This code will modify the original string and also compile without warnings:
#include <stdio.h>
void foo(const char *str)
{
// Casting comes with great responsibility
// You're just saying to the compiler
// "Trust me and shut up"
char *ptr = (char*) str;
ptr[2]='A';
ptr[3]='T';
}
int main()
{
const char p[] = "this is a test";
foo(p);
puts(p);
}
output:
$ ./a.out
thAT is a test
As I said, the above will compile without warning. If you remove the cast, you'll get this:
k.c:5:17: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
5 | char *ptr = str;
| ^~~
Do note that since p
is declared as const
this is undefined behavior. However, you instead write main
like this:
int main()
{
char p[] = "this is a test";
foo(p);
puts(p);
}
then, the program is completely valid. And even though you pass a writable string to the function foo
, you'd expect it to not change, since foo
takes a constant pointer as argument. But as you can see, such things can be bypassed.
Moral lesson: Casting is NOT the goto solution for warnings. Instead, you should REALLY carefully consider if your cast match your intentions. If you're intentions here is to just get rid of the warning, the right solution is to remove the const
for the parameter. If you're intentions with adding the cast is "I know that this function promises to not modify the argument, but I have good reasons for both promising that and then instantly break that promise" then a cast is correct.
回答4:
In c we can manipulate an array like a pointer with the right pointer arithmatic like he used and we can manipulate it like an array!
const char *str
is a pointer to const char OR an array of const char data types!
In a function, all parameters are passed by value (arrays are no exception). When you pass an array in a function it "decays into a pointer". And when you compare an array to something else, again it "decays into a pointer"
so we can write the while loop again in different way:
void dash(const char *str)
{
int i = 0;
while (str[i])
{
if (str[i] == ' ')
{
printf("%c", '-');
}
else
{
printf("%c", str[i]);
}
++i;
}
}
Now, the first syntax (with the pointer deref operator *
is more effecient than array syntax).
in general array name or the address of the first array element (of any type), can decays to a pointer of the same data type!
In his implementation, he behaves the str
as a const char pointer
, in the while loop he is derefrence the pointer (like str[i]
, with the brackets) and in the last line (str++
) he is moving the pointer to points to the next char element (which usualy knwon as pointer arithmetics
).
回答5:
In this case, read the definition from right to left:
const char *str // str is a pointer to a const char
The address of str
can change while the char
it points to cannot.
To answer you other question, while (*str)
will continue to interate until *str == '\0'
. '\0'
is used to mark the end of a string in C.
What the program does, if you're unsure, is print it, replacing ' '
with '-'
. In your example, "this-is-a-test"
would be printed. Note: the string "this is a test"
is not modified.
回答6:
The *
is related to pointers but it has two uses.
In the declaration, *
is used to declare the pointer type, as in:
const char *str;
Where str
is a pointer to a const char
(or multiple const char
stored in sequence, C doesn't care about the difference).
In an expression, *
is used to dereference a pointer, get the value it points to. As in:
printf("%c", *str);
Where *str
is that const char
itself that the pointer str
is pointing to.
Related to pointers, there's also &
that does the other way around. It gets the pointer of any value you have stored in memory.
The importance of const
here is not related to pointers, it's related to the fact you're passing a string literal to dash()
. Unlike strings that are stored in the heap or the stack, string literals cannot be modified and should be treated as const
for their immutability.
来源:https://stackoverflow.com/questions/62562804/how-is-this-const-being-used