C 如果使用函数中的指针反转字符串,则main中的输出将被篡改

C 如果使用函数中的指针反转字符串,则main中的输出将被篡改,c,pointers,C,Pointers,我正在尝试使用Stephen Prata的C语言自学,本章结尾的练习之一是编写一个函数,用反转的字符串替换字符串的内容。。这是关于字符串的一章,有大量的指针。我尽可能多地使用指针,以便更好地理解,但我被卡住了 我的问题是,当我在main中打印返回指针的值时,它是乱码 当我使用gdbjust学习如何使用它时,我可以看到从我的函数返回的内存地址与在函数中使用的地址相同,并且它被分配给main中的指针,据我所知,这是正常的 我尝试了很多东西,我错过了什么?FWIW我还没有在这本书中了解到malloc,

我正在尝试使用Stephen Prata的C语言自学,本章结尾的练习之一是编写一个函数,用反转的字符串替换字符串的内容。。这是关于字符串的一章,有大量的指针。我尽可能多地使用指针,以便更好地理解,但我被卡住了

我的问题是,当我在main中打印返回指针的值时,它是乱码

当我使用gdbjust学习如何使用它时,我可以看到从我的函数返回的内存地址与在函数中使用的地址相同,并且它被分配给main中的指针,据我所知,这是正常的

我尝试了很多东西,我错过了什么?FWIW我还没有在这本书中了解到malloc,尽管我经常光顾各种各样的www页面,试图更好地理解C


$ cc -o exercise8 exercise8.c && ./exercise8
This is s1 before: abcd
This is s2 in function: dcba
This is s3 after: d`!

您返回一个指向本地变量的指针,自动变量以ISO标准术语表示,它是在堆栈上分配的,当您从函数返回时,内存被释放,留下一个指向内存的悬空指针,该内存可能包含也可能不包含您放在那里的字符串,这完全取决于环境。应该将输出缓冲区作为函数参数提供,或者用Malc分配它,或者用新的.< /P>用C++来分配它。 编辑;添加了一些示例代码

void reverse(const char* s1, char* s2) {
  const int l = strlen(s1);
  const char* p = s1 + l - 1;
  do {
    *s2++ = *p;
  } while (p-- != s1);
  *s2 = 0;
}

int main() {
  // some code here
  char s1[5] = "abcd";
  char s2[5] = "";

  reverse(s1, s2);

  // some more code here

  return 0;
}


您返回一个指向本地变量的指针,自动变量以ISO标准术语表示,它是在堆栈上分配的,当您从函数返回时,内存被释放,留下一个指向内存的悬空指针,该内存可能包含也可能不包含您放在那里的字符串,这完全取决于环境。应该将输出缓冲区作为函数参数提供,或者用Malc分配它,或者用新的.< /P>用C++来分配它。 编辑;添加了一些示例代码

void reverse(const char* s1, char* s2) {
  const int l = strlen(s1);
  const char* p = s1 + l - 1;
  do {
    *s2++ = *p;
  } while (p-- != s1);
  *s2 = 0;
}

int main() {
  // some code here
  char s1[5] = "abcd";
  char s2[5] = "";

  reverse(s1, s2);

  // some more code here

  return 0;
}

上面的代码将使s2指向堆栈上的字符串。函数完成后,此ar3变量将被删除

您应该输出到预先分配的某个变量。修改如下

int main(void)
{
   char * s1  = "abcd";
   char s3[5];
   printf("This is s1 before: %s\n", s1);

   str_rev(s1, s3);
   printf("This is s3 after: %s\n", s3);

}

void str_rev(char * string, char * s2)
{
   ........

   // don't return

   // Also assign the last character with the NULL terminator
   ar2[strlen(string)] = '\0';
}
当然,一旦你读到关于malloc的章节,你可以根据s1的长度为s3分配必要的内存。在那之前,请继续阅读并享受乐趣

上面的代码将使s2指向堆栈上的字符串。函数完成后,此ar3变量将被删除

您应该输出到预先分配的某个变量。修改如下

int main(void)
{
   char * s1  = "abcd";
   char s3[5];
   printf("This is s1 before: %s\n", s1);

   str_rev(s1, s3);
   printf("This is s3 after: %s\n", s3);

}

void str_rev(char * string, char * s2)
{
   ........

   // don't return

   // Also assign the last character with the NULL terminator
   ar2[strlen(string)] = '\0';
}
当然,一旦你读到关于malloc的章节,你可以根据s1的长度为s3分配必要的内存。在此之前,请继续阅读并享受乐趣。

s2指向您的本地ar3阵列。因此,当str_rev返回时,通过s2查看ar3所在的内存不再有效。现在s2被称为悬空指针,这是学习正确使用C指针的巨大痛苦之一

对于不使用malloc且满足替换字符串内容的练习要求的简单解决方案,请尝试将结果复制到原始字符串的函数参数指针中;但是要小心,因为您当前的代码已经更改了指针字符串。您知道,此指针指向内存中有足够的字符,并且不是函数的本地指针。

s2指向本地ar3数组。因此,当str_rev返回时,通过s2查看ar3所在的内存不再有效。现在s2被称为悬空指针,这是学习正确使用C指针的巨大痛苦之一


对于不使用malloc且满足替换字符串内容的练习要求的简单解决方案,请尝试将结果复制到原始字符串的函数参数指针中;但是要小心,因为您当前的代码已经更改了指针字符串。您知道,此指针指向内存,包含足够的字符,并且不是函数的本地指针。

问题描述听起来好像您可以在适当的位置反转字符串。保持简单

void reverse_range(char *first, char *last) // [first, last)
{
    for (; first != last && first != --last; ++first)
    {
        char temp = *first; *first = *last; *last = temp;
    }
}

void reverse(char *str)
{
    reverse_range(str, str + strlen(str));
}

int main()
{
    char text[] = "0123456789";

    printf("before: %s\n", text);
    reverse(text);
    printf("after : %s\n", text);
}

问题描述听起来像是可以在适当的位置反转字符串。保持简单

void reverse_range(char *first, char *last) // [first, last)
{
    for (; first != last && first != --last; ++first)
    {
        char temp = *first; *first = *last; *last = temp;
    }
}

void reverse(char *str)
{
    reverse_range(str, str + strlen(str));
}

int main()
{
    char text[] = "0123456789";

    printf("before: %s\n", text);
    reverse(text);
    printf("after : %s\n", text);
}

问题的一部分在于,在处理字符串文字时,实际上无法用反转的字符串替换字符串的内容,即字符串的形式为char*s1=abcd

在不使用文字的情况下,我制作了一个相对容易理解的递归示例:

/*   A function that replaces the contents of a string with the string reversed. */
#include <stdio.h>
#include <string.h>
void str_rev(char * string);

int main(void)
{

   char s1[] = "abc";
   char s2[] = "even";
   char s3[] = "quodd";


   printf("This is s1 before: %s\n", s1);
   str_rev(s1);
   printf("This is s1 after: %s\n", s1);

   printf("This is s2 before: %s\n", s2);
   str_rev(s2);
   printf("This is s2 after: %s\n", s2);

   printf("This is s3 before: %s\n", s3);
   str_rev(s3);
   printf("This is s3 after: %s\n", s3);

    return 0;

}

void str_rev(char * string) {

    //Store the first char of the string locally
    char firstChar = string[0];
    //Store the last char of the string locally
    int lastCharPos = strlen(string)-1;
    char lastChar = string[lastCharPos];

    //Shorten the string (temporarily)
    string[lastCharPos] = '\0';


    if (string[1] != '\0') {
        //Call on the now shortened string, eg.
        //"abc" becomes "b"
        //"even" becomes "ve"
        //"quodd" becomes "uod"
        str_rev(string+1);
    }

    //Swap the first and last characters
    string[0] = lastChar;
    string[lastCharPos] = firstChar;

}

问题的一部分在于,在处理字符串文字时,实际上无法用反转的字符串替换字符串的内容,即字符串的形式为char*s1=abcd

在不使用文字的情况下,我制作了一个相对容易理解的递归示例:

/*   A function that replaces the contents of a string with the string reversed. */
#include <stdio.h>
#include <string.h>
void str_rev(char * string);

int main(void)
{

   char s1[] = "abc";
   char s2[] = "even";
   char s3[] = "quodd";


   printf("This is s1 before: %s\n", s1);
   str_rev(s1);
   printf("This is s1 after: %s\n", s1);

   printf("This is s2 before: %s\n", s2);
   str_rev(s2);
   printf("This is s2 after: %s\n", s2);

   printf("This is s3 before: %s\n", s3);
   str_rev(s3);
   printf("This is s3 after: %s\n", s3);

    return 0;

}

void str_rev(char * string) {

    //Store the first char of the string locally
    char firstChar = string[0];
    //Store the last char of the string locally
    int lastCharPos = strlen(string)-1;
    char lastChar = string[lastCharPos];

    //Shorten the string (temporarily)
    string[lastCharPos] = '\0';


    if (string[1] != '\0') {
        //Call on the now shortened string, eg.
        //"abc" becomes "b"
        //"even" becomes "ve"
        //"quodd" becomes "uod"
        str_rev(string+1);
    }

    //Swap the first and last characters
    string[0] = lastChar;
    string[lastCharPos] = firstChar;

}

这段代码无法编译。您仍然假设它将返回字符串,而不是将指针作为参数传递。哇,哇,不需要aggr

令人沮丧的否决票。不过,你的评论已经得到了适当的注意。我之所以咄咄逼人,是因为另一个正确的帖子被否决了。我记下这一条只是为了暂时得分,因为你的代码无法编译,可能会让新手感到沮丧。我不确定这是否有礼貌,我是新来的。我现在修改了投票结果,因为它已经编译好了。@stefan:我不确定它是否有礼貌。不,可能不是。对于那些完全错误或毫无帮助的答案,我保留我的反对票。小的编码错误或其他好答案中的打字错误最好在注释中标记出来。@Roddy Yeah。如果不是因为对正确答案投了完全错误的反对票,我就不会考虑投反对票。此外,在这种情况下,我不认为这是一个小错误,因为它只关注两个代码行。您仍然假设它将返回字符串,而不是将指针作为参数传递。哇,哇,不需要积极的向下投票。不过,你的评论已经得到了适当的注意。我之所以咄咄逼人,是因为另一个正确的帖子被否决了。我记下这一条只是为了暂时得分,因为你的代码无法编译,可能会让新手感到沮丧。我不确定这是否有礼貌,我是新来的。我现在修改了投票结果,因为它已经编译好了。@stefan:我不确定它是否有礼貌。不,可能不是。对于那些完全错误或毫无帮助的答案,我保留我的反对票。小的编码错误或其他好答案中的打字错误最好在注释中标记出来。@Roddy Yeah。如果不是因为对正确答案投了完全错误的反对票,我就不会考虑投反对票。此外,在这种情况下,我不认为这是一个小错误,因为它只关注两行代码的值。抱歉,但究竟为什么要递归地进行字符串处理,它不必要地使事情复杂化,更糟的是,不必要地对可以处理的字符串的长度施加人为限制。用一根长长的绳子打电话会让你脸上炸开一个巨大的烟囱。。。递归对于树和东西都是很好的,但对于字符串,按定义定义的字符串序列,为什么你会考虑它。@写一个函数,用字符串反转一个字符串的内容。我很高兴地注意到你的不符合这个规格。不过下次运气会好一些:事实上,我刚刚尝试了你们的程序,我得到了一些非常好的结果!对,事实上,我的代码中有一个小小的错误,现在已经全部修复,使得注释完全无效。。。嗯。这里仍然没有充分的理由使用任何递归性。还有,仅仅因为别人对你的答案发表了负面评论就否决了别人。。。我希望你能花点时间早点看其他答案,并对我的答案做出负面评论,这样我就可以早点解决它。顺便说一句,如果你能给我一个使用递归的好理由,我很乐意听你说。1这是一个练习,学习递归的概念很有用。2这个方法很有用,因为它假设没有malloc的先验知识。3符合问题中的规范。事实上,我更喜欢Blastfurne的答案。1如果你想教授递归的概念,请使用你实际使用它的上下文,比如一棵树。2我的第一个例子也是如此。3如果你喜欢另一个答案,就投票给另一个答案,不要仅仅因为你不喜欢就投票给其他东西。对不起,但你到底为什么要递归地处理字符串,它不必要地使事情复杂化,更糟糕的是,不必要地对你可以处理的字符串的长度施加人为限制。用一根长长的绳子打电话会让你脸上炸开一个巨大的烟囱。。。递归对于树和东西都是很好的,但对于字符串,按定义定义的字符串序列,为什么你会考虑它。@写一个函数,用字符串反转一个字符串的内容。我很高兴地注意到你的不符合这个规格。不过下次运气会好一些:事实上,我刚刚尝试了你们的程序,我得到了一些非常好的结果!对,事实上,我的代码中有一个小小的错误,现在已经全部修复,使得注释完全无效。。。嗯。这里仍然没有充分的理由使用任何递归性。还有,仅仅因为别人对你的答案发表了负面评论就否决了别人。。。我希望你能花点时间早点看其他答案,并对我的答案做出负面评论,这样我就可以早点解决它。顺便说一句,如果你能给我一个使用递归的好理由,我很乐意听你说。1这是一个练习,学习递归的概念很有用。2这个方法很有用,因为它假设没有malloc的先验知识。3符合问题中的规范。实际上我更喜欢高炉的答案。1如果你想教c
递归概念使用一个实际使用它的上下文,就像树一样。2我的第一个例子也是如此。3如果你喜欢另一个答案,就投票给另一个答案,不要仅仅因为你不喜欢就投票给那些东西。不管是谁先投了反对票,都可能因为这个原因投了反对票。在第一个代码示例中,printf%s\n,s1;给出输出abcd,并打印%s\n,s2;给出输出dcb。第二个例子更糟糕,在我的unix系统上导致了一个异常,堆栈跟踪以检测到的***glibc***./wich2:free:invalid pointer:0x0830b007***开头。更不用说,这两个示例即使有代码更正也不符合最初的规范:编写一个函数,用反转的字符串替换字符串的内容。。哦,好吧:我的第一次否决票^^^@William如果你真的指出代码中有问题,我会很高兴,这样我就可以修复它了,就像我现在所做的那样。这是我在飞行中写的,因为我还是人类,所以我会犯错误。事实上,第一次否决是在代码出现之前,我只是在代码中添加了说明如何进行字符串反转的内容。至于不进行就地反转,我之所以这样做是因为这是原始海报所针对的解决方案。无论是谁先投了反对票,都可能是因为这个原因而投了反对票。在第一个代码示例中,printf%s\n,s1;给出输出abcd,并打印%s\n,s2;给出输出dcb。第二个例子更糟糕,在我的unix系统上导致了一个异常,堆栈跟踪以检测到的***glibc***./wich2:free:invalid pointer:0x0830b007***开头。更不用说,这两个示例即使有代码更正也不符合最初的规范:编写一个函数,用反转的字符串替换字符串的内容。。哦,好吧:我的第一次否决票^^^@William如果你真的指出代码中有问题,我会很高兴,这样我就可以修复它了,就像我现在所做的那样。这是我在飞行中写的,因为我还是人类,所以我会犯错误。事实上,第一次否决是在代码出现之前,我只是在代码中添加了说明如何进行字符串反转的内容。至于不进行原地翻转,我之所以这样做是因为这是原始海报所针对的解决方案。
This is s1 before: abc
This is s1 after: cba
This is s2 before: even
This is s2 after: neve
This is s3 before: quodd
This is s3 after: ddouq
#include <stdio.h>
#include <string.h>

void my_strrev(char* begin){
    char temp;
    char* end;
    end = begin + strlen(begin)-1;

    while(end>begin){
        temp = *end;
        *end = *begin;
        *begin = temp;
        end--;
        begin++;
    } 
}

main(){
    char string[]= "foobar";
    my_strrev(string);
    printf("%s", string);
}