Lập trình

Lập trình

Thứ Ba, 15 tháng 12, 2015

Tìm hiểu con trỏ và bộ nhớ [1]

Con trỏ là một kiểu chứa địa chỉ của biến mà nó trỏ đến, biến mà nó trỏ đến theo thuật ngữ gọi là pointee
, qua con trỏ ta có thể truy cập được vào ô nhớ mà con trỏ đang tham chiếu, nhưng con trỏ bắt buộc phải chiếu đến một pointee trước khi nó truy cập vào pointee

NULL pointer là một con trỏ đặc biệt , được hiểu rằng nó không trỏ đến cái gì. Nó có ý nghĩa là con trỏ không trỏ đến pointee nào . Trong ngôn ngữ C kí hiệu NULL được sử dụng cho mục đích này. Chương trình bị runtime error nếu ta cố tình tham chiếu con trỏ NULL. (NULL có giá trị kiểu int là 0).

- Gán 2 con trỏ:
Việc gán 2 con trỏ sử dụng operator "=" giữa hai con trỏ làm cho con trỏ trỏ cùng đến một pointee. Việc hai con trỏ cùng trỏ đến một pointee theo thuật ngữ tin học người ta gọi nó là "sharing"

- Shallow and Deep copying
  http://theironns.blogspot.com/2015/12/shallow-copy-va-deep-copy.html

- Cần initation cho con trỏ vừa khai báo , vì ngày khi vừa khai báo, con trỏ chỉ được phân bổ vùng nhớ nhưng chưa cho trỏ tới pointee nào, người ta gọi là bad value

- Toán tử * là toán tử đơn dành cho con trỏ để dereference tới pointee mà nó đang trỏ, nó gây ra runtime nếu con trỏ không trỏ tới pointee nào.

- Con trỏ phải được trỏ đến pointee trước khi sử dụng, nếu không sẽ bị crash
Trường hợp dưới đây may mắn được compiler báo lỗi :
 #include <iostream>

using namespace std;

int main(){
int* p = new int;
int**q = new int*;
int* i;
cout << i << " " << *i;
*i = 7;
return 0;
}

error C4700: uninitialized local variable 'i' used

Dưới đây là các luật cần phải nắm rõ khi sử dụng con trỏ:

- Con trỏ lưu địa chỉ tới pointee của nó, pointee là nới chưa các giá trị hữu ích .

- Toán tử derefrence "*" tham chiếu giúp con trỏ tham chiều tới pointee của nó. Một con tro có thể được dereference sau khi  gán nó trỏ tới một pointee. Phần lớn các bug liên quan đến con trỏ đều liên quan đến việc vi phạm điều này.

- Việc phân bổ một con trỏ không tự động gán nó trỏ tới một pointee. Gán con trỏ trỏ tới một pointee đặc biệt là một sự thi hành riêng vì vậy nó rất dễ bị lãng quên.

- Việc gán giữa hai con trỏ cho nhau làm chúng trỏ cùng tới một pointee giống nhau

Biến locals của function.


Khi gọi một hàm con trong một hàm khác thì tất cả các biến local của nó được phân bổ bộ nhớ chứ không phân bố lần lượt như cứ gặp khai báo biến nào thì biến đó mới được phân bố bộ nhớ cho.

- The locals are allocated when their function begins running and are deallocated when it exits

# Thuận tiện của việc dùng biến cục bộ (locals variable)

- Thuận tiện: Cục bộ đáp ứng một nhu cầu thuận tiện - những hàm thường xuyên cần bộ nhớ tạm thời thứ mà tồn tại trong suất thời gian tính toán của hàm. Biến cục bộ cung cấp bộ nhớ tạm thời ngắn hạn, một vùng nhớ độc lập

- Hiệu quả : Liên quan tới các kĩ thuật sử dụng bộ nhớ khác . Allocating and deallocating chúng rất hiệu quả , rất nhanh và chúng là không gian hiệu quả trong cách chúng sử dụng và tái sử dụng bộ nhớ (hình ảnh).

- copy value of local: tham số cục bộ là một kiểu copy cục bộ cơ bản lấy thông tin từ lời gọi. Cái này được biết như là "tham trị". Tham số là các biến cục bộ, thứ được init với toán tử gán (=) toán tử được gọi. Lời gọi không chia sẻ (sharing) giá trị tham số cúng cho callee như trong cách dùng con trỏ (với pointee) - callee nhận nó làm của riêng khi copy. Điều này có một lợi ích là callee có thể thay đổi giá trị mà nó mà ko ảnh hưởng đến caller. Cái độc lập này rất tốt vì nó giữ hoạt động của caller và callee của function tách biệt - Đây là một qui tắc rất quan trọng cho kĩ sư phần mềm: đó là giữ cho các thành phần tách biệt nhất có thể

# Điểm yếu của biến cục bộ

- Vòng đời ngắn. allocation và deallocation của chúng rất khăc nghiệt. Đôi khi một chương trình cần bộ nhớ thứ tiếp tục được phân bổ (vẫn cần dùng đến biến cục bộ) ngay cả sau khi hàm (hàm ban đầu đã allocated co nó ) kết thúc. Biến cục bộ sẽ không làm việc bởi vì chúng tự động deallocated khi hàm của nó kết thúc. Vẫn đế này sẽ được giải quyết với heap memory.

- Giao tiếp hạn chế: bới vì biến cục bộ copy từ tham biến caller , chúng không cung cấp phương tiện giao tiếp lại với caller , đây chính là nhược điểm của lợi ích "phải tách biệt" ở trên. Vấn đề này được giải quyết bởi tham chiếu (reference parameters ).


# Có một vài từ đồng nghĩa cới "Local"
Biến local cũng được biết đến như biến "automatic" vì cấp phát và deallocation(trả vung nhớ về cho máy) được làm một cách tự động như là một phần của cơ chế gọi của function. Biến local cũng được biết như biến "stack" vì , ở mức thấp, ngôn ngữ gần như luôn luôn thực hiện biến local sử dụng với cấu trúc stack trong bộ nhớ

# Dấu và (&)
Chúng ta đã hiểu cách phân bổ và làm việc của locals variable, bạn có thể đánh giá một trong những đoạn bug tồi tệ nhất có thể xảy ra trong C và C++. Điều gì sai với đoạn code sao ở nơi function Victim gọi function TAB(). Hãy xem vấn đến và nó có thể hữu ích khi làm một vài hình ảnh vẽ ra bộ nhớ local của 2 function :

int* TAB(){
int temp;
return (&temp); // trả về đia chỉ của biến local kiểu int
}

void Victim(){
int* ptr;
ptr = TAB();
*ptr = 42// Error!
}

TAB() hoàn toàn tốt trong khi nó chạy. Vẫn đề xảy ra khi lời gọi sau khi TAB() thoát. TAB() trả về một con trỏ trỏ tới kiểu int, nhưng đâu là nơi int đó được cấp phát? Vấn đề là những biến local temp được phân bố chỉ khi TAB() đang chạy. Khi TAB() kết thúc thì nó bị deallocate. Tất cả các biến local của nó đều bị deallocate khi nó thoát.

Tóm lại là chúng ta đã sử dụng lại bộ nhớ khi bộ nhớ đó đã bị deallocate trong ví dụ trên nên nó dẫn tới lỗi.


#Tóm lại cho bộ nhớ local
Locals rất thuận tiện với những gì chúng làm - cung cấp một bộ nhớ tiện ích và hiệu quả cho function thứ mà tồn tại chỉ khi nó đang thực thi. Locals có hai khiếm khuyết - Làm thế nào để giao tiếp lại với caller của nó (caller đã đề cập cách nói này ở trên) và làm thế nào mà function phân bố bộ nhớ với vòng đời bị giới hạn của nó. (Ta xem ở bài sau).

Cảm ơn các bạn đã xem . Hẹn gặp lại các bạn.

Xem phần tiếp theo tại Con trỏ và bộ nhớ [2] - Bộ nhớ heap

Không có nhận xét nào:

Đăng nhận xét