为什么某些C / C ++函数使用指针作为参数?

时间:2011-08-14 02:46:44

标签: c++ c pointers

我来自C#,我正在学习C ++(带this tutorial)因此我对内存的知识相对较少,但我在指针中看到的唯一用途是“节省空间”和迭代通过数组。那么为什么像scanf函数这样的函数会将指针作为参数呢?例如:

printf("What's your name?: "); 然后: scanf("%s",&userName)。将变量本身作为参数传递会不会更有意义?我正在看的C书说,使用变量本身会产生意想不到的结果,但我不明白为什么。有谁可以用C ++的方式启发我?我从学习C开始,因为我意识到我有多喜欢OOP。

8 个答案:

答案 0 :(得分:6)

有三种不同的方法可以将变量传递给C ++中的函数,传递副本,传递引用和传递指针。

#include <iostream>

void passByCopy(int a)
{
   a += 1;
}

void passByReference(int &a)
{
   a += 1;
}

void passByPointer(int *a)
{
   (*a) += 1; // De-reference then increment.
}

int main()
{
   int a = 0;
   // Passing by copy, creates a copy of the 'a' object, then sends it to the function.
   passByCopy(a);
   std::cout << a << std::endl; // Outputs 0

   // Passing by reference, causes the 'a' object in the function to reference the 'a'
   // object at this scope. The value of 'a' will change. 
   passByReference(a);
   std::cout << a << std::endl; // Outputs 1

   // Passing by pointer, does almost the same thing as a pass by reference, except a 
   // pointer value can by NULL, while a reference can't.
   passByPointer(&a);

   std::cout << a << std::endl; // Outputs 2
}

使用scanf,该函数的目的是将值传递给当前作用域中的变量,因此它不能使用pass by copy。它不使用传递引用有两个原因,一个是它是一个旧的C函数,因此在编写时不存在引用传递。第二个是它是一个可变函数,这意味着函数内部接收一个指针列表而不是一系列参数。

答案 1 :(得分:6)

C和C ++按值传递变量;形式参数是内存中与实际参数不同的对象。因此,如果在函数中修改形式参数,则不更改实际参数的值。

通过传递指针,该函数可以修改实际参数的内容:

void swap(int *a, int *b)
{
  int t = *a;
  *a = *b;
  *b = t;
}

...
swap(&x, &y);

因此,在*a中写入swap等同于在来电者中写入x

C中的指针有三个主要目的:

  • 伪造pass-by-reference语义,如上所述(正如Karl在评论中所说,C ++有一种支持真正的pass-by-reference的机制);
  • 跟踪动态分配的内存(内存分配函数malloccallocrealloc以及C ++ new运算符返回指针值);
  • 构建动态数据结构(树,列表,队列,堆栈等)。

答案 2 :(得分:4)

所有参数都以C中的值传递。如果要通过引用传递,则需要显式传递相关变量的地址。当您希望函数修改传入的变量时,按引用传递非常重要。

C ++有引用,但由于C ++代码经常调用C库函数,你有时会看到C ++中使用的相同语法。

答案 3 :(得分:2)

在您的具体示例中,scanf是C标准库的一部分。 C没有字符串类,因此字符串表示为字符数组。

在C中,通常在需要字符串时传递第一个字符的地址。这可以是&str[0]str。我不确定&username如果期望char *是正确的,我想这取决于用户名。

答案 4 :(得分:1)

C没有通过引用传递的概念,只能返回一个值。因此,传递指针的传统目的是允许被调用函数写入变量,以便调用者可以读取已写入的内容。

C ++能够通过引用传递,但scanfprintf是C函数,在构建为C ++时也可用,因此无法使用它们。

编辑:进一步解释,甚至在可变参数问题旁边,在下面的代码中:

void calledFunction(int var1, int var2)
{
  var1 = 23;
  var2 = 19;
}

void callingFunction(void)
{
    int v1 = 9, v2 = 18;

    calledFunction(v1, v2);
    printf("%d %d", v1, v2);
}

输出为“9 18”。 calledFunction获取v1v2的本地副本,因为C按值传递,这意味着v1v2的值已传入,但没有其他链接被保留在原件上。因此,它修改副本的事实对原件没有影响。

答案 5 :(得分:1)

在C中没有引用,所有参数都按值传递。

如果scanf位于C#中,则其参数前面会有outref关键字,并且不需要指针。传递指针允许scanf函数将一些值写入指针指向的任何位置。

但是在C ++中有引用,但与C#中的知识不同。尽管如此,如果您使用iostream库而不是从C继承到C ++的stdio,那么您就不必以这种方式使用指针。你刚才写道:

String username;
cin >> username;

答案 6 :(得分:1)

我在其他文章中没有提到的指针的另一个用途是,它是C函数可以返回多个值的唯一两种方式之一。 C函数被定义为返回单个对象,但它可以接收许多参数。如果你的多值函数总是返回相同的值集,那么方便的问题是将值包装在结构中并使函数返回结构或使函数接收指针,通过它可以写入值。

如果函数还需要返回状态或错误代码,则指针参数选项变得更有吸引力(因为通过指针写入它是愚蠢的,迫使调用者分配另一个变量并阻碍标准习语,如{{1 }})。如果这组值在编译时是不可预测的(比如scanf,它必须在运行时解释格式字符串),那么指针参数将成为唯一(明智的)选项。

编辑:在您的示例中,if(f()==ERROR)...假设用户名是数组类型或指向适合保存字符串的存储的指针,则无需传递地址。如果它是一个数组,它将作为一个指针隐式传递。如果它已经是一个指针,则取地址会产生指向指针的指针,这不适合保存字符串。所以请放下scanf("%s",&username);

答案 7 :(得分:0)

scanf是一个C函数。 C没有引用,所以它必须使用指针来写入参数。此外,scanf是一个varargs函数,因此它需要可变数量的参数。而varargs函数只能采用内置类型:指针(对任何东西),整数,双精度。