30/09/2018, 18:03

Hỏi về mảng tĩnh

Mình có 1 thắc mắc về mảng tĩnh mong mọi người giải đáp giúp, đây là code của mình :

#include <iostream>

using namespace std;

int main()
{
	int const n = 5;
	int a[5] = {1, 2, 3, 4, 5}; // 0->4
	
	
	a[6] = 3; // a[6] chưa có ? cho giá trị a[6] = 3 

	cout << a[6];


	for (int i = 0; i <= 6 ; i++)
	{
		cout << a[i] << endl;
	}
	system("pause");
	return 0;
}

Trong đó mình tạo 1 mảng tĩnh có 5 phần tử với các giá trị từ 1 đến 5, và chỉ số của mảng sẽ chạy từ 0 -> 4. Mình thử cout ra giá trị a[6] thì nó hiện ra là giá trị rác, nhưng nếu mình sửa lại a[6] = 3 thì vẫn in ra được như thường (riêng a[5] mình không gán nên vẫn là giá trị rác).

Theo mình biết thì bản chất của mảng tĩnh là cố định, nếu như theo cách trên mình làm thì có phải là đang mở rộng chiều dài mảng ra không ? Mình thấy khá mâu thuẫn @@. Mong mọi người giúp !

Nguyễn Văn Tâm viết 20:12 ngày 30/09/2018

C và C++ vẫn cho phép chỉ số của mảng vượt quá giá trị đã cấp phát. Ở đây a[6] nó sẽ hiểu là vùng nhớ thứ 6 kể từ vùng nhớ mà a trỏ tới.

Võ Hoàng Việt viết 20:10 ngày 30/09/2018

Vậy điều gì nếu mình thực hiện như sau ? :

for (int i = 6; i < 20000; i++)
{
a[i] = 100;
} 

Nó vẫn cho mình truy cập luôn hả bạn ? Nếu vậy thì đâu còn đúng về khái niệm mảng tĩnh là cố định nữa ?

Minh Hoàng viết 20:12 ngày 30/09/2018

Bạn có thể cho mình xem khái niệm của mảng tĩnh ko?
Cái mà bạn đang nói tới đó là một lỗi tràn bộ đệm phổ biến. Nhiều IDE sẽ tự động báo lỗi thôi.
Mảng tĩnh là cố định về kích thước (compile sẽ cố định kích thước này), nhưng với bản thân người viết thì không cố định. Vậy cố định ở đây là dành cho compile, không phải cho coder.
Việc được phép truy cập a[6] có lẽ là do cài đặt operator[] của int. Có vẻ như operator này không hề kiểm tra kích thước của mảnh (hoặc là ko kiểm tra được, mình ko ràng lắm), nên cho phép ghi đè và đọc vùng nhớ a[6].

Vậy điều gì nếu mình thực hiện như sau ? :

Tùy tình hình, nếu vướt quá bộ nhớ stack sẽ có lỗi xuất hiện (hoặc không tùy compile).

.vn viết 20:09 ngày 30/09/2018

nếu bạn để code ở trên chạy dược mới lạ

Võ Hoàng Việt viết 20:08 ngày 30/09/2018

Vậy nếu mình xây dựng các hàm Them, Xoa 1 vị trí trong mảng, như code này :
http://codepad.org/KLsQyanL
thì đây vẫn là 1 hành vi truy cập bộ nhớ trái phép ?

Sẵn đây bạn cho mình hỏi : Mảng động được lưu trong Heap, còn mảng tĩnh được lưu trong Stack, vì thế mảng động có thể tuỳ biến được kích thước ?

Võ Hoàng Việt viết 20:06 ngày 30/09/2018

Mình đã thử gán a[20000] = 100 rồi, vẫn chạy được và in ra bt @@. Nhưng nay giờ mình tìm hiểu thì đây là hành vi truy cập trái phép vào vùng nhớ stack, có thể gây nguy hiểm @@

Minh Hoàng viết 20:19 ngày 30/09/2018

Sẵn đây bạn cho mình hỏi : Mảng động được lưu trong Heap, còn mảng tĩnh được lưu trong Stack, vì thế mảng động có thể tuỳ biến được kích thước ?

Mảng động có thể tùy biến được kích thước là do compiler hỗ trợ việc này.
Quay lại một chút về quá trình cấp vùng nhớ của heap. Khi bạn cần một vùng nhớ động, compiler sẽ cho bạn một vùng nhớ trên heap tương ứng với size yêu cầu, đồng thời cung cấp thêm một capacity. Nếu bạn muốn tăng giảm kích thước thì ok, compiler sẽ tăng thêm bằng cấp cung cấp thêm ô nhớ nằm sẵn trong capacity này. Nếu vượt quá capacity này thì sao? compiler sẽ tìm một vùng nhớ rộng hơn và đưa cho bạn. Hầu hết các công việc đều do compiler làm.
Nhưng tại sao lại không hỗ trợ cho stack memory, có thể lý do là nó có thể gây lỗi. Vùng nhớ stack khá đặc biệt, nó chứa các lời gọi hàm, các địa chỉ,… nếu chỉ thay đổi một ít trong này có thể gây lỗi cho cả chương trình (hoặc có thể là os). Một ví dụ là nếu bạn truy cập trái phép một vùng nhớ chứa địa chỉ và thay địa chỉ đó bằng địa chỉ của một chương trình nguy hiểm. (bạn có thể xem một số cách tận dụng lỗi với gets).
Nên nói chung quậy phá dữ liệu nhiều thì nên chuyển qua heap.
Về trường hợp đoạn code ở trên thì đúng là như vậy (không biết nó có gây lỗi gì ko nhỉ).

Võ Hoàng Việt viết 20:17 ngày 30/09/2018

Bạn ơi, theo mình hiểu thì cái mảng động nó chỉ khác mảng tĩnh ở chỗ là số phần tử có thể dc xác định trong khi chạy chương trình, còn mảng tĩnh thì phải khai báo trước. Bạn có thể cho mình 1 ví dụ về việc thay đổi kích thước mảng động không ? Cảm ơn bạn nhiều lắm !

nhatlonggunz viết 20:16 ngày 30/09/2018

Có lẽ là do:

arr[5] = {1, 2, 3, 4};
std::cout << *(arr + 2); // xuất ra 3

Mảng là một dãy các ô nhớ (ai sửa hộ em chỗ này, em thấy có vẻ không đúng) và cũng là con trỏ trỏ vào phần tử đầu tiên.
=> arr + 0 là phần tử đầu, arr + 1, là phần tử ở vị trí (index) 1
Vậy thay vì truy cập mảng bằng arr[2], ta có thể truy cập bằng *(arr + 2) (phần tử đầu là arr, +2 vị trí nữa)

Vì thế, gán arr[100] = 1 (dù chỉ khai báo mảng là arr[5]) tương đương với gán *(arr + 100) = 1
Vậy là anh đã gán cho vùng nhớ cách mảng đó 95 ô nhớ :v => cái ô nhớ mà anh gán nó không thuộc về mảng.
Và nếu lỡ ô nhớ đó là ô nhớ đã được dùng (compiler dùng, máy dùng, cấp phát động hay các biến khác dùng, …) thì sẽ gặp lỗi.

Bài liên quan
0