2011-01-13 33 views
8

trong ví dụ này, mặc dù tôi sẽ không bao giờ sử dụng các biến WNDCLASSEX, x, y, cx, cy, họ sẽ vẫn sử dụng bộ nhớ khi tôi đang ở trong vòng lặp thông điệp:Khi khai báo các biến trong phạm vi {}, chúng sẽ vẫn sử dụng bộ nhớ sau?

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpArgs, int iCmdShow) 
    { 
    WNDCLASSEX wc; 
    ... 
    RegisterClassEx(&wc); 

    const int cx = 640; 
    const int cy = 480; 
    // center of the screen 
    int x = (GetSystemMetrics(SM_CXSCREEN) - cx)/2; 
    int y = (GetSystemMetrics(SM_CXSCREEN) - cy)/2; 

    CreateWindow(..., x, y, cx, cy, ...); 

    MSG msg; 

    while (GetMessage(&msg, NULL, 0, 0) > 0) 
    { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
    return 0; 
    } 

Nhưng tôi tự hỏi , nếu tôi đặt chúng trong một phạm vi, họ vẫn sẽ sử dụng bộ nhớ trong vòng lặp tin nhắn? ví dụ.

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpArgs, int iCmdShow) 
{ 
{ 
    WNDCLASSEX wc; 
    ... 
    RegisterClassEx(&wc); 

    const int cx = 640; 
    const int cy = 480; 
    // center of the screen 
    int x = (GetSystemMetrics(SM_CXSCREEN) - cx)/2; 
    int y = (GetSystemMetrics(SM_CXSCREEN) - cy)/2; 

    CreateWindow(..., x, y, cx, cy, ...); 
} 

MSG msg; 

while (GetMessage(&msg, NULL, 0, 0) > 0) 
{ 
    TranslateMessage(&msg); 
    DispatchMessage(&msg); 
} 
return 0; 
} 

hoặc có thể nếu tôi đặt chúng thành hai hàm và gọi chúng là winmain, ví dụ:

wnd_register(hInst); 
wnd_create(hInst); 

sẽ ngăn chúng sử dụng bộ nhớ?

+0

câu hỏi hayMặc dù, sẽ rất dễ dàng để thử nghiệm như nightcracker nói. Có thể đã dành ít thời gian thử nghiệm nó cho mình hơn là gõ câu hỏi này lên! –

+1

Tại sao bạn khai báo các biến mà bạn không sử dụng – Falmarri

+0

Đây thực sự là một tối ưu hóa vi mô vô ích. Chủ đề trên Windows theo mặc định có ngăn xếp 1 MB. Bạn đang lo lắng về lãng phí ít hơn một phần trăm của một phần trăm không gian ngăn xếp có sẵn của bạn. – Michael

Trả lời

6

Trình biên dịch có rất nhiều thời gian để xử lý những người dân địa phương đơn giản, như bạn có trong ví dụ của mình. Chúng có thể sống trên ngăn xếp, chúng chỉ có thể tồn tại dưới dạng các giá trị ngay lập tức trong mã máy, hoặc chúng chỉ có thể tồn tại trong sổ đăng ký. Không gian ngăn xếp thường được phân bổ khi nhập vào một hàm. Trình biên dịch sẽ trừ một số giá trị từ con trỏ ngăn xếp để tạo không gian cho tất cả người dân địa phương. Khi trả về hàm, con trỏ ngăn xếp được khôi phục về giá trị ban đầu của nó. Điều này thường không được thực hiện khi thoát khỏi các khối phạm vi khác nhau. Hầu hết các trình biên dịch sẽ cố gắng tích cực tái sử dụng không gian ngăn xếp ngay sau khi các biến không còn được sử dụng nữa. Trong ví dụ của bạn, nó sẽ hoàn toàn hợp pháp cho x và msg để có cùng một địa chỉ chính xác trên ngăn xếp, vì việc sử dụng chúng không bị chồng chéo.

Câu trả lời của tôi cho this question đi vào chi tiết hơn về cách các biến cục bộ được phân bổ trên ngăn xếp.

Trong ví dụ của bạn, hằng số, cx và cy, rất có thể sẽ không có bộ nhớ sao lưu chúng khi chạy và chỉ là giá trị ngay lập tức trong mã được tạo. x và y rất có thể sẽ sống trong sổ đăng ký cho đến khi chúng cần được đẩy lên ngăn xếp cho lệnh gọi hàm CreateWindow. wc và msg gần như chắc chắn sẽ nằm trên stack.

Bạn không nên lo lắng về tối ưu hóa vi mô ở cấp độ này - hãy để trình biên dịch phân bổ không gian cho các biến cục bộ khi nó thấy phù hợp. Bạn có một ngăn xếp 1 MB theo mặc định, lượng dữ liệu được tiêu thụ bởi các biến này thậm chí sẽ không đăng ký là tiếng ồn. Hãy dành thời gian của bạn lo lắng về các vấn đề thú vị hơn thay thế.

+0

+1, mặc dù thực sự trình biên dịch có thể KHÔNG trùng lặp wc và x, vì wc có địa chỉ được lấy và chuyển đến hàm, vì vậy nó có thể được truy cập trong cuộc gọi sau trong khi vẫn còn trực tiếp, chẳng hạn như CreateWindow. Tôi nói có lẽ như là trình biên dịch có thể hình dung làm phân tích chéo chức năng, nhưng hầu hết không –

+0

Vâng, bạn nói đúng, lấy địa chỉ không ngăn cản trình biên dịch từ tái sử dụng không gian. Trình biên dịch không thể phân tích chức năng chéo ở đây vì RegisterClassEx và CreateWindow sống trong một mô-đun khác. – Michael

0

Ôi không, bốn số nguyên trong bộ nhớ trong khi chương trình đang chạy, thật là một sự lãng phí!

  1. Hãy dùng thử, một hộp thông báo đơn giản cố in chúng là đủ (tôi nghĩ).
  2. Đừng bận tâm.
+0

thực sự wndclassex có khoảng 10 thành viên: P bao gồm một chuỗi – Kaije

+0

Có thể thậm chí không làm điều đó. Bạn sẽ nhận được một lỗi biên dịch, 'x' là undeclared –

+0

Chuỗi trong wndclass rất có thể là một hằng số và sẽ không đi ra khỏi không gian trên ngăn xếp cho người dân địa phương. – Michael

3

Có lẽ không, nhưng đó là chi tiết triển khai. Họ sẽ bị phá hủy mặc dù (các cuộc gọi destructor sẽ được thực hiện nếu có bất kỳ để thực hiện). Cho dù và khi nào hệ thống phục hồi bộ nhớ được sử dụng để lưu trữ tự động không được chỉ định theo tiêu chuẩn. Hầu hết làm cho nó trở lại khá nhiều ngay lập tức afaik.

+0

Bất kỳ bộ nhớ động nào được sử dụng bên trong các đối tượng lớp, chẳng hạn như trong chuỗi, phải được giải phóng ngay lập tức bởi đối tượng hủy của đối tượng. Tôi tự hỏi nếu có trình biên dịch mà giữ lại bộ nhớ ngăn xếp cho đến khi kết thúc chức năng cho mục đích tốc độ, mặc dù nó thường chỉ có 1 hướng dẫn để phát hành nó? –

+1

Hầu hết các trình biên dịch sẽ tích cực tái sử dụng không gian ngăn xếp. Nó sẽ là hoàn toàn hợp pháp cho msg và wc để có cùng một địa chỉ chính xác trong cả hai ví dụ của mình. – Michael

+0

@Michael, tôi có thể thấy làm thế nào có thể cho trường hợp thứ hai, nhưng đối với người đầu tiên trình biên dịch không thể gọi destructor cho 'wc' cho đến khi kết thúc chức năng để nó không thể chia sẻ bộ nhớ với bất cứ điều gì. –

0

Các biến được khai báo của bạn bên trong {} sẽ không nằm trong phạm vi và bị mất. Trong thực tế, bạn sẽ nhận được một lỗi biên dịch nếu bạn cố gắng sử dụng chúng bên ngoài khối: 'x' không khai báo. Tuy nhiên, điều này là cẩu thả. Chỉ cần tạo một hàm cho mã này, như bạn đã nói trong bản chỉnh sửa của mình. Việc giữ cho các dòng chính() của bạn càng ít càng tốt là thực hành lập trình đơn giản.

1

Vâng, tôi không chắc chắn về chúng bằng cách sử dụng bộ nhớ hoặc những gì tiêu chuẩn nói về nó.

Điều tôi biết là ở cuối khối bộ nhớ {} trình phá hủy sẽ được gọi và các biến sẽ không thể truy cập được. Điều này có nghĩa là, trong khi nó không được giải phóng, ít nhất nó có thể được tái sử dụng.

Ví dụ:

struct Foo { 
    Foo(void) { std::cout << "Hi!"; } 
    ~Foo(void) { std::cout << "Bye!"; } 
}; 

int main(int argc, char * argv[]) 
{ 
    { 
     Foo bar; // <- Prints Hi! 
    } // <- Prints Bye! 

    // Memory used by bar is now available. 
} 

Edit: Cảm ơn Tomalak Geret'kal;)

+0

"Điều này có nghĩa là, trong khi nó không được giải phóng, ít nhất nó có thể được tái sử dụng." - Bạn không có cách nào kiểm soát nơi biến tự động của bạn đi. Không có cách nào để "tái sử dụng" bộ nhớ chưa được đưa ra. –

+0

Lưu ý rằng vì bạn có một hàm tạo rõ ràng chạy mã, trình biên dịch PHẢI chạy nó khi thoát khỏi khối. Điều này không nói bất cứ điều gì về không gian ngăn xếp cơ bản. – Michael

+0

Bạn đang thiếu dấu ngoặc trong ví dụ mã của mình. –

0

Họ sẽ không. Họ sẽ chỉ sống cho đến khi kết thúc khối bao quanh của họ.

1

Một lời khuyên kỳ diệu: Tin cậy trình biên dịch của bạn. Nó tối ưu hóa. Đó là thông minh. Nó tối ưu hóa tốt hơn so với hầu hết chúng ta có thể.

Nếu bạn không chắc chắn, hãy sử dụng trình lược tả hoặc kiểm tra đầu ra của trình biên dịch sau khi tối ưu hóa. Nhưng hãy nhớ - tối ưu hóa tầm thường là một cái gì đó bạn nên không làm trong mã của bạn, vì nó là vô nghĩa và chỉ làm tổn thương khả năng đọc mã của bạn.

Một số biến (đặc biệt là hằng số) sẽ không sử dụng bất kỳ bộ nhớ nào trên ngăn xếp vì chúng sẽ được ánh xạ lên thanh ghi CPU hoặc được nhúng trực tiếp vào lệnh lắp ráp.

này ngụ ý rằng các mã:

func(123+456*198*value); 

int a = 123; 
int b = 56; 
int c = 400; 
int d = b+c; 
int e = d*198; 
e *= value; 
e += a; 
func(e); 

sẽ biên dịch chính xác những điều tương tự (nếu các biến không bao giờ được sử dụng một lần nữa).

Nghiêm túc, đừng bận tâm. Nếu bạn muốn tối ưu hóa, hãy tối ưu hóa từ quan điểm thuật toán, không phải là cú pháp.

0

Nếu bạn đặt chúng trong một phạm vi lồng nhau bên trong hàm (tùy chọn đầu tiên), thì khi điều khiển đến hết phạm vi, biến trở nên không thể truy cập được (hoặc lỗi biên dịch nếu bạn sử dụng chúng trực tiếp hoặc hành vi không xác định thời gian chạy nếu bạn lưu con trỏ vào một trong số chúng), các trình phá hủy của chúng được chạy (nếu có các trình phá hủy) và việc triển khai có thể sử dụng lại dung lượng lưu trữ của chúng trong khung ngăn xếp. Nhưng các tiêu chuẩn không yêu cầu nó để tái sử dụng không gian.

Nếu bạn chia chức năng của mình thành hai (tùy chọn thứ hai) ... về mặt cắt tóc chuẩn, không có sự khác biệt! Khi hàm trả về, các biến trở nên không thể tiếp cận, các trình phá hủy của chúng được chạy và việc triển khai có thể sử dụng lại dung lượng bộ nhớ của chúng, nhưng không bắt buộc. Và có triển khai nghiêm túc được - mặc dù không thuộc về C/C++ - mà không ngay lập tức tái chế bộ nhớ rằng: xem nổi tiếng giấy "Cheney on the M.T.A."

Tuy nhiên, tất cả hiện thực của C/C++ nhất mà tôi hiện đang nhận thức được làm tái chế bộ nhớ được phân bổ cho các biến cục bộ của hàm khi một hàm trả về. Tái chế bộ nhớ cho các biến phạm vi lồng nhau-cục bộ là ít chắc chắn hơn nhiều. Trong mọi trường hợp, như một số người khác đã đề cập, nó không phải là đáng lo ngại về một vài chục byte của không gian ngăn xếp trong bối cảnh này.

Cá nhân, tôi sẽ chia nhỏ mã của bạn thành hai hàm chỉ vì cách đó mỗi hàm chỉ thực hiện một tác vụ. Nói chung tốt hơn cho việc bảo trì dài hạn.

0

Nói chung có, nếu biến nằm trên ngăn xếp, thì không gian cho nó sẽ được đưa vào ngăn xếp cho toàn bộ thời lượng của hàm bao. Trình biên dịch thường tính toán số lượng không gian tối đa mà các biến của hàm có thể chiếm và sau đó làm cho hàm phân bổ tất cả các hàm đó cùng một lúc khi hàm được nhập lần đầu tiên. Tuy nhiên, các nhà xây dựng và phá hủy vẫn sẽ được gọi khi vào và ra khỏi các phạm vi bên trong. Không gian cho các biến trong một phạm vi có thể được sử dụng lại để biểu diễn các biến từ một phạm vi riêng biệt.

Các vấn đề liên quan