简体   繁体   中英

Modify const char * in C

I am practicing for interviews. The problem Im currently stuck on is reversing a constant string in C. I know that since str2 is const, I can modify the location str2 points to, but not its value. I have a function below called reverse_const. It will reverse the const char *str_const in place and print it. However, when I try to print st2 after reversal from the main method, the string isn't reversed anymore. Its like reverse_const() is temporarily changing the memory location of str2. What am I doing wrong here?

#include <stdio.h>
#include <string.h>

void reverse(char *str){
    int c_size = strlen(str);
    char *c_begin = str, *c_end = str + (c_size - 1);

    int i;
    for(i = 0; i < c_size / 2; i++){
        *c_begin ^= *c_end;
        *c_end ^= *c_begin;
        *c_begin ^= *c_end;

        c_begin++;
        c_end--;
    }
}

void reverse_const(const char *str_const){
    int c_size = strlen(str_const);
    char str[c_size];
    strcpy(str, str_const);
    char *c_begin = str, *c_end = str + (c_size - 1);

    int i;
    for(i = 0; i < c_size / 2; i++){
        *c_begin ^= *c_end;
        *c_end ^= *c_begin;
        *c_begin ^= *c_end;

        c_begin++;
        c_end--;
    }

    str_const = str;
    printf("%s\n", str_const);
}

int main(){
    char str1[] = "Indiana";
    char *str2 = "Kentucky";
    printf("TESTS:\nString 1 pre-reversal: %s\n", str1);
    reverse(str1);
    printf("String 1 post-reversal: %s\n", str1);
    printf("Constant string 2 pre-reversal: %s\n", str2);
    reverse_const(str2);
    printf("Constant string 2 post-reversal: %s\n", str2);
}

When you pass by arguments, the function gets a copy. str_const = str; assigns to that copy. You could pass a pointer to pointer to be able change the value of a pointer outside of your function, however you're allocating the string copy on the stack, and therefore the string copy becomes invalid once you leave the scope of reverse_const so this wouldn't make sense here.

If you want a string copy that survives the end of the reverse_const , allocate the array with malloc or do both the allocation and the copying with strdup . That you have to return the malloc'ed pointer somehow (by a return value of via a pointer to pointer argument) and the caller will then have the responsibility to free that malloc 'ed memory once it's done with it.

You need to write your function so that you have some way to return the argument modified.

One such solution is pass-by-reference :

void reverse_const(const char **str_const){
    const char *in = *str_const;
    char *out = malloc(strlen(in)+1);

    /* write to out */

    *str_const = out;
}

But a better way would be to use the return value:

char *reverse_const(const char *str_const){
    const char *in = str_const;
    char *out = malloc(strlen(in)+1);

    /* write to out */

    return out;
}

The return type could be const char * , but that's an unnecessary restriction since you know the returned string may be safely modified.

Note that both examples use malloc . Arrays cannot be passed back, in this manner, in C, as they exist on the stack and are destroyed when the function exits.

Whenever a long-running program uses malloc , there really ought to be a matching free somewhere in the code, or else you'll have a memory leak. It's a pain, but something all C programmers must master.

If you want the reversed str2 back in main() , you will either need to pass an adequately sized buffer to reverse_const to hold the reversed string, or you will need to dynamically allocate storage for it in reverse_const (a local variable length array won't do):

#include <stdlib.h>
...
void reverse_const (const char **str_const)
{
    int c_size = strlen (*str_const);
    char *str = calloc (c_size + 1, sizeof *str);
    strcpy (str, *str_const);
    char *c_begin = str, *c_end = str + (c_size - 1);

    int i;
    for (i = 0; i < c_size / 2; i++) {
        *c_begin ^= *c_end;
        *c_end ^= *c_begin;
        *c_begin ^= *c_end;

        c_begin++;
        c_end--;
    }

    *str_const = str;
    printf ("%s\n", *str_const);
}

int main (void) {

    char str1[] = "Indiana";
    char *str2 = "Kentucky";

    printf ("TESTS:\nString 1 pre-reversal: %s\n", str1);

    reverse (str1);

    printf ("String 1 post-reversal: %s\n", str1);
    printf ("Constant string 2 pre-reversal: %s\n", str2);

    reverse_const ((const char **)&str2);

    printf ("Constant string 2 post-reversal: %s\n", str2);

    free (str2);

    return 0;
}

Output

$ ./bin/revconststr
TESTS:
String 1 pre-reversal: Indiana
String 1 post-reversal: anaidnI
Constant string 2 pre-reversal: Kentucky
ykcutneK
Constant string 2 post-reversal: ykcutneK

Returning The Pointer

You have an additional option to return the pointer to str to assign to str2 in main() . This is more what you would normally expect to see. Let me know if you have any questions:

char *reverse_const2 (const char **str_const)
{
    int c_size = strlen (*str_const);
    char *str = calloc (c_size + 1, sizeof *str);
    strcpy (str, *str_const);
    char *c_begin = str, *c_end = str + (c_size - 1);

    int i;
    for (i = 0; i < c_size / 2; i++) {
        *c_begin ^= *c_end;
        *c_end ^= *c_begin;
        *c_begin ^= *c_end;

        c_begin++;
        c_end--;
    }

    //*str_const = str;
    printf ("%s\n", *str_const);

    return str;
}

int main (void)
{

    char str1[] = "Indiana";
    char *str2 = "Kentucky";

    printf ("TESTS:\nString 1 pre-reversal: %s\n", str1);

    reverse (str1);

    printf ("String 1 post-reversal: %s\n", str1);
    printf ("Constant string 2 pre-reversal: %s\n", str2);

    str2 = reverse_const2 ((const char **)&str2);

    printf ("Constant string 2 post-reversal: %s\n", str2);

    free (str2);

    return 0;
}

The problem here is that you modify the argument passed to reverse_const , but arguments in C are passed by value meaning that they are copied. The variable you modify in the function is a copy of the original pointer, changing a copy will of course not change the original.

C doesn't have pass by reference which is needed here, but it can be emulated by using pointers, in the case of your function you need to pass a pointer to the pointer, then use the dereference operator * to modify what the pointer to pointer points to, and use the address-of operator & when calling the function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM