Truyền mảng theo tham chiếu C++

Khi thiết kế một hàm chấp nhận một mảng C trong C++, cá nhân tôi sẽ chọn chuyển qua con trỏ tới một mảng hầu như mọi lúc. Gần đây, tôi nhận ra rằng việc chuyển tham chiếu đến một mảng thực sự có lợi hơn trong một số trường hợp sử dụng vì chúng ta có thể sử dụng trình vòng lặp C++ cho mảng C và tận dụng lợi thế của nhiều thuật toán và vùng chứa STL

Trong bài đăng trên blog này, tôi muốn thảo luận nhanh về việc chuyển một mảng C đến một hàm thông qua con trỏ và tham chiếu

Mảng truyền C++ theo tham chiếu VS theo con trỏ

Lưu ý chính khi truyền một mảng cho một hàm là chúng ta có thể đã truyền một con trỏ ngay cả khi chúng ta nghĩ rằng mình đã truyền một tham chiếu. Các mảng như T arr[N]T arr[] trong chữ ký hàm đều phân rã thành con trỏ. Cách chính xác để chuyển qua tham chiếu là sử dụng T (&arr)[N]

Truyền theo con trỏ và truyền theo tham chiếu đến mảng đôi khi không tạo ra quá nhiều khác biệt trong thực tế. Nhưng nếu đi qua con trỏ, chúng tôi sẽ mất lợi ích của việc sử dụng các trình vòng lặp sizeof và C++ trong một số trường hợp sử dụng

pass_array. cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include 
#include

// C style interface
template <typename T>
void print_array_by_pointer(T* arr, size_t N)
// This is equivalent to
// template
// void print_array_by_pointer(T arr[], size_t N)
{
// arr is a pointer.
std::cout << "Size of pointer: " << sizeof(arr) << std::endl;
std::cout << "Elements in array: " << std::endl;
// This would not work.
// std::copy(std::cbegin(arr), std::cend(arr),
// std::ostream_iterator(std::cout, " "));
// Cannot get an iterator from a pointer.
for (int i{0}; i < N; ++i)
{
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}

template <typename T, int N>
void print_array_by_pointer(T arr[N])
// This is equivalent to
// template
// void print_array_by_pointer(T* arr)
// or
// template
// void print_array_by_pointer(T arr[])
{
// arr decays to a pointer.
std::cout << "Size of pointer: " << sizeof(arr) << std::endl;
std::cout << "Elements in array: " << std::endl;
// This would not work.
// std::copy(std::cbegin(arr), std::cend(arr),
// std::ostream_iterator(std::cout, " "));
// Cannot get an iterator from a pointer.
for (int i{0}; i < N; ++i)
{
std::cout << arr[i] << " ";
}
std::cout << std::endl;
}

template <typename T, int N>
void print_array_by_reference(T (&arr)[N])
{
// arr is still an array of size N.
std::cout << "Size of array: " << sizeof(arr) << std::endl;
std::cout << "Elements in array: " << std::endl;
std::copy(std::cbegin(arr), std::cend(arr),
std::ostream_iterator(std::cout, " "));
std::cout << std::endl;
}

template <typename T, int N>
void print_array_by_pointer_to_array(T (*arr)[N])
{
// arr is a pointer to an array of size N.
std::cout << "Size of array: " << sizeof(*arr) << std::endl;
std::cout << "Elements in array: " << std::endl;
std::copy(std::cbegin(*arr), std::cend(*arr),
std::ostream_iterator(std::cout, " "));
std::cout << std::endl;
}

int main()
{
int arr[5] = {0, 1, 2, 3, 4};
print_array_by_pointer(arr, sizeof(arr) / sizeof(arr[0]));
print_array_by_pointerdecltype(arr[0])>::type,
sizeof(arr) / sizeof(arr[0])>(arr);
print_array_by_reference(arr);
// Same interface as print_array_by_pointer.
// print_array_by_reference::type,
// sizeof(arr) / sizeof(arr[0])>(arr);
print_array_by_pointer_to_array(&arr);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ g++ pass_array.cpp -o pass_array -std=c++14
pass_array.cpp: In instantiation of ‘void print_array_by_pointer(T*) [with T = int; int N = 5]’:
pass_array.cpp:69:57: required from here
pass_array.cpp:29:48: warning: ‘sizeof’ on array function parameter ‘arr’ will return size of ‘int*’ [-Wsizeof-array-argument]
29 | std::cout << "Size of pointer: " << sizeof(arr) << std::endl;
| ~^~~~
pass_array.cpp:23:31: note: declared here
23 | void print_array_by_pointer(T arr[N])
| ~~^~~~~~
$ ./pass_array
Size of pointer: 8
Elements in array:
0 1 2 3 4
Size of pointer: 8
Elements in array:
0 1 2 3 4
Size of array: 20
Elements in array:
0 1 2 3 4
Size of array: 20
Elements in array:
0 1 2 3 4

kết luận

Trong C, không có chuyển qua tham chiếu. Trong C++, cho phép chuyển qua tham chiếu. Mặc dù không có gì đúng hay sai, nhưng có lẽ cách tốt nhất trong C++ là thiết kế giao diện hàm tham chiếu đến mảng. Nó không phải là vấn đề lớn nếu ban đầu giao diện chức năng có một con trỏ bị phân rã, chúng ta có thể chuyển đổi giao diện chức năng sau mà không phải thay đổi các cài đặt gọi hàm

Bạn có thể chuyển một mảng bằng tham chiếu trong C không?

Việc truyền mảng cho hàm trong C/C++ được truyền theo tham chiếu . Mặc dù chúng ta không tạo một biến tham chiếu, trình biên dịch sẽ chuyển con trỏ tới mảng, làm cho mảng ban đầu có sẵn để sử dụng hàm được gọi. Do đó, nếu hàm sửa đổi mảng, nó sẽ được phản ánh trở lại mảng ban đầu.

Mảng có được truyền theo tham chiếu không?

Mảng là Đối tượng, vâng, nhưng không có gì trong Java được chuyển qua tham chiếu. Tất cả các tham số đi qua là theo giá trị. Trong trường hợp của một Đối tượng, những gì được thông qua là một tham chiếu đến Đối tượng (i. e. một con trỏ), theo giá trị

Làm cách nào để chuyển một mảng theo giá trị trong C?

Điều này có thể được thực hiện bằng cách gói mảng trong một cấu trúc và tạo một biến kiểu của cấu trúc đó và gán giá trị cho mảng đó . Sau đó, chuyển biến cho một số chức năng khác và sửa đổi nó theo yêu cầu.

Làm cách nào để chuyển một mảng theo địa chỉ trong C?

Để nói rõ điều này. Không thể "chuyển một mảng" trong C . Tất cả những gì người ta có thể làm là chuyển địa chỉ của phần tử đầu tiên của nó bằng cách thực hiện me(x) (trong đó mảng x phân rã địa chỉ của phần tử đầu tiên của nó) hoặc chuyển địa chỉ của mảng bằng cách thực hiện me(&x).