1001 Tips: Con trỏ và hàm (Pointer & Function) trong C++

Lập Trình
1001 Tips: Con trỏ và hàm (Pointer & Function) trong C++
Con trỏ và tham số của hàm Chúng ta đã tìm hiểu về 2 kiểu tham số của hàm: Hàm có tham số nhận giá trị: giá trị truyền vào hàm có thể là giá trị của biến, một hằng số hoặc một biểu thức toán học… Hàm có tham số kiểu tham chiếu: giá trị truyền vào cho hàm là tên biến, và tham số của hàm sẽ tham chiếu trực tiếp đến vùng nhớ của biến đó. Chúng ta còn có thêm một kiểu truyền dữ liệu vào cho hàm nữa, đó là truyền địa chỉ vào hàm ( Pass arguments by address ). Do đó, kiểu tham số của hàm có thể nhận giá trị là địa chỉ phải là con trỏ. void foo ( int *iPtr) { cout << "Int value at " << iPtr << " is " << *iPtr << endl ; } int main () { int iValue = 10 ; foo(&iValue); system( "pause" ); return 0 ; } Trong đoạn chương trình trên, sau khi truyền địa chỉ của biến iValue vào hàm foo , tham số iPtr bây giờ sẽ giữ địa chỉ của biến iValue , và chúng ta có thể sử dụng toán tử dereference cho con trỏ iPtr . Kết quả in ra màn hình trên máy tính của mình là: Int value at 0xBFBA144C is 10 Nếu vùng nhớ tại địa chỉ được sử dụng làm đối số cho hàm không phải là hằng, chúng ta có thể thay đổi giá trị của vùng nhớ đó ngay bên trong hàm thông qua toán tử dereference : void changeValue ( int *iPtr) { *iPtr = 10 ; } int main () { int iValue = 5 ; cout << "Value = " << iValue << endl ; changeValue(&iValue);...

Con trỏ và tham số của hàm

Chúng ta đã tìm hiểu về 2 kiểu tham số của hàm:

  • Hàm có tham số nhận giá trị: giá trị truyền vào hàm có thể là giá trị của biến, một hằng số hoặc một biểu thức toán học…
  • Hàm có tham số kiểu tham chiếu: giá trị truyền vào cho hàm là tên biến, và tham số của hàm sẽ tham chiếu trực tiếp đến vùng nhớ của biến đó.

Chúng ta còn có thêm một kiểu truyền dữ liệu vào cho hàm nữa, đó là truyền địa chỉ vào hàm (Pass arguments by address). Do đó, kiểu tham số của hàm có thể nhận giá trị là địa chỉ phải là con trỏ.

void foo(int *iPtr)
{
	cout << "Int value at " << iPtr << " is " << *iPtr << endl;
}

int main()	
{
	int iValue = 10;
	foo(&iValue);
	
	system("pause");
	return 0;
}

Trong đoạn chương trình trên, sau khi truyền địa chỉ của biến iValue vào hàm foo, tham số iPtr bây giờ sẽ giữ địa chỉ của biến iValue, và chúng ta có thể sử dụng toán tử dereference cho con trỏ iPtr. Kết quả in ra màn hình trên máy tính của mình là:

Int value at 0xBFBA144C is 10

Nếu vùng nhớ tại địa chỉ được sử dụng làm đối số cho hàm không phải là hằng, chúng ta có thể thay đổi giá trị của vùng nhớ đó ngay bên trong hàm thông qua toán tử dereference:

void changeValue(int *iPtr)
{
	*iPtr = 10;
}

int main()
{
	int iValue = 5;
	cout << "Value = " << iValue << endl;
	
	changeValue(&iValue);
	cout << "Value = " << iValue << endl;
	
	system("pause");
	return 0;
}

Kết quả in ra:

Value = 5
Value = 10

Như vậy, chúng ta có thể hoán vị giá trị của 2 số nguyên thông qua hàm như sau:

void swapIntValue(int *ptr1, int *ptr2)
{
	int temp = *ptr1;
	*ptr1 = *ptr2;
	*ptr2 = temp;
}

int main()
{
	int value1 = 2;
	int value2 = 5;
	
	cout << "Before swap: " << value1 << " " << value2 << endl;
	swapIntValue(&value1, &value2);
	cout << "After swap : " << value1 << " " << value2 << endl;
	
	system("pause");
	return 0;
}

Kết quả:

Before swap: 2 5
After swap : 5 2

Như các bạn thấy, con trỏ khi làm tham số cho hàm cũng có khả năng thay đổi giá trị của vùng nhớ không phải hằng như con trỏ thông thường thông qua toán tử dereference.

Chúng ta còn có thể truyền địa chỉ của mảng một chiều vào cho tham số kiểu con trỏ của hàm. Ví dụ:

void printArray(int *arr, int length)
{
	for (int i = 0; i < length; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

int main()
{
	int iArr[] = { 3, 2, 5, 1, 7, 10, 32 };
	printArray(iArr, sizeof(iArr) / sizeof(int));
	
	system("pause");
	return 0;
}

Lưu ý, chúng ta không thể biết chính xác kích thước của mảng một chiều thông qua con trỏ, do đó, chúng ta cần tính toán trước kích thước của mảng trước khi truyền vào cho hàm.

Sử dụng Pointer to const để làm tham số cho hàm

Như các bạn đã biết, Pointer to const là loại con trỏ chỉ có chức năng để đọc (read-only). Do đó, sử dụng Pointer to const làm tham số cho hàm sẽ đảm bảo rằng giá trị tại vùng nhớ được truyền vào cho hàm sẽ không bị thay đổi.

void printArray(const int *arr, int length)
{
	for (int i = 0; i < length; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

int main()
{
	int arr[] = {};
	int length = sizeof(arr) / sizeof(int);
	
	printArray(arr, length);
	
	system("pause");
	return 0;
}

Lúc này, chúng ta có thể đảm bảo rằng giá trị của các phần tử trong mảng arr sẽ không bị thay đổi bởi hàm printArray.

Tham số của hàm là tham chiếu vào con trỏ

Khi chúng ta truyền đối số cho hàm là một địa chỉ, cái địa chỉ này cũng chỉ là bản copy của địa chỉ ban đầu. Về bản chất, truyền địa chỉ vào hàm là truyền đối số là giá trị (pass by value). Địa chỉ của đối số sẽ được copy và gán lại cho tham số con trỏ của hàm. Nếu bên trong hàm có câu lệnh thay đổi địa chỉ được truyền vào, chúng chỉ thay đổi bản sao của địa chỉ gốc. Để dễ hình dung hơn, chúng ta xem xét ví dụ sau:

void setToNull(int *ptr)	
{
	ptr = NULL; // (4)
}  // (5)

int main()
{
	int value = 5;
	int *pValue = &value; // (1)
	
	cout << "pValue point to " << pValue << endl; // (2)
	setToNull(pValue); // (3)
	cout << "pValue point to " << pValue << endl; // (6)
	
	system("pause");
	return 0;
}

Có 6 bước để nói về đoạn chương trình trên:

(1) Gán địa chỉ của biến value cho con trỏ pValue.
(2) In ra địa chỉ mà con trỏ pValue đang nắm giữ.
(3) Truyền giá trị của con trỏ đang nắm giữ cho hàm setToNull
(4) Sau khi con trỏ ptr trong hàm setToNull nhận được giá trị đầu vào, con trỏ ptr này được gán lại giá trị NULL.
(5) Ra khỏi phạm vi của hàm setToNull, con trỏ ptr bị hủy.
(6) In ra lại giá trị của con trỏ pValue. Lúc này, chúng ta có thể thấy giá trị của pValue không hề thay đổi, nó vẫn còn trỏ đến địa chỉ của biến value.

Như vậy, giá trị địa chỉ được truyền vào hàm được nắm giữ bởi tham số con trỏ của hàm, từ đó chúng ta có thể sử dụng toán tử dereference để thao tác với vùng nhớ tại địa chỉ đó. Chúng ta cũng có thể cho tham số của hàm trỏ đến địa chỉ khác, nhưng không ảnh hưởng gì đến con trỏ gốc.

0.png?raw=true959×551

Trong một số trường hợp cụ thể, chúng ta muốn thay đổi địa chỉ của con trỏ đối số đang trỏ đến, chúng ta có thể sử dụng tham chiếu cho con trỏ đối số. Xét đoạn chương trình bên dưới:

void setToNull(int *&ptr)	
{
	ptr = NULL;
}

int main()
{
	int value = 5;
	int *pValue = &value;
	
	cout << "pValue point to " << pValue << endl;
	setToNull(pValue);
	if(pValue == NULL)
		cout << "pValue point to NULL" << endl;
	else
		cout << "pValue point to " << pValue << endl;
	
	return 0;
}

Kết quả của đoạn chương trình này cho thấy con trỏ pValue sau khi truyền vào hàm setToNull đã được gán giá trị NULL. Do tham số con trỏ của hàm setToNull là một tham chiếu kiểu (int *), nó sẽ tham chiếu đến đối số được truyền vào, trong trường hợp này, tham số tham chiếu con trỏ ptr có cùng địa chỉ với pValue, việc thay đổi giá trị mà ptr nắm giữ cũng làm thay đổi giá trị của pValue.

Con trỏ và kiểu trả về của hàm

Chúng ta đã cùng tìm hiểu 2 kiểu giá trị trả về của hàm có kiểu trả về:

  • Hàm trả về giá trị.
  • Hàm trả về tham chiếu.

Bây giờ, chúng ta sẽ cùng tìm hiểu một số vấn đề về kiểu giá trị trả về của hàm là địa chỉ (return by address).

Khi nói về việc trả về địa chỉ từ hàm, chúng ta hiểu rằng đó là địa chỉ của những biến hoạt động bên trong hàm. Địa chỉ này sẽ được trả về cho lời gọi hàm, và địa chỉ này thường được tiếp tục sử dụng bằng cách gán nó lại cho 1 con trỏ. Do đó, kiểu trả về của hàm cũng phải là kiểu con trỏ.

Ví dụ:

int * createAnInteger(int value = 0)
{
	int myInt = value;
	return &myInt;
}

int main()
{
	int *pInt = createAnInteger(10);
	cout << *pInt << endl;
	
	return 0;
}

Sau khi nhìn vào kết quả, chúng ta thấy có vẻ chương trình đã cho ra kết quả như mong muốn:

10

Nhưng thực chất, đoạn chương trình trên đã gây ra lỗi nghiêm trọng. Lý do là biến myInt được khai báo bên trong hàm là biến cục bộ, được cấp phát bằng kỹ thuật Automatic memory allocation, và vùng nhớ được cấp phát cho biến myInt được lưu trữ trên phân vùng Stack của bộ nhớ ảo. Do đó, ngay sau khi ra khỏi hàm, vùng nhớ của biến myInt đã bị hệ điều hành thu hồi, nhưng địa chỉ của biến myInt trước đó đã được trả về cho lời gọi hàm, nên con trỏ pInt trong hàm main được gán một địa chỉ của một vùng nhớ không thuộc quyền quản lý của chương trình hiện hành nữa.

Như mình đã nói, nếu không may, một chương trình khác yêu cầu cấp phát vùng nhớ ngay tại địa chỉ của biến myInt lúc chưa bị hủy, nội dung bên trong vùng nhớ này sẽ bị các chương trình khác thay đổi, dẫn đến việc sử dụng toán tử dereference đến vùng nhớ đó không cho ra kết quả như ban đầu nữa. Các bạn có thể chạy đoạn chương trình sau để kiểm chứng:

int * createAnInteger(int value = 0)
{
	int myInt = value;
	return &myInt;
}

int main()
{
	int *pInt = createAnInteger(10);
	cout << "Print immediately:         " << *pInt << endl;
	_sleep(1000);
	cout << "After a fews seconds:   " << *pInt << endl;

	system("pause");
	return 0;
}

Kết quả trên máy tính của mình:

1.png?raw=true796×384

Như các bạn thấy, chỉ sau thời điểm vùng nhớ của biến myInt bị hủy mới có 1 giây mà đã có chương trình khác sử dụng vùng nhớ đó, làm cho giá trị in ra màn hình console không còn như ban đầu nữa. Và nếu không may hơn nữa, nếu chương trình khác sử dụng cơ chế đồng bộ của kỹ thuật multithreading lên vùng nhớ này, việc dereference vào vùng nhớ đó cũng có thể gây crash chương trình.

Nguyên nhân của những hệ quả mà mình vừa kể ra đều là do vùng nhớ được cấp phát trên Stack thông qua kỹ thuật Automatic memory allocation sẽ bị thu hồi tự động bởi hệ điều hành. Để giải quyết vấn đề này, chúng ta cần sử dụng phân vùng Heap để có thể tự quản lý thời điểm giải phóng vùng nhớ để trả lại cho hệ điều hành quản lý.

int * createAnInteger(int value = 0)
{
	return new int(value);
}

int main()
{
	int *pInt = createAnInteger(10);
	
	cout << "Print immediately:   " << *pInt << endl;
	_sleep(5000);
	cout << "After a few seconds: " << *pInt << endl;
	
	system("pause");
	return 0;
}

Kết quả lúc này đã được đảm bảo do chúng ta biết rằng vùng nhớ cấp phát trên Heap chỉ bị hệ điều hành thu hồi khi toàn bộ chương trình kết thúc.


Tổng kết

Trong bài học này, chúng ta đã biết cách truyền tham số là địa chỉ (hoặc con trỏ) vào cho hàm, và trả về địa chỉ cho lời gọi hàm. Bên cạnh đó, chúng ta cũng đã biết được một số vấn đề phát sinh khi sử dụng các kỹ thuật này. Vẫn còn nhiều vấn đề cần phải nói khi sử dụng con trỏ, chúng ta sẽ cùng tiếp tục tìm hiểu trong các bài học tiếp theo.

Bài tập cơ bản

Xét đoạn chương trình của ví dụ trên.

#include <iostream>
using namespace std;

int * createAnInteger(int value = 0)
{
	return new int(value);
}

int main()
{
	int *pInt = createAnInteger(10);
	
	cout << "Print immediately:   " << *pInt << endl;
	_sleep(5000);
	cout << "After a few seconds: " << *pInt << endl;
	
	system("pause");
	return 0;
}

Đoạn chương trình trên cho ra kết quả đúng, giá trị được in ra khi sử dụng toán tử dereference để truy xuất không bị thay đổi theo thời gian, nhưng nó lại phát sinh một vấn đề khác. Đó là vấn đề gì?

Station D via daynhauhoc.com

Lương của Developer (C++, PHP) tại Cốc Cốc có thể lên đến $2,000?”]

Bài viết liên quan

Thị trường EdTech Vietnam- Nhiều tiềm năng nhưng còn bị bỏ ngỏ tại Việt Nam

Thị trường EdTech Vietnam- Nhiều tiềm năng nhưng còn bị bỏ ngỏ tại Việt Nam

Lĩnh vực EdTech (ứng dụng công nghệ vào các sản phẩm giáo dục) trên toàn cầu hiện nay đã tương đối phong phú với nhiều tên tuổi lớn phân phối đều trên các hạng mục như Broad Online Learning Platforms (nền tảng cung cấp khóa học online đại chúng – tiêu biểu như Coursera, Udemy, KhanAcademy,…) Learning Management Systems (hệ thống quản lý lớp học – tiêu biểu như Schoology, Edmodo, ClassDojo,…) Next-Gen Study Tools (công cụ hỗ trợ học tập – tiểu biểu như Kahoot!, Lumosity, Curriculet,…) Tech Learning (đào tạo công nghệ – tiêu biểu như Udacity, Codecademy, PluralSight,…), Enterprise Learning (đào tạo trong doanh nghiệp – tiêu biểu như Edcast, ExecOnline, Grovo,..),… Hiện nay thị trường EdTech tại Việt Nam đã đón nhận khoảng đầu tư khoảng 55 triệu đô cho lĩnh vực này nhiều đơn vị nước ngoài đang quan tâm mạnh đến thị trường này ngày càng nhiều hơn. Là một trong những xu hướng phát triển tốt, và có doanh nghiệp đã hoạt động khá lâu trong ngành nêu tại infographic như Topica, nhưng EdTech vẫn chỉ đang trong giai đoạn sơ khai tại Việt Nam. Tại Việt Nam, hệ sinh thái EdTech trong nước vẫn còn rất non trẻ và thiếu vắng nhiều tên tuổi trong các hạng mục như Enterprise Learning (mới chỉ có MANA), School Administration (hệ thống quản lý trường học) hay Search (tìm kiếm, so sánh trường và khóa học),… Với chỉ dưới 5% số dân công sở có sử dụng một trong các dịch vụ giáo dục online, EdTech cho thấy vẫn còn một thị trường rộng lớn đang chờ được khai phá. *** Vừa qua Station D đã công bố Báo cáo Vietnam IT Landscape 2019 đem đến cái nhìn toàn cảnh về các ứng dụng công...

By stationd
Bộ cài đặt Laravel Installer đã hỗ trợ tích hợp Jetstream

Bộ cài đặt Laravel Installer đã hỗ trợ tích hợp Jetstream

Bài viết được sự cho phép của tác giả Chung Nguyễn Hôm nay, nhóm Laravel đã phát hành một phiên bản chính mới của “ laravel/installer ” bao gồm hỗ trợ khởi động nhanh các dự án Jetstream. Với phiên bản mới này khi bạn chạy laravel new project-name , bạn sẽ nhận được các tùy chọn Jetstream. Ví dụ: API Authentication trong Laravel-Vue SPA sử dụng Jwt-auth Cách sử dụng Laravel với Socket.IO laravel new foo --jet --dev Sau đó, nó sẽ hỏi bạn thích stack Jetstream nào hơn: Which Jetstream stack do you prefer? [0] Livewire [1] inertia > livewire Will your application use teams? (yes/no) [no]: ... Nếu bạn đã cài bộ Laravel Installer, để nâng cấp lên phiên bản mới bạn chạy lệnh: composer global update Một số trường hợp cập nhật bị thất bại, bạn hãy thử, gỡ đi và cài đặt lại nha composer global remove laravel/installer composer global require laravel/installer Bài viết gốc được đăng tải tại chungnguyen.xyz Có thể bạn quan tâm: Cài đặt Laravel Làm thế nào để chạy Sql Server Installation Center sau khi đã cài đặt xong Sql Server? Quản lý các Laravel route gọn hơn và dễ dàng hơn Xem thêm Tuyển dụng lập trình Laravel hấp dẫn trên Station D

By stationd
Principle thiết kế của các sản phẩm nổi tiếng

Principle thiết kế của các sản phẩm nổi tiếng

Tác giả: Lưu Bình An Phù hợp cho các bạn thiết kế nào ko muốn làm code dạo, design dạo nữa, bạn muốn cái gì đó cao hơn ở tầng khái niệm Nếu lập trình chúng ta có các nguyên tắc chung khi viết code như KISS , DRY , thì trong thiết kế cũng có những nguyên tắc chính khi làm việc. Những nguyên tắc này sẽ là kim chỉ nam, nếu có tranh cãi giữa các member trong team, thì cứ đè nguyên tắc này ra mà giải quyết (nghe hơi có mùi cứng nhắc, mình thì thích tùy cơ ứng biến hơn) Tìm các vị trí tuyển dụng designer lương cao cho bạn Nguyên tắc thiết kế của GOV.UK Đây là danh sách của trang GOV.UK Bắt đầu với thứ user cần Làm ít hơn Thiết kế với dữ liệu Làm mọi thứ thật dễ dàng Lặp. Rồi lặp lại lần nữa Dành cho tất cả mọi người Hiểu ngữ cảnh hiện tại Làm dịch vụ digital, không phải làm website Nhất quán, nhưng không hòa tan (phải có chất riêng với thằng khác) Cởi mở, mọi thứ tốt hơn Bao trừu tượng luôn các bạn, trang Gov.uk này cũng có câu tổng quát rất hay Thiết kế tốt là thiết kế có thể sử dụng. Phục vụ cho nhiều đối tượng sử dụng, dễ đọc nhất nhất có thể. Nếu phải từ bỏ đẹp tinh tế – thì cứ bỏ luôn . Chúng ta tạo sản phẩm cho nhu cầu sử dụng, không phải cho người hâm mộ . Chúng ta thiết kế để cả nước sử dụng, không phải những người đã từng sử dụng web. Những người cần dịch vụ của chúng ta nhất là những người đang cảm thấy khó sử dụng dịch...

By stationd
Applicant Tracking System là gì? ATS hoạt động ra sao

Applicant Tracking System là gì? ATS hoạt động ra sao

Công nghệ phát triển hướng đến giải quyết và cải tiến cho mỗi quy trình, hoạt động của doanh nghiệp cũng như đời sống. Đối với lĩnh vực tuyển dụng, sự xuất hiện của phần mềm ATS (Applicant Tracking System) mang đến nhiều thay đổi đáng kể, cả đối với nhà tuyển dụng và ứng viên. Vậy phần mềm ATS là gì? Chúng được sử dụng ra sao? Những thắc mắc về phần mềm ATS trong tuyển dụng sẽ được Station D giải đáp tại bài viết dưới đây. Hệ thống sàng lọc ứng viên ATS (Applicant Tracking System) Applicant Tracking System là gì? Applicant Tracking System (ATS) hay còn gọi là Hệ thống quản lý hồ sơ ứng viên là phần mềm quản lý quy trình tuyển dụng từ đầu đến cuối một cách tự động hóa. ATS được thiết kế để giúp nhà tuyển dụng tiết kiệm thời gian và chi phí trong việc thu thập, sắp xếp và sàng lọc hồ sơ các ứng viên. Các tính năng nổi bật của Applicant Tracking System Các tính năng nổi bật của Applicant Tracking System Applicant Tracking System (ATS) là một công cụ quan trọng giúp các công ty quản lý quy trình tuyển dụng hiệu quả hơn. Cùng chúng tôi điểm qua các tính năng nổi bật của ATS: Sàng lọc và quản lý hồ sơ ứng viên Khả năng tìm kiếm và sàng lọc ứng viên là một trong các tính năng nổi bật nhất của ATS. Với việc sử dụng từ khóa và tiêu chí cụ thể, hệ thống có thể nhanh chóng tìm kiếm và sàng lọc các hồ sơ phù hợp, tự động loại bỏ những ứng viên không đạt yêu cầu. Điều này giúp nhà tuyển dụng tập trung vào những ứng viên...

By stationd
Hiểu về trình duyệt – How browsers work

Hiểu về trình duyệt – How browsers work

Bài viết được sự cho phép của vntesters.com Khi nhìn từ bên ngoài, trình duyệt web giống như một ứng dụng hiển thị những thông tin và tài nguyên từ server lên màn hình người sử dụng, nhưng để làm được công việc hiển thị đó đòi hỏi trình duyệt phải xử lý rất nhiều thông tin và nhiều tầng phía bên dưới. Việc chúng ta (Developers, Testers) tìm hiểu càng sâu tầng bên dưới để nắm được nguyên tắc hoạt động và xử lý của trình duyệt sẽ rất hữu ích trong công việc viết code, sử dụng các tài nguyên cũng như kiểm thử ứng dụng của mình. Cách để npm packages chạy trong browser Câu hỏi phỏng vấn mẹo về React: Component hay element được render trong browser? Khi hiểu được cách thức hoạt động của trình duyệt chúng ta có thể trả lời được rất nhiều câu hỏi như: Tại sao cùng một trang web lại hiển thị khác nhau trên hai trình duyệt? Tại sao chức năng này đang chạy tốt trên trình duyệt Firefox nhưng qua trình duyệt khác lại bị lỗi? Làm sao để trang web hiển thị nội dung nhanh và tối ưu hơn một chút?… Hy vọng sau bài này sẽ giúp các bạn có một cái nhìn rõ hơn cũng như giúp ích được trong công việc hiện tại. 1. Cấu trúc của một trình duyệt Trước tiên chúng ta đi qua cấu trúc, thành phần chung và cơ bản nhất của một trình duyệt web hiện đại, nó sẽ gồm các thành phần (tầng) như sau: Thành phần nằm phía trên là những thành phần gần với tương tác của người dùng, càng phía dưới thì càng sâu và nặng về xử lý dữ liệu hơn tương tác. Nhiệm...

By stationd
Tối ưu tỉ lệ chuyển đổi với Google Optimize và Google Analytics

Tối ưu tỉ lệ chuyển đổi với Google Optimize và Google Analytics

Tối ưu tỷ lệ chuyển đổi là một trong những yếu tố quan trọng mà một Growth Marketer không thể bỏ qua. Google Analytics là công cụ hữu hiệu và phổ biến nhất giúp chúng ta theo dõi, đo lường và tối ưu tỷ lệ chuyển đổi. Số liệu từ Google Analytics giúp chúng ta nhìn ra điểm cần thay đổi để tăng tỷ lệ chuyển đổi. Theo dõi chuyển đổi là quá trình thiết yếu nhưng cũng rất “khó nhằn”. Ngày nay, mọi người đang sử dụng điện thoại di động nhiều hơn và thường xuyên hơn khi mua hàng trực tuyến. Việc tối ưu hóa website phiên bản di động rất quan trọng. Traffic đến từ mobile khá lớn và làm ảnh hưởng nhiều tới tỷ lệ chuyển đổi. Để xem website của bạn có hoạt động tốt trên mobile hay không như thế nào? Làm gì khi tỷ lệ chuyển đổi giảm? Website phiên bản di động của bạn có nội dung hấp dẫn không? Không có những nút điều hướng hành động? Trang bị lỗi …, những điều này có thể ảnh hưởng tới tỉ lệ chuyển đổi Google Analytics, hiểu được điều này, bạn sẽ có cơ sở để khắc phục và hoàn thiện website của mình. Hãy cùng đến với chủ đề “DÙNG GOOGLE ANALYTICS TỐI ƯU TỈ LỆ CHUYỂN ĐỔI TRÊN MOBILE” với sự chia sẻ của diễn giả Nguyễn Minh Đức, CEO IM GROUP tại Vietnam Mobile Day 2018 nhé

By stationd