01/10/2018, 13:58

Chuyển code mảng động 2 chiều dạng C sang C++ (con trỏ)

Mình có 1 đoạn code cấp phát mảng động 2 chiều sau (ngôn ngữ C)

template <class T>
void allocMatrix(T** &matrix, int n, int m)
{
	size_t pointerSize = n * sizeof(T*);
	size_t storeSize = n * (size_t)m * sizeof(T);
	void *buffer = calloc(pointerSize + storeSize, 1); //1
	if (buffer == nullptr) return;
	matrix = (T**)buffer;
	T *data = matrix[0] = (T*)((char*)buffer + pointerSize); //2
	for (int i = 0; i < n; ++i)
	{
		data += m;
		matrix[i] = data;
	}
}

Đoạn code trên dùng để cấp phát 1 vùng nhớ duy nhất chứa con trỏ và các phần tử trong ma trận (sách Kĩ Thuật Lập Trình - Trần Đan Thư)
Code trên có dùng hàm calloc của C, bây giờ mình muốn chỉnh lại sang C++ (từ //1 đến //2) như sau:

T *buffer = new T[pointerSize + storeSize];
if (buffer == nullptr) return;
matrix = (T**)buffer;
T *data = matrix[0] = buffer + pointerSize;

thì tính chất có giống với code gốc phía trên không mọi người. Mình có chạy qua nhiều test (đã thành công) nhưng vẫn muốn lên đây hỏi cho chắc. Cảm ơn mọi người.

viết 16:07 ngày 01/10/2018

code gốc đã là C++ rồi mà??

viết cái gì ko ai hiểu @_@

em chuyển code kia lại xài new sai rồi
T *buffer = new T[pointerSize + storeSize];
phải là
char *buffer = new char[pointerSize + storeSize];
nếu xài C++17 thì dùng std::byte thay cho char

viết 15:59 ngày 01/10/2018

nếu muốn kĩ hơn nữa thì em viết thêm cái cast cho đàng hoàng:

char* buffer = new char[pointerSize + storeSize];

//gán địa chỉ của char* buffer (con trỏ 1 cấp) cho T** matrix (con trỏ 2 cấp)
//phải viết rõ ràng ra để người khác biết là mình biết mình đang
//chỉa súng vào chân mình
matrix = static_cast<T**>(static_cast<void*>(buffer)); 

//tương tự. Viết dài dòng để người đọc hiểu mình biết mình đang làm gì
T* data = matrix[0] = static_cast<T*>(static_cast<void*>(buffer + pointerSize));
Hứa Anh Minh viết 16:06 ngày 01/10/2018

ý là em thấy code ban đầu khai báo void, rồi sau đó phải ép về kiểu T* nên em nghĩ là lúc đầu khai báo bằng T* để lúc sau khỏi cần ép kiểu

rogp10 viết 16:08 ngày 01/10/2018

Muốn cho giống thì phải dùng new(std::nothrow), dùng new thường sẽ bị văng exception khi ko thể cấp phát được.

Vì sao phải cast 2 lần? Bởi vì chỉ có chuyển từ void* đi và đến là được xác định (defined).

Hứa Anh Minh viết 16:01 ngày 01/10/2018

em chưa rõ lắm đoạn cast 2 lần, anh giải thích cho em với
code trong sách khai báo là

void *buffer = calloc(pointerSize + storeSize, 1);
matrix = (T**)buffer;
T *data = matrix[0] = (T*)((char*)buffer + pointerSize);

em thấy ép kiểu 2 lần là (T*)((char*)buffer... nên định ban đầu sẽ cho T *buffer = new (nothrow) T[pointerSize + storeSize]; để khỏi phải ép kiểu 2 lần.

rogp10 viết 16:02 ngày 01/10/2018

void* có hai đặc tính:

  • Chuyển đổi qua void* giữ nguyên giá trị con trỏ.
  • void* không có phép tính.

Cấp phát kiểu T ngay sẽ rất nguy hiểm vì phải chia trường hợp ứng với alignment của T. Phân tích dòng 6 sẽ thấy rằng phần dữ liệu phải nằm sau n con trỏ. Câu này nhiều nguy cơ dính unspecified do T có alignment chặt hơn char (C++) hoặc undefined do không phải là 1 byte © chiếu theo 6.3.2.3 N1256.

Vậy hay nhất là xin cấp phát char rồi chỉ dùng void* làm trung gian như trên.

Bài liên quan
0