How is this const being used?

六月ゝ 毕业季﹏ 提交于 2020-08-19 10:20:32

问题


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 is const 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!