【问题标题】:How does printf processes it's arguments when one is pass by reference of another argument当一个参数通过另一个参数的引用传递时,printf 如何处理它的参数
【发布时间】:2016-02-14 18:03:03
【问题描述】:

我发现了一些有趣的东西,但无法解释。 我正在编写一个简单的例程来反转字符串。这工作正常,没有抱怨。

我的问题在printf。当我单独打印原始字符串时,它打印正确,但是当我将原始字符串打印为第一个 arg 并将函数调用 reverse 作为第二个时,两者都显示为 reversed。

输出:

|| abcdthelgko ||
原始字符串:| okglehtdcba |, Rev Str | okglehtdcba |

代码:

char* ReverseStr(char* str, int len)
{
  char *start = &str[0], *end = &str[len-1];
  char temp;

  while(start < end)
  {
    temp = *start;
    *start = *end;
    *end = temp;

    start++;
    end--;
  }
  return str;
}

int main()
{
  char str_unique[] = "abcdthelgko";
  int str_unique_len = sizeof(str_unique)/sizeof(str_unique[0]);

  printf("\n || %s || \n", str_unique);
  printf("Orig Str: | %s |, Rev Str | %s |\n", str_unique, ReverseStr(str_unique, str_unique_len-1));
  return 0;
}

* 反馈后修改:* 测试 2 个理论 1、printf从右到左处理cmets 2. 如果函数调用是其中一个 args,则在 printf 之前调用该函数, 我做了另一个示例代码来更改通过引用传递的变量的值。我看到的是,在变量值中,它可以正常工作: ================== 输出======================

 i = 5, changed i = 2, again i= 2 

 changed i = 2, i= 2 

 i = 5, changed i = 2 

===================代码======================

int* change (int* addr);

int main()
{
  int i;
  i=5;
  printf("\n i = %d, changed i = %d, again i= %d \n ", i, *change(&i), i);

  i=5;
  printf("\n changed i = %d, i= %d \n", *change(&i), i);

  i=5;
  printf("\n i = %d, changed i = %d \n", i, *change(&i));
  return 0;
}

int* change (int* addr)
{
  *addr = 2;
  return (addr);
}

【问题讨论】:

  • 提示:尝试使用与第一个相同的第三个 printf。
  • 嗨,为了检查理论,printf 从右到左处理参数,或者如果函数调用是 printf 的参数,在调用 printf 函数之前首先处理,我写了另一个代码。而不是 str reverse,我尝试了一个简单的实验来更改 ref 传递的变量值......在这种情况下,我看到了原始输出。 i 是 = 5,我将 i 的地址传递给在该位置写入 2 的函数:[CODE]: printf("\n i = %d, changed i = %d \n", i, *change(&amp;i)); [OUTPUT]: i = 5, changed i = 2

标签: c string pointers arguments printf


【解决方案1】:

您可以在 ReverseStr 中复制传递的字符串并在不做任何修改的情况下返回它,同时传递的字符串将被反转。 (返回通过引用传递的东西通常是多余的,但是通过返回原始副本,您的函数将证明值得返回一些东西而不是返回 void)。

或者分配内存来保存反转的字符串,并且永远不要改变原始字符串:

char* ReverseStr(char* str, int len) {
    char *reversed = malloc(len * sizeof(char));
    int i, j = len - 1;
    for (i = 0; j >= 0; i++) {
        reversed[i] = str[j--];
    }

    return reversed;
}

返回原始但反转传递的 str 所以你可能想在你的 main 中反转 print 语句

char* ReverseStr(char* str, int len) {
    char *start = &str[0], *end = &str[len-1];
    char temp;
    int i;

    char *original = malloc(len * sizeof(char));
    for (i = 0; str[i] != '\0'; i++) {
        original[i] = str[i];
    }

    while (start < end) {
        temp = *start;
        *start = *end;
        *end = temp;

        start++;
        end--;
    }
    return original;
}

【讨论】:

  • 1.如问题本身所示,返回地址一点也不多余。 2.strdup.
  • 我想你是对的,但在这种情况下,我的意思是强调传递了一个引用,这样你就可以在 main 中访问它而不返回它。调用函数或在函数调用后简单地访问变量将给出相同的结果。
  • 这是一种风格/便利性的决定,对性能的影响最小(或完全没有经过正确的优化),但这是一种合法的模式
  • 嗨,我关心的不是“如何获取原始字符串并获取反向字符串?”。有很多方法可以做到这一点。我的观点是,当我首先打印原始字符串,然后调用反向时,我同意修改字符串就地......那么为什么原始字符串会被反转。这更像是一个关于 printf 如何处理它的问题,而不是一个反转字符串的问题!
  • 代码返回 original,它 not 指向字符串,因为数组不包含空字符。建议确保original 指向一个字符串。
【解决方案2】:

由于 ReverseStr 是在 就地 进行操作(称为 mutating 输入),因此“原始”字符串不再可用。

当您以另一个函数调用作为其参数之一来调用函数时,首先执行内部调用。考虑:

printf("%lf", pow(2, 2));

power (pow) 函数在printf 之前调用。您的代码中也是如此,并且会改变原始字符串,因此当调用 printf 时,它会获取已经“在两个参数中”都被反转的字符串的地址。

解决此问题的一种方法是分配原始字符串的副本,将其传递给ReverseStr,然后您将同时拥有两个版本:

int main()
{
  char str_unique[] = "abcdthelgko";
  char *str_dup = strdup(str_unique);
  int str_unique_len = sizeof(str_unique)/sizeof(str_unique[0]);

  printf("\n || %s || \n", str_unique);
  printf("Orig Str: | %s |, Rev Str | %s |\n", str_unique,
      ReverseStr(str_dup, str_unique_len - 1));

  free(str_dup); // Don't forget to free the memory allocated by strdup!
  return 0;
}

【讨论】:

  • 嗨,关于您的以下观点:当您以另一个函数调用作为其参数之一来调用函数时,首先执行内部调用。这似乎不是真的。我尝试了一个简单的实验来更改 ref 传递的变量值,而不是 str reverse……在这种情况下,我看到了原始输出。 i = 5 并且我将 i 的地址传递给在该位置写入 2 的函数: [ CODE: printf("\ni = %d, changed i = %d \n", i, *change(&i) );] [输出]: i = 5,改变 i = 2
  • 嗨,用另一个实验更新了这个问题......它似乎不成立!
  • @SougataChatterjee - 为什么你认为它不成立?你的例子再次证明了这一点。也不能是任何其他方式,在使用它的输出的函数返回之后调用函数是没有意义的。如果您愿意,也可以使用调试器验证这一点。
  • 因为如果它是真的,在 printf 中,当我打印 i 和更改(&i)时,两者都应该打印更改后的值 2。但我看到它正在打印 5 和 2。但是对于字符串.. ..字符串和 func(string) 都在打印反转的字符串。明白了吗?
  • @SougataChatterjee - 我只能说它确实以这种方式工作,而且在评论中解释(甚至是真正的答案)太长了。我对你的建议是从头开始,尽可能彻底地学习指针、非指针类型、函数调用和堆栈。我敢肯定,一旦您这样做了,您将对计算机的工作原理、编程的工作原理有更深入的了解,而这个具体问题对您来说将非常明显。
【解决方案3】:

函数 ReverseStr() 正在原位修改提供的字符串,而不是将其复制到新字符串中。

在 C 中,参数从右到左压入堆栈。在调用 printf() 时,首先推送 ReverseStr() 返回的结果(同时反转原始字符串 str_unique),然后推送指向 str_unique 的指针,但其内容被 ReversStr() 反转.

您应该通过将原始字符串复制到新的目的地来保持原始字符串不变。这个新的目的地既可以在 ReverseStr 内的运行时 (malloc) 中分配(使用完后别忘了释放它),也可以由调用者提供。

【讨论】:

  • “在 C 中,参数从右到左推入堆栈”——不——在某些情况下可能会发生这种情况,但一般情况下并非如此。参数可以按任何顺序传递,并且不需要它们在堆栈上。
  • 你是对的,它们并不总是在堆栈中传递。然而,当参数在寄存器中传递时,出于明显的兼容性原因,行为是完全相同的(参数函数从右到左执行)。
  • 这取决于 ABI - 一些 ABI 甚至混合使用基于寄存器和堆栈的参数传递。另请注意,求值顺序与传递参数的顺序不同 - 不要求以任何特定顺序求值参数。
【解决方案4】:

它在 printf 完成之前运行 ReverseStr,
存储在 str_unique 中的字符串在 ReverseStr
之后发生了变化 然后一切都变了:)

【讨论】:

  • 嗨,关于您的以下观点:“它在 printf 完成之前运行 ReverseStr”。这似乎不是真的。而不是 str reverse,我尝试了一个简单的实验来更改 ref 传递的变量值......在这种情况下,我看到了原始输出。 i 是 = 5,我将 i 的地址传递给在该位置写入 2 的函数:___CODE___ ::printf("\n i = %d, changed i = %d \n", i, *change(&amp;i));___OUTPUT___::i = 5, changed i = 2
  • 根据您的反馈使用最新实验更新了原始问题....似乎不适合更改数字的值。
  • 你的新实验在 vs2015 上输出这个:i = 2, changed i = 2, again i= 5 changed i = 2, i= 5 i = 2, changed i = 2
猜你喜欢
  • 2014-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 2011-10-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多