Truyền địa chỉ cho hàm (Passing arguments by address)

Khóa học lập trình C++ căn bản

5.0 (5 đánh giá)
Tạo bởi Kteam Cập nhật lần cuối 12:08 28-08-2020 27.706 lượt xem 3 bình luận
Tác giả/Dịch giả: Kteam
Học nhanh

Danh sách bài học

Truyền địa chỉ cho hàm (Passing arguments by address)

Dẫn nhập

Ở bài học trước, mình đã chia sẻ cho các bạn một cách để sử dụng mảng động mà không cần thao tác quá phức tạp bằng con trỏ, đó là LỚP DỰNG SẴN std::Vector.

Hôm nay, sau khi đã nắm được khái niệm con trỏ trong những bài học trước, chúng ta sẽ cùng quay lại phần hàm trong C++, cụ thể là vấn đề Truyền địa chỉ cho hàm (Passing arguments by address).


Nội dung

Để đọc hiểu bài này tốt nhất các bạn nên có kiến thức cơ bản về:

Trong bài ta sẽ cùng tìm hiểu các vấn đề:

  • Truyền địa chỉ cho hàm (Passing arguments by address)
  • Truyền địa chỉ cho hàm bằng giá trị
  • Truyền địa chỉ cho hàm bằng tham chiếu
  • Tổng kết về phương pháp truyền địa chỉ cho hàm

Truyền địa chỉ cho hàm (Passing arguments by address)

Hiện tại, mình đã giới thiệu 2 cách truyền đối số cho một hàm trong C++ là: truyền giá trị (Call by value) truyền tham chiếu (Call by reference). Trong bài học này, mình sẽ giới thiệu về phương pháp truyền địa chỉ cho hàm.

Truyền địa chỉ cho hàm là truyền địa chỉ của biến đối số chứ không phải giá trị biến đối số. Vì đối số là một địa chỉ, tham số hàm phải là một con trỏ. Sau đó, hàm có thể truy cập hoặc thay đổi giá trị được trỏ đến.

#include <iostream>
using namespace std;

void foo(int *ptr)
{
	*ptr = 20;
}

int main()
{
	int value = 10;

	cout << "value = " << value << '\n';
	foo(&value);
	cout << "value = " << value << '\n';

	system("pause");
	return 0;
}

Output:

Truyền địa chỉ cho hàm (Passing arguments by address)

Trong ví dụ trên, hàm foo() đã thay đổi giá trị của đối số (biến value) thông qua tham số hàm là con trỏ ptr (địa chỉ biến value).

Truyền địa chỉ cho hàm thường được sử dụng với mảng. Ví dụ:

#include <iostream>
using namespace std;

void printArray(const int *array, int length)
{
	// kiểm tra con trỏ null
	if (!array)
		return;

	for (int index = 0; index < length; ++index)
		cout << array[index] << ' ';
}

int main()
{
	int array[6] = { 6, 5, 4, 3, 2, 1 };
	printArray(array, 6);

	system("pause");
	return 0;
}

Output:

Truyền địa chỉ cho hàm (Passing arguments by address)

Như ví dụ trên, bạn có thể sử dụng tham số hằng con trỏ trong trường hợp hàm không có mục đích thay đổi giá trị đối số.


Truyền địa chỉ cho hàm bằng giá trị

Khi truyền một con trỏ tới một hàm theo địa chỉ, giá trị của con trỏ (địa chỉ mà nó trỏ tới) sẽ được sao chép từ đối số sang tham số hàm.

Nói cách khác, nó là phương pháp truyền giá trị. Nếu bạn thay đổi giá trị của tham số hàm, bạn chỉ thay đổi một bản sao. Do đó, đối số con trỏ ban đầu sẽ không bị thay đổi.

#include <iostream>
using namespace std;

// tempPtr là bản sao của ptr
void setToNull(int *tempPtr)
{
	// trỏ tempPtr đến một vị trí khác, không phải thay đổi giá trị tempPtr trỏ tới
	tempPtr = nullptr;
}

int main()
{
	int value = 10;
	int *ptr = &value;

	cout << *ptr << "\n"; // 10

	// tempPtr là bản sao của ptr
	setToNull(ptr);

	if (ptr)
		cout << *ptr << "\n"; // 10
	else
		cout << " ptr is null";

	system("pause");
	return 0;
}

Output:

Truyền địa chỉ cho hàm (Passing arguments by address)

Chú ý:

  • Khi truyền một đối số theo địa chỉ, tham số hàm nhận được một bản sao của địa chỉ từ đối số. Tại thời điểm này, tham số hàm và đối số đều trỏ đến cùng một giá trị.
  • Nếu thay đổi giá trị được tham số hàm trỏ đến, điều đó sẽ thay đổi giá trị mà đối số đang trỏ đến, vì cả tham số hàm và đối số đều trỏ đến cùng một giá trị.
  • Nếu tham số hàm được gán một địa chỉ khác, điều đó sẽ không ảnh hưởng đến đối số, vì tham số hàm chỉ là bản sao của đối số.

Xem ví dụ bên dưới để hiểu rõ hơn:

#include <iostream>
using namespace std;

void setValue(int *tempPtr)
{
	*tempPtr = 20; // thay đổi giá trị tempPtr và ptr trỏ tới
}

int main()
{
	int value = 10;
	int *ptr = &value;

	cout << *ptr << "\n"; // 10

	// tempPtr là bản sao của ptr
	setValue(ptr);

	if (ptr)
		cout << *ptr << "\n"; // 20
	else
		cout << " ptr is null";

	system("pause");
	return 0;
}

Output:

Truyền địa chỉ cho hàm (Passing arguments by address)


Truyền địa chỉ cho hàm bằng tham chiếu

Chúng ta có thể truyền địa chỉ cho hàm bằng tham chiếu để thay đổi địa chỉ mà đối số trỏ đến từ bên trong hàm.

#include <iostream>
using namespace std;

// tempPtr tham chiếu đến con trỏ ptr, mọi thay đổi trên tempPtr sẽ làm thay đổi ptr
void setToNull(int *&tempPtr)
{
	tempPtr = nullptr;
}

int main()
{
	int value = 10;
	int *ptr = &value;

	cout << *ptr << "\n"; // 10

	// tempPtr là tham chiếu đến ptr
	setToNull(ptr);

	if (ptr)
		cout << *ptr << "\n";
	else
		cout << "ptr is null" << "\n";

	system("pause");
	return 0;
}

Output:

Truyền địa chỉ cho hàm (Passing arguments by address)


Tổng kết về phương pháp truyền địa chỉ cho hàm

Ưu điểm:

  • Hàm có thể thay đổi giá trị của các đối số. Ngược lại, bạn có thể sử dụng tham chiếu hằng (const reference) nếu không muốn hàm thay đổi giá trị đối số.
  • Không mất thời gian và bộ nhớ để sao chép giá trị của đối số vào tham số của hàm, ngay cả khi được sử dụng với các dữ liệu có cấu trúc hoặc lớp.
  • Hàm có thể trả về nhiều giá trị thông qua tham chiếu.

Nhược điểm:

  • Đối số phải là biến thông thường (có địa chỉ trong bộ nhớ), vì hằng hay biểu thức không có địa chỉ.
  • Cần kiểm tra null trước khi sử dụng tham số.

Khi nào nên sử dụng:

  • Khi đối số là kiểu mảng.
  • Khi có nhu cầu thay đổi giá trị của đối số sau khi thực hiện hàm.

Khi nào không nên sử dụng:

  • Khi đối số là kiểu cấu trúc (struct) hoặc lớp (class) (sử dụng truyền tham chiếu).
  • Khi đối số có kiểu dữ liệu cơ bản (sử dụng truyền giá trị).

Chú ý: Vì truyền tham chiếu thường an toàn hơn so với truyền địa chỉ, nên truyền tham chiếu thường được ưu tiên trong hầu hết các trường hợp.


Kết luận

Qua bài học này, bạn đã nắm được phương pháp Truyền địa chỉ cho hàm (Passing arguments by address). Và những ưu điểm, nhược điểm, khi nào nên và không nên sử dụng của phương pháp trên.

Trong bài tiếp theo, chúng ta sẽ cùng tìm hiểu CÁC KIỂU TRẢ VỀ CỦA HÀM (Returning values by value, reference, and address) trong C++.

Cảm ơn các bạn đã theo dõi bài viết. Hãy để lại bình luận hoặc góp ý của mình để phát triển bài viết tốt hơn. Đừng quên “Luyện tập – Thử thách – Không ngại khó”.


Tải xuống

Tài liệu

Nhằm phục vụ mục đích học tập Offline của cộng đồng, Kteam hỗ trợ tính năng lưu trữ nội dung bài học Truyền địa chỉ cho hàm (Passing arguments by address) dưới dạng file PDF trong link bên dưới.

Ngoài ra, bạn cũng có thể tìm thấy các tài liệu được đóng góp từ cộng đồng ở mục TÀI LIỆU trên thư viện Howkteam.com

Đừng quên likeshare để ủng hộ Kteam và tác giả nhé!


Thảo luận

Nếu bạn có bất kỳ khó khăn hay thắc mắc gì về khóa học, đừng ngần ngại đặt câu hỏi trong phần bên dưới hoặc trong mục HỎI & ĐÁP trên thư viện Howkteam.com để nhận được sự hỗ trợ từ cộng đồng.

Nội dung bài viết

Tác giả/Dịch giả

Khóa học

Khóa học lập trình C++ căn bản

Hiện nay, C++ đã là cái tên rất quen thuộc trong ngành lập trình. Mặc dù C++ là ngôn ngữ lập trình đã ra đời khá lâu, nhưng không phải ai cũng có cơ hội để tìm hiểu về nó.

Vì vậy, Kteam đã xây dựng lên khóa học LẬP TRÌNH C++ CĂN BẢN để cung cấp một lượng kiến thức về ngôn ngữ C++ nói riêng, và các khái niệm khác trong lập trình nói chung.

Nội dung khóa học sẽ được phân tách một cách chi tiết, nhằm giúp các bạn dễ hiểu và thực hành được ngay. Serial dành cho những bạn chưa có bất kỳ kiến thức gì về lập trình, hoặc những bạn mất căn bản muốn lấy lại kiến thức nền tảng lập trình, cụ thể là C++.

Đánh giá

win1702 đã đánh giá 16:00 18-07-2024

Tien Dung đã đánh giá 09:02 21-01-2022

Nhat Cuong đã đánh giá 16:12 15-10-2021

Lê Hoàng Nguyên đã đánh giá 10:35 12-08-2021

PATeas đã đánh giá 16:21 26-07-2021

Ổn áp

Bình luận

Để bình luận, bạn cần đăng nhập bằng tài khoản Howkteam.

Đăng nhập
giang.nam1401 đã bình luận 19:40 11-05-2022

e có học qua khóa của anh, nhưng khi bắt tay vào đọc code mẫu trên mạng, e vẫn chưa hiểu cách hoạt động của những hàm này là như thế nào! 

void *__cdecl LoadPluginSo(int a1)
{
  void *v2; 
  void *handle; 
  int (__cdecl *v4)(int); 
  char *name; 

  handle = dlopen("/home/bak/bin/Plugin.so", 2);
  if ( !handle )
    return 0;
  v4 = (int (__cdecl *)(int))dlsym(handle, "mFunction");
  if ( !v4 )
    return 0;
  name = (char *)v4(a1);
  if ( name )
    v2 = dlsym(handle, name);
  else
    v2 = 0;
  return v2;
}

và có 1 hàm khác ở cùng file sử dụng hàm trên

int (__cdecl *__cdecl GetAttrIndex(int a1, int a2, int a3))
{
  int (__cdecl *result)(int, int, int); // eax

  result = (int (__cdecl *)(int, int, int))LoadPluginSo(30);
  if ( result )
    result = (int (__cdecl *)(int, int, int))result(a1, a2, a3);
  return result;
}

Vậy trong linux file /home/bak/bin/Plugin.so  này được viết theo cấu trúc như thế nào để nhận được các arguments  và xử lý ạ ? cảm ơn a đã xem

Dưa hấu rất ngon đã bình luận 16:04 19-10-2021

đoạn 3:00 có tiếng mèo của admin cute vãi 

 

duydee123 đã bình luận 10:03 05-06-2019

hay

Không có video.