2009-11-10 22 views
19

Tôi nghe nói rằng trong C, nếu tôi làmBộ nhớ chỉ đọc được thực hiện trong C như thế nào?

char *s = "hello world". 

các "hello world" là thực sự được lưu trữ trong bộ nhớ chỉ đọc.

Tôi không rõ ràng về bộ nhớ chỉ đọc. Giải thích là gì? Có phải đó giống như một lá cờ để trình biên dịch nói với trình biên dịch để không viết vào phần đó?

+1

Bạn có một tài liệu tham khảo? Tôi nghĩ bạn có thể có nghĩa là: const char * s = "hello world". –

+3

Không rõ là tất cả kiến ​​trúc bộ vi xử lý đều hỗ trợ bộ nhớ được bảo vệ. – jldupont

+1

@James Black: OP rõ ràng là nói về chuỗi chữ "hello world", mà thực sự có thể được lưu trữ trong bộ nhớ chỉ đọc, bất kể con trỏ được khai báo như thế nào. – AnT

Trả lời

27

Đó không phải là một tính năng của C language nhưng một tính năng của trình biên dịch/linker và hệ điều hành làm việc cùng nhau.

Khi bạn biên dịch mã ví dụ sau đây sẽ xảy ra:

  • Trình biên dịch sẽ đưa chuỗi thành một dữ liệu phần read-only.

  • Trình liên kết thu thập tất cả dữ liệu trong các phần chỉ đọc đó và đặt chúng vào một phân đoạn duy nhất. Phân đoạn này nằm trong tệp thi hành và được gắn cờ với thuộc tính "chỉ đọc".

  • Bây giờ, có bộ nạp thực thi của hệ điều hành. Nó tải thực thi (hoặc ánh xạ nó vào bộ nhớ chính xác hơn). Khi việc này hoàn tất, trình tải sẽ chuyển các phần và đặt quyền truy cập cho mỗi phân đoạn. Đối với phân đoạn dữ liệu chỉ đọc, rất có thể sẽ vô hiệu hóa việc thực thi mã và ghi truy cập. Mã (ví dụ, các hàm của bạn) có quyền thực thi nhưng không có quyền ghi. Dữ liệu thông thường như biến tĩnh được truy cập đọc và ghi và vân vân ...

Giờ đây hệ thống vận hành hiện đại đã làm điều đó.

Như đã nói, đây không phải là tính năng của ngôn ngữ C. Nếu bạn biên dịch cùng một vấn đề cho DOS, ví dụ, chương trình sẽ chạy nhưng không có khả năng bảo vệ ghi nào là có thể, bởi vì trình nạp DOS không biết về các phần chỉ đọc.

+0

Biến không đổi cũng có đặt cùng một phần với "hello world" không? (Ví dụ: const int a = 6) – root

+0

"Nó phụ thuộc". Điều duy nhất bạn có thể nói chắc chắn về một const int là trình biên dịch sẽ tạo ra một thông báo chẩn đoán nếu bạn cố sửa đổi nó. Nó có thể được lưu trữ trong một phần chỉ đọc, hoặc nó có thể không bao giờ được lưu trữ ở tất cả, và mã hóa trực tiếp như là một hằng số vào các hướng dẫn sử dụng nó. –

+0

@tsubasa - có thể không, và chắc chắn không phải nếu 'a' là địa phương. Nhưng dù câu trả lời là gì, nó cũng sẽ phụ thuộc vào hệ điều hành và bộ nạp. –

3

Bộ nhớ chỉ đọc thực sự được thực hiện bởi hệ thống con bộ nhớ của hệ điều hành. Hệ điều hành có thể đánh dấu một số trang nhất định là chỉ đọc.

Trong tệp nhị phân, trình biên dịch có thể cho hệ điều hành biết các phần nào của tệp thi hành nên được đặt trong các trang bộ nhớ đọc chỉ đọc và ghi.

+1

hmmm ... bảo vệ bộ nhớ thực được thực hiện ở cấp độ bộ vi xử lý. – jldupont

+0

@jldupont: Việc bảo vệ bộ nhớ thực sự được tăng cường ở cấp độ phần cứng (ít nhất là trong x86), nhưng thiết lập ban đầu được thực hiện bởi hệ điều hành, tức lànó là hệ điều hành mà * đánh dấu * các trang chỉ đọc như vậy, và sau đó phần cứng thực thi các dấu chỉ đọc được thiết lập bởi hệ điều hành. – AnT

+0

@AndreyT: tất nhiên ... quan điểm của tôi là liên quan đến @R Samuel. Nếu không có ** phần cứng ** hỗ trợ, có rất nhiều người có thể làm ở cấp phần mềm. – jldupont

6

Thực thi có hai phần: phần .data, chứa các biến toàn cầu và phần .text, chứa mã máy thực tế.

Chuỗi được đặt vào phần .data. Những gì C làm khi nó thấy "Hello world" là nó đặt chuỗi "Hello world" vào bản thân thực thi, và thay thế cá thể của "Hello world" trong chương trình với địa chỉ mà chuỗi đó kết thúc được nạp.

Có nói rằng, tôi không chắc chắn lý do tại sao nó chỉ đọc - về mặt lý thuyết một chương trình có thể thay đổi bộ nhớ riêng của mình ..

+2

Không phải tất cả các bộ xử lý và hệ điều hành đều hỗ trợ mã tự sửa đổi. Trên thực tế, hầu hết các hệ điều hành hiện đại đều có bảo vệ chống lại mã tự sửa đổi, như một tính năng bảo mật. – Crashworks

+0

Chuỗi ký tự, vì chúng không cần phải sửa đổi, có thể và thường được lưu trữ trong phần văn bản. – caf

2

Ví dụ về cách thực hiện việc này trong Linux là trên page 179 of Advanced Linux Programming bởi Mark Mitchell, Jeffrey Olham và Alex Samuel.

+0

Cuốn sách tuyệt vời đó là .. Ngay cả sau 2 năm :) ... – bsoundra

0

Bạn có thể thử một cái gì đó giống như

s[4] = '0'; 

và xem nếu nó nói "hello w0rld" khi bạn gọi

puts(s); 

Nếu nó gây ra một lỗi phân khúc ngay lập tức hoặc một phòng chống ngoại lệ Data Execution sau đó nó có lẽ chỉ đọc. (Nếu hệ thống cho phép bạn tránh xa nó, điều đó không làm cho nó trở thành một ý tưởng hay.)

1

Như những người khác đã đề cập, nội dung của chuỗi không đổi được lưu trữ trong bộ nhớ chỉ đọc được xác định bởi hoạt động kiến trúc hệ thống, trình biên dịch và chip.

Chính xác hơn, tiêu chuẩn C chỉ định rằng các chuỗi được trích dẫn được coi là có loại "const char []" (hoặc các từ với hiệu ứng đó, tôi không có sẵn tiêu chuẩn).

Bất kỳ mã nào cố gắng sửa đổi nội dung của chuỗi như vậy đều gọi hành vi không xác định. Điều đó có nghĩa rằng bất cứ điều gì nghĩa đen có thể xảy ra tại thời điểm đó, và các nhà cung cấp của trình biên dịch thậm chí không cần thiết để ghi lại những gì có thể xảy ra.

Trong thực tế, điều này có nghĩa là chương trình C hoặc C++ muốn di chuyển phải tránh thay đổi các chuỗi không đổi.

Nói chung, trình biên dịch sẽ không cho phép bạn sửa đổi nội dung của biến "const", vì vậy bạn có thể xem "const" có nghĩa là "chỉ đọc" trong hầu hết các trường hợp. Thật không may, có một ngoại lệ đặc biệt cho char * và const char *, phần lớn vì lý do lịch sử. Điều đó có nghĩa là mã như sau:

char *x = "Hello, World"; 
*x = 'h'; 

sẽ biên dịch mà không có lỗi hoặc cảnh báo, mặc dù nó gọi hành vi không xác định.

0

Khi bạn viết char s[10]="sneha"; bạn đang phân bổ 10 byte dung lượng lưu trữ (không phải bộ nhớ, bộ nhớ chỉ đưa vào hình ảnh khi u r thực hiện chương trình của bạn) trong tệp đối tượng của bạn. Đây là phân bổ bộ nhớ tĩnh (tại thời gian biên dịch).

Nhưng khi bạn viết char *s="sneha"; bạn không phân bổ bất kỳ dung lượng lưu trữ nào để lưu trữ "sneha". Nó sẽ được lưu trữ trong phần READ ONLY. Nhưng con trỏ s được lưu trữ trong phần khác nhau dựa trên nơi nó được khai báo. Nhưng nó chỉ vào DỮ LIỆU CHỈ ĐỌC chỉ "sneha". Vì vậy, nếu bạn cố gắng viết trên nó, bạn sẽ nhận được lỗi phân đoạn.

Ví dụ:

char *s[10]="sneha"; 
s[1]="N"; 
printf("%s",s); // you expecting output sNeha, 
       // but you get a seg fault since it is ONLY DATA 
Các vấn đề liên quan