一般问题:在 C/C++ 中作为指针传递什么

General question: What to pass as pointer in C/C++?

本文关键字:指针 什么 C++ 问题      更新时间:2023-10-16

嘿,我想知道是否值得通过指针传递原始的单个值,如intfloatdoublechar?可能不值得!?但是,如果您只是通过指针传递所有内容,这是否会使程序变慢?你应该总是只将数组作为指针传递吗?谢谢!

我想知道是否值得通过指针传递原始的单个值,如 int、float、double 或 char?

你想完成什么?是否希望能够写入传入的值?还是您只需要使用它?如果你想写它,惯用的方式是通过引用传递。如果您不需要写入它,则最好避免意外写入它并按值传递的任何风险。按值传递将创建变量的副本以供本地使用。(顺便说一句,如果您不想复制并且想要一定程度的安全性,您可以通过 const 引用)

但是,如果您只是通过指针传递所有内容,这是否会使程序变慢?

很难说。取决于很多事情。在按值传递和按引用(或指针)传递中,您创建一个新的基元类型。按值传递,您正在制作副本。在通过引用/指针传递时,您将地址传递给原始地址。但是,在后一种情况下,您需要额外的内存提取,该内存可能会缓存,也可能不会缓存。如果不测量它,很难说 100%。

话虽如此,我怀疑差异是否明显。编译器可能能够在许多按值传递的情况下优化副本,如本文所述。(感谢空格 C0wb0y)。

你应该总是只将数组作为指针传递吗?

由此。

C++不可能按值将完整的内存块作为参数传递给函数,但允许我们传递其地址。

传递数组:

 int foo(int bar[], unsigned int length)
 {
       // do stuff with bar but don't go past length
 }

我建议避免使用数组并使用std::vector,它具有更容易理解的复制语义。

如果您关心的是速度,那么通过指针传递基元值可能不值得 - 然后您就有"间接"的开销来访问该值。

但是,指针通常是"总线的宽度",这意味着处理器可以一次发送整个值,而不是"移位"值以发送总线。 因此,指针在总线上的传输速度可能比较小的类型(如 char)更快。 这就是为什么旧的Cray计算机曾经将其char值设为32位(当时总线的宽度)的原因。

在处理大型对象(如类或数组)时,传递指针比将整个对象复制到堆栈上更快。这适用于 OOP,例如

查看您最喜欢的C++教科书,了解"输出参数"的讨论。


将指针用于输出参数而不是引用的一些优点是:

    没有
  • 令人惊讶的行为,没有远距离操作,呼叫站点和调用方的语义都很清楚。

  • 与 C 的兼容性(您的问题标题表明这一点很重要)

  • 从共享库或 DLL 导出的函数可由其他语言使用,不应使用仅C++功能(如引用)。

您很少需要通过指针传递任何内容。如果需要修改参数的值,或者想要防止复制,则按引用传递,否则按值传递。

请注意,防止复制也可以

通过复制省略来完成,因此您必须非常小心,不要陷入过早优化的陷阱。这实际上会使您的代码变慢。

你的问题没有真正的答案,除了我倾向于记住的几个规则:char 是 8 个字节,指针是 4 个字节,因此切勿将单个 char 作为指针传递。在 int 和 float 等东西与指针大小相同但必须引用指针之后,从技术上讲需要更多时间

如果我们去奔腾 i386 汇编程序:

在 C 中的参数"a"寄存器中加载值,该参数为 int:

movl 8(%ebp),%eax  

同样的事情,但作为指针传递:

movl 8(%ebp),%eax
movl (%eax),%eax

必须取消引用指针需要另一个内存操作,因此理论上(不确定是否在现实生活中)传递指针的时间更长......出现内存问题后。如果你想有效地编码,所有组合类型(类,结构,数组......)都必须通过指针传递。想象一下,用一种 16 字节的类型做一个递归函数,该函数通过 copy 传递 1000 次调用,在堆栈中产生 16000 字节(你真的不想这样吗:)?

因此,为了使它简短

明了:查看您的类型的大小,如果它大于指针传递它,否则通过副本传递它......

值和对象传递基元类型作为const引用。尽可能避免指针。取消引用指针有一些开销,并且会使代码混乱。比较以下两个版本的factorial函数:

// which version of factorial is shorter and easy to use?
int factorial_1 (int* number)
{
  if ((*number) <= 1)
    return 1;
  int tmp = (*number) - 1;
  return (*number) * factorial_1 (&tmp);
}
// Usage:
int r = 10;
factorial_1 (&r); // => 3628800

int factorial_2 (int number)
{
  return (number <= 1) ? 1 : (number * factorial_2 (number - 1));
}
// Usage:
// No need for the temporary variable to hold the argument.
factorial_1 (10); // => 3628800

调试变得困难,因为您无法说出对象的值何时何地可以更改:

int a = 10;
// f cound modify a, you cannot guarantee g that a is still 10.
f (&a); 
g (&a);

首选 vector 类而不是数组。它可以根据需要增长和收缩,并跟踪其大小。访问元素vector方式与数组兼容:

int add_all (const std::vector<int>& vec)
{
    size_t sz = vec.size ();
    int sum = 0;
    for (size_t i = 0; i < sz; ++i)
        sum += vec[i]; 
}

NO中,您唯一会传递非常量引用的情况是函数需要输出参数。

相关文章: