2013-08-23 54 views
36

Trong đoạn mã sau, *(int32 *) 0 = 0; có nghĩa là gì?Điều này có nghĩa là gì ?: * (int32 *) 0 = 0;

void 
function (void) 
{ 
    ... 

    for (;;) 
    *(int32 *) 0 = 0;  /* What does this line do? */ 
} 

Một vài lưu ý:

  • Mã này dường như không thể truy cập, như có một tuyên bố thoát trước đó đoạn cụ thể của mã.
  • int32typedef 'đã chỉnh sửa nhưng bạn không nên quan tâm quá nhiều về điều đó.
  • Đoạn mã này là từ thời gian chạy ngôn ngữ trong trình biên dịch, cho bất kỳ ai quan tâm.
+7

Đó là vòng lặp vô hạn lưu trữ 4 byte 0 vào vị trí 0-3. Nó trông giống như một cố ý hủy bỏ hoặc cố ý treo. –

+2

@Habib có ofcourse Tôi biết rằng 'for (;;)' là một vòng lặp vô hạn. Phần dưới đây là phần tôi không hiểu. Xin lỗi vì sự mơ hồ. – NlightNFotis

+0

@Curtis Tôi không nghĩ đó là một điều xấu. Điều này được viết bởi các kỹ sư biên dịch có kinh nghiệm, và nó phải ở đó vì một lý do. Việc thực hiện bất thường trong trường hợp có điều gì đó không ổn có vẻ giống như một lý do chính đáng, được đưa ra câu trả lời cho đến nay. – NlightNFotis

Trả lời

32

Mã này được làm như sau:

for (;;) // while(true) 
    *(int32 *) 0 = 0; // Treat 0 as an address, de-reference the 0 address and try and store 0 into it. 

này nên segfault, null pointer de-tham khảo.

EDIT

Biên soạn và chạy để biết thêm thông tin:

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 

int main(void){ 
    *(int32_t *) 0 = 0; 
    printf("done\n"); 
    return 0; 
} 

gcc -g null.c; ./a.out

Program received signal SIGSEGV, Segmentation fault. 
0x00000000004004cd in main() at null.c:7 
7   *(int32_t *) 0 = 0; 
+3

@Hrishi: Trên bất kỳ hệ thống hợp lý nào (ngoại trừ có lẽ đối với một số nền tảng nhúng rất nhỏ), lỗi sẽ chỉ ảnh hưởng đến một chương trình đó. –

+0

@KeithThompson Tôi nghĩ rằng hệ điều hành sẽ lưu trữ một bảng con trỏ đến các thường trình của nó trong các địa chỉ thấp hơn trong bộ nhớ? điều đó không còn đúng trong các hệ điều hành hiện đại? – Hrishi

+25

@ Hrishi: Tôi không có ý tưởng - nhưng trên bất kỳ hệ điều hành đa xử lý hiện đại nào (Unix, Linux, Windows, ...), một chương trình không có đặc quyền không thể làm hỏng cấu trúc dữ liệu của hệ điều hành (trừ khi có lỗi trên hệ điều hành, nhưng lỗi như vậy không thể được kích hoạt bởi mã tầm thường như vậy). Thông thường, mỗi quá trình có không gian địa chỉ ảo riêng; địa chỉ 0 cho quá trình của bạn, nếu nó tồn tại, không có gì để làm với địa chỉ 0 cho hạt nhân hoặc cho bất kỳ quá trình khác. –

3

Đây là một vòng lặp vô hạn của hành vi undefined (dereferencing một con trỏ null). Nó có khả năng sụp đổ với một segfault trên * n * x hoặc vi phạm truy cập trên Windows.

6

for(;;) tương đương với while(1),

*(int32 *) 0 = 0; viết từ 0 đến một con trỏ null dereferenced, được dự kiến ​​sẽ gây ra một vụ tai nạn, nhưng thực tế sẽ không tại mọi thời điểm trên một số trình biên dịch: Crashing threads with *(int*)NULL = 1; problematic?

3

bình luận của Mike khá chính xác: nó lưu trữ VALUE không ở ADDRESS 0.

Điều này sẽ là sự cố trên hầu hết các máy.

22

Trong thực tế rằng mã này seg-đứt gãy không giải thích lý do tại sao nó tồn tại =)

Tôi nghĩ đó là từ thời gian chạy của một số MCU .. và lý do nó là có bởi vì nếu thực hiện chương trình sẽ nhận được đến thời điểm này như thế hoặc nếu MCU cấu hình với phần cứng watchdog, buộc MCU khởi động lại vì phần cứng watchdog và không bao giờ kết thúc vòng lặp.

Mục tiêu chính của các công trình như vậy để gọi ngắt có thể được xử lý bằng hệ điều hành hoặc phần cứng để bắt đầu một số hành động nhất định.

Biết rằng x86 của nó sẽ phụ thuộc vào chế độ CPU ... ở chế độ Real không có gì thực sự xảy ra ngay lập tức nếu không có cơ quan giám sát, tại địa chỉ 0 có địa chỉ xử lý 'chia cho 0', vì vậy nếu đó là một số MS-DOS cũ hoặc thời gian chạy x86 nhúng nó sẽ thay đổi địa chỉ của xử lý 'Divide by 0' thành 0, vì vậy ngay sau khi nó xảy ra và ngắt này không bị che khuất CPU sẽ nhảy tới vị trí 0: 0 và có lẽ sẽ chỉ khởi động lại vì hướng dẫn bất hợp pháp ..nếu nó được bảo vệ hoặc mã VM x86 thì đó là một cách để thông báo cho OS hoặc bất kỳ người giám sát nào khác rằng có một vấn đề trong thời gian chạy và phần mềm nên được 'giết' bên ngoài.

+0

Cảm ơn bạn đã giải thích cho dude! Tuy nhiên đây là mã được nhắm mục tiêu x86 và dường như không bao giờ được thực thi. Có một 'exit (0)' trước đoạn mã cụ thể đó, và tôi tin rằng nó ở đó để hủy bỏ trong trường hợp có gì đó sai với 'exit'. – NlightNFotis

+0

bạn đúng, câu trả lời được cập nhật – evilruff

+0

cảm ơn, dường như xác nhận lý thuyết của tôi :) – NlightNFotis

26

Kể từ khi các bang OP mã đã được viết bởi các kỹ sư trình biên dịch có kinh nghiệm, có thể đây là mục đích của các mã:

  • *(int32 *) 0 = 0; được công nhận bởi thực hiện này C cụ thể như mã gây ra hành vi không được định nghĩa bởi tiêu chuẩn C và được biết đến với việc triển khai này là bất hợp pháp.
  • Các for (;;) bổ sung cho biết mã này không bao giờ bị thoát. Các kỹ sư biên dịch biết rằng trình tối ưu hóa sẽ nhận ra mã này và suy luận rằng nó có thể được "tối ưu hóa", bởi vì bất kỳ chương trình nào đạt đến mã này đều được phép có bất kỳ hành vi nào, vì vậy trình tối ưu hóa có thể chọn cung cấp hành vi nếu mã không bao giờ đạt được.

này loại suy luận có thể chỉ nếu bạn có kiến ​​thức cụ thể của hoạt động nội bộ của một thi C. Đó là điều mà một kỹ sư biên dịch có thể bao gồm trong các tiêu đề đặc biệt cho việc triển khai C, có lẽ để đánh dấu một số mã nhất định (chẳng hạn như mã sau khi gọi abort) không bao giờ đạt được. Nó không bao giờ nên được sử dụng trong lập trình bình thường.


Ví dụ, hãy xem xét mã này:

if (a) 
    for (;;) 
     *(int 32 *) 0 = 0; 
else 
    foo(); 

Trình biên dịch có thể nhận ra rằng lúc bấy giờ điều khoản được phép có bất cứ hành vi. Do đó, trình biên dịch được tự do lựa chọn hành vi của nó. Để đơn giản, nó chọn nó để có cùng một hành vi như foo();. Sau đó, mã trở thành:

if (a) 
    foo(); 
else 
    foo(); 

và có thể được đơn giản hóa hơn nữa để:

foo(); 
1

Có thể là trình biên dịch không biết exit() không trả lại, nhưng nó không biết cấu trúc này không trở về.

3

Bản gốc IBM PC stored the interrupt vector table in the lowest 1 KiB of memory. Do đó thực sự viết một giá trị 32 bit đến địa chỉ 0 trên kiến ​​trúc như vậy sẽ ghi đè lên địa chỉ cho INT 00h. INT 00h looks unused in the PC.

On về cơ bản bất cứ điều gì hiện đại (có nghĩa là trong x86/x86-64 parlace bất cứ điều gì đang chạy trong bảo vệ hoặc dài chế độ), nó sẽ kích hoạt một lỗi segmentation trừ khi bạn đang ở trong vòng 0 (kernel mode) bởi vì bạn là bước ra ngoài phạm vi 'dereference dereference range' của quy trình của bạn.

Do sự thiếu cân nhắc là hành vi không xác định (như đã nêu), lỗi phân đoạn là cách hoàn toàn chấp nhận được để xử lý tình huống đó. Nếu bạn biết rằng trên kiến ​​trúc đích, một tham số địa chỉ số không gây ra lỗi phân đoạn, nó có vẻ là một cách khá chắc chắn để ứng dụng gặp sự cố. Nếu exit() trả về, đó có thể là những gì bạn muốn làm, vì một cái gì đó vừa sai lầm khủng khiếp.Đó là mã từ thời gian chạy của một trình biên dịch cụ thể có nghĩa là bất cứ ai đã viết nó có thể tận dụng kiến ​​thức về các hoạt động bên trong của trình biên dịch và thời gian chạy, cũng như điều chỉnh nó cho hành vi của kiến ​​trúc đích cụ thể.

+1

00h là số chia cho số không gián đoạn trên DOS. (http://en.wikipedia.org/wiki/BIOS_interrupt_call) Tôi nghi ngờ đó là những gì đang xảy ra ở đây, mặc dù. –

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