【问题标题】:Modify const char * in C修改 C 中的 const char *
【发布时间】:2016-01-20 16:43:42
【问题描述】:

我正在练习面试。我目前遇到的问题是在 C 中反转一个常量字符串。我知道由于 str2 是 const,我可以修改 str2 指向的位置,但不能修改它的值。我在下面有一个名为 reverse_const 的函数。它将反转 const char *str_const 并打印它。但是,当我尝试从 main 方法反转后打印 st2 时,字符串不再反转。它就像 reverse_const() 是临时改变 str2 的内存位置。我在这里做错了什么?

#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);
}

【问题讨论】:

  • char str[c_size]; 不足以容纳长度为 c_sizenul-terminated 字符串。
  • 我在这里做错了什么?你没有做任何改变str2指向的地方。你已经发现你需要改变它指向的位置来解决这个问题......但是上下文也很重要! str_const = str;str2 没有影响,这是在 main()s 上下文中。
  • "我可以修改 str2 指向的位置" 如果该位置不变,则不会。指针不是数组或字符串(数组也不是字符串)。请阅读一本好的 C 书籍中有关指针、数组和字符串文字的内容。
  • @Olaf 也许我应该说“我可以更改 str2 指向的地址,但不能更改原始地址的值。”?
  • str2(使用标记!)不指向地址。您可以更改的只是它自己的给定值。而str2 仍然是不是字符串!既不是常量也不是非常量!忘记那个更难阅读/理解的异或交换。使用临时变量并将其放入自己的函数中。使它成为static 很可能会使编译器自动内联它。这不会变慢。

标签: c pointers pass-by-reference


【解决方案1】:

如果您希望将反向的 str2 重新放入 main(),则需要将足够大小的缓冲区传递给 reverse_const 以保存反向字符串,或者您需要在 @987654324 中为其动态分配存储空间@(局部变长数组不行):

#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;
}

输出

$ ./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

返回指针

您还有一个附加选项可以返回指向str 的指针以分配给main() 中的str2。这更多是您通常期望看到的。如果您有任何问题,请告诉我:

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;
}

【讨论】:

  • 如果您返回指向反转字符串的指针,而不是覆盖 str2,我更喜欢 reverse_const 函数。
  • 同意,我只是在使用给定函数的形式,不知道是否会发生变化。
  • @JohnBode - 添加返回作为第二个示例。
【解决方案2】:

你需要编写你的函数,以便有办法返回修改后的参数。

一个这样的解决方案是按引用传递

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

    /* write to out */

    *str_const = out;
}

但更好的方法是使用返回值:

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

    /* write to out */

    return out;
}

返回类型可以是const char *,但这是一个不必要的限制,因为您知道返回的字符串可以安全地修改。

请注意,两个示例都使用malloc。数组不能以这种方式在 C 中传回,因为它们存在于堆栈中,并在函数退出时被销毁。

每当一个长时间运行的程序使用malloc 时,代码中的某处确实应该有一个匹配的free,否则就会出现内存泄漏。这很痛苦,但所有 C 程序员都必须掌握。

【讨论】:

  • 任何使用内存分配而不提供释放该分配的细节的建议通常对读者来说是危险的(他们可能对动态分配了解不够,无法看到完整的要求)。
【解决方案3】:

当你通过参数传递时,函数会得到一个副本。 str_const = str; 分配给该副本。您可以将指针传递给指针,以便能够在函数外部更改指针的值,但是您在堆栈上分配字符串副本,因此一旦离开reverse_const 的范围,字符串副本就会变得无效,所以这在这里没有意义。

如果您想要在reverse_const 末尾保留的字符串副本,请使用malloc 分配数组,或者使用strdup 进行分配和复制。您必须以某种方式返回malloc'ed 指针(通过指向指针参数的指针的返回值),然后调用者将负责free malloc'ed 内存一旦完成。

【讨论】:

    【解决方案4】:

    这里的问题是您修改了传递给reverse_const 的参数,但是C 中的参数是按值传递 意味着它们被复制了。你在函数中修改的变量是原指针的一个copy,改变一个副本当然不会改变原来的。

    C 没有此处需要的 按引用传递,但可以通过使用指针模拟,对于您的函数,您需要传递一个指针的指针,然后使用解引用操作符*修改指针指向的内容,调用函数时使用地址操作符&amp;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-22
      • 1970-01-01
      • 2020-08-01
      • 2015-09-22
      • 2020-01-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多