c语言值传递和引用传递
First of all, this article is NOT going to explain C++
首先,本文不会解释C ++
references and 引用和 pointers. If you are not clear on what these are then this is quite probably the wrong article for you and you really should probably go read 指针 。 如果您不清楚这些是什么,那么这可能对您来说是错误的文章,您真的应该去 here, 这里 , here or 这里或 here. 这里阅读。In C++ all arguments are passed to functions by value. In other words, a copy of the value is taken and that is what the function has to work with. If you want to modify the original value you must either pass a pointer or reference to the original value you wish to modify. Of course, even the pointer or reference are still passed by value. Pointers and references are just another C++ type and when they are passed around you do so by value.
在C ++中,所有参数都按值传递给函数。 换句话说,将获取值的副本,这就是函数必须使用的值。 如果要修改原始值,则必须传递一个指针或引用要修改的原始值。 当然,即使指针或引用也仍然按值传递。 指针和引用只是另一种C ++类型,当它们传递给您时,它按值进行传递。
Now, what about the case where we don't want to modify the value? There's no need to pass by reference or pointer, right? Wrong! Now, in the case of fundemental types it probably doesn't make a huge deal of difference since these are unlikely to be any more complicated that a pointer or reference type; howver, in the case of fully blown class objects, such as string, vector, map, set or any other (including your own class objects) it can make a huge difference. You see, passing by value can be very expensive both in terms of memory usage and performance (time / space complexity)
现在,如果我们不想修改值呢? 不需要通过引用或指针传递,对吗? 错误! 现在,在基本类型的情况下,它可能并没有太大的区别,因为它们不太可能比指针或引用类型复杂。 但是,对于完全爆炸的类对象,例如string , vector , map , set或任何其他类对象(包括您自己的类对象),它可能会产生巨大的影响。 您会发现,就内存使用量和性能( 时间/空间复杂度 )而言,按值传递可能会非常昂贵
Classes have copy constructors, which are defined to facilitate the correct copying semantics for a class. Now in C++ there are two types of copy, shallow and deep. A shallow copy is where all the values of the class are copied but pointers are not followed. A deep copy is where pointers are followed and all the objects that they point to are also copied, thus creating a copy of all the "deep" objects, too. Any class that contains references to other objects should (unless there is a very good reason not to) provide both an assignment and copy constructor such that the class is always copied deeply.
类具有复制构造函数 ,该复制构造函数被定义为促进类的正确复制语义。 现在,在C ++中,有两种类型的复制, shallow和deep 。 浅表副本是复制类的所有值但不跟随指针的地方。 深度复制是指跟随指针的地方,指针所指向的所有对象也将被复制,从而也创建了所有“深度”对象的副本。 任何包含对其他对象的引用的类都应该(除非有很好的理由不这样做)同时提供分配和复制构造函数,以使该类始终被深深复制。
Consider the std::vector class. This class contains an internal buffer of memory that is managed by the vector. In reality, we can assume that the vector contains a pointer that points to memory allocated on the heap. The vector class implements a copy constructor that will perform a deep copy on a vector object if a copy is taken. This is the only sane thing to do, otherwise we have two objects referencing the same memory and then we have issues of ownership. In other words, which of the vectors is now responsible for managing and freeing that memory and what happens to the other vector if that memory is released? Of course, it'll be left with a dangling pointer that is referencing invalid memory! Bad mojo for all!!!
考虑一下std :: vector类。 此类包含由向量管理的内部内存缓冲区。 实际上,我们可以假定向量包含一个指向堆上分配的内存的指针。 vector类实现了一个复制构造函数,如果进行了复制,则该复制构造函数将在vector对象上执行深层复制。 这是唯一明智的操作,否则我们有两个引用相同内存的对象,然后出现所有权问题。 换句话说,哪个向量现在负责管理和释放该内存,如果释放了该内存,另一个向量会发生什么? 当然,它会留下一个指向无效内存的悬空指针 ! 所有人的坏魔力!
Now, imagine we have a vector class that contains thousands of items. If we pass this object to a function by value the whole of the internal buffer will be copied. Not only is this really very inefficient in terms of the time it will take to allocate the memory and copy the values from the original vector to the copy it also increases memory usage greatly and, as a side effect, the risk of memory fragmentation. Imagine if this same vector is copied around again and again (maybe in a loop); it should be pretty clear just how inefficient this is.
现在,假设我们有一个包含数千个项目的向量类。 如果我们通过值将该对象传递给函数,则将复制整个内部缓冲区。 这不仅在分配内存和将值从原始向量复制到副本所需的时间上确实非常低效,而且还大大增加了内存使用量,并且副作用是内存碎片化的风险。 想象一下,是否一次又一次地(也许是循环地)复制了相同的向量; 应该很清楚这是多么低效。
The solution is to pass things around by const reference (or const pointer in C). The cost of passing things around by const reference is trivial and about as expensive as passing around an int value. Not only is it so much more efficient to pass objects in this way, but the semantics of your function become way clearer. Just looking at the function prototype tells us that the value being passed is never meant to be modified by this function. You are helping to enforce your objects interface contract.
解决方案是通过const引用(或C中的const指针)传递内容。 通过const引用传递事物的成本是微不足道的,并且与传递int值一样昂贵。 以这种方式传递对象不仅效率更高,而且函数的语义也变得更加清晰。 仅查看函数原型就可以告诉我们,传递的值绝不会被该函数修改。 您正在帮助执行对象接口合同 。
Let's see a trivial example.
让我们来看一个简单的例子。
#include <vector>
#include <chrono>
#include <iostream>
void foo(std::vector<int> byValue)
{
// do nothing
}
void bar(std::vector<int> const & byRef)
{
// do nothing
}
int main()
{
auto && v = std::vector<int>(0x7FFFFFF);
auto && x1 = std::chrono::steady_clock::now();
foo(v);
auto && x2 = std::chrono::steady_clock::now();
bar(v);
auto && x3 = std::chrono::steady_clock::now();
auto && d1 = std::chrono::duration_cast<std::chrono::nanoseconds>(x2 - x1);
auto && d2 = std::chrono::duration_cast<std::chrono::nanoseconds>(x3 - x2);
std::cout
<< "Time to call foo: " << d1.count() << std::endl
<< "Time to call bar: " << d2.count() << std::endl;
}
When running this on my Windows 7 laptop, build with Visual Studio 2013 and executed in as a Release build the call by value takes approximately 1 second whilst the call by reference takes less than a nanosecond. That makes the pass by value a billion times slower! Of course, this is a contrived example and on different machines with different compilers YMMV, but hopefully it serves to demonstrate just how slow passing by value can, when compared to passing by reference!
在我的Windows 7笔记本电脑上运行此程序时,使用Visual Studio 2013进行构建并作为Release版本执行,按值调用大约需要1秒钟,而按引用调用则需要不到1纳秒。 这使传值速度降低了十亿倍! 当然,这是一个人为的示例,并且在具有不同编译器YMMV的不同机器上,但希望它能证明与按引用传递相比,按值传递可以有多慢!
In the case of passing by value the cost in terms of both time and space complexity is O(N), where N is the number of bytes to be copied. Passing by reference will cost O(1), which is a significant improvement. Okay, the pedants amongst you may wish to argue that even for a reference it's O(N), because a reference is composed of bytes. True, but the big (massive) difference that the size of a reference is always constant and will be in the order of a few bytes (4 on a 32 bit machine, 8 on a 64 bit machine) and not hundreds, thousands, millions or even billion in the case of non-fundamental objects.
在按值传递的情况下, 时间和空间复杂度方面的成本均为O(N),其中N是要复制的字节数。 通过引用传递将花费O(1),这是一个重大改进。 好的,你们中间的学徒可能会争辩说,即使对于引用,它也是O(N),因为引用是由字节组成的。 的确如此,但是最大的(巨大的)区别是引用的大小始终是恒定的,并且大约是几个字节(32位计算机上为4,64位计算机上为8),而不是数百,数千,数百万对于非基本物体,甚至是十亿。
Note: that some compilers may optimize out the calls to the functions foo and bar due to the fact they don't do anything. This is most likely to happen if you have aggressive optimisation enabled on your compiler. You can either disable this or add some code to these functions to make use of the passed references. Whilst disabling optimisation may skew the results in an absolute sense, the relative comparison should still hold up because what we're truly interested in here is the asymtoptic variance (Big O) rather than wall clock time!
注意:有些编译器可能会不执行任何操作,因此可能会优化对foo和bar函数的调用。 如果在编译器上启用了积极的优化,则最有可能发生这种情况。 您可以禁用此功能,也可以向这些功能添加一些代码以利用传递的引用。 尽管禁用优化可能会在绝对意义上扭曲结果,但相对比较仍然应该成立,因为我们真正感兴趣的是渐近方差(Big O),而不是挂钟时间!
Further reading:
进一步阅读:
- Passing arguments by value 通过值传递参数
- Passing arguments by reference 通过引用传递参数
- Passing arguments by address (pointer) 通过地址 (指针)传递参数
翻译自: https://www.experts-exchange.com/articles/18343/C-Passing-by-value-vs-passing-by-reference.html
c语言值传递和引用传递