2011-07-11 39 views
18

Vì vậy, tôi đã trải qua K & Phiên bản thứ hai của R đang thực hiện các bài tập. Cảm thấy khá tự tin sau khi thực hiện một số bài tập, tôi nghĩ tôi sẽ kiểm tra việc triển khai thực tế các chức năng này. Đó là sau đó sự tự tin của tôi chạy trốn khỏi hiện trường. Tôi không thể hiểu được nó.Hiểu C cài đặt chức năng thư viện cài sẵn

Ví dụ tôi kiểm tra getchar():

Đây là nguyên mẫu trong libio/stdio.h

extern int getchar (void); 

Vì vậy, tôi theo nó qua nó và nhận được này:

__STDIO_INLINE int 
getchar (void) 
{ 
    return _IO_getc (stdin); 
} 

Một lần nữa tôi theo nó đến số libio/getc.c:

int 
_IO_getc (fp) 
    FILE *fp; 
{ 
    int result; 
    CHECK_FILE (fp, EOF); 
    _IO_acquire_lock (fp); 
    result = _IO_getc_unlocked (fp); 
    _IO_release_lock (fp); 
    return result; 
} 

Và tôi đưa đến một tập tin tiêu đề libio/libio.h, mà là khá khó hiểu:

#define _IO_getc_unlocked(_fp) \ 
     (_IO_BE ((_fp)->_IO_read_ptr >= (_fp)->_IO_read_end, 0) \ 
    ? __uflow (_fp) : *(unsigned char *) (_fp)->_IO_read_ptr++) 

Đó là nơi tôi cuối cùng đã kết thúc cuộc hành trình của tôi.

Câu hỏi của tôi khá rộng. Tất cả điều này có nghĩa là gì? Tôi không thể cho cuộc sống của tôi tìm ra bất cứ điều gì hợp lý ra khỏi nó bằng cách nhìn vào mã. Có vẻ như một loạt các mã đã trừu tượng hóa các lớp sau lớp.

Quan trọng hơn khi nó thực sự có được những nhân vật từ stdin

+3

nó đọc nhân vật khi nó gọi '__uflow() '. –

Trả lời

24

_IO_getc_unlocked là macro không thể khắc phục. Ý tưởng là bạn có thể nhận được một nhân vật từ luồng mà không cần phải gọi một hàm, làm cho nó hy vọng đủ nhanh để sử dụng trong các vòng chặt chẽ, v.v.

Hãy tách riêng từng lớp một. Đầu tiên, _IO_BE là gì?

/usr/include/libio.h:# define _IO_BE(expr, res) __builtin_expect ((expr), res) 

_IO_BE là một gợi ý để trình biên dịch, mà expr sẽ thường đánh giá để res. Nó được sử dụng để cấu trúc lưu lượng mã nhanh hơn khi kỳ vọng là đúng, nhưng không có hiệu ứng ngữ nghĩa khác. Vì vậy, chúng ta có thể thoát khỏi đó, để lại cho chúng ta:

#define _IO_getc_unlocked(_fp) \ 
    (((_fp)->_IO_read_ptr >= (_fp)->_IO_read_end) \ 
    ? __uflow(_fp) : *(unsigned char *)(_fp)->_IO_read_ptr++)) 

Hãy tắt chức năng này vào một hàm nội tuyến cho rõ ràng:

inline int _IO_getc_unlocked(FILE *fp) { 
    if (_fp->_IO_read_ptr >= _fp->_IO_read_end) 
    return __uflow(_fp); 
    else 
    return *(unsigned char *)(_fp->_IO_read_ptr++); 
} 

Nói tóm lại, chúng ta có một con trỏ vào một bộ đệm, và một con trỏ đến cuối bộ đệm. Chúng tôi kiểm tra xem con trỏ có nằm ngoài bộ đệm hay không; nếu không, chúng tôi sẽ tăng nó và trả lại bất kỳ ký tự nào có giá trị cũ. Nếu không, chúng tôi gọi __uflow để nạp bộ đệm và trả về ký tự mới được đọc.

Như vậy, điều này cho phép chúng ta tránh chi phí của một cuộc gọi hàm cho đến khi chúng ta thực sự cần làm IO để nạp bộ đệm đầu vào.

Hãy nhớ rằng các chức năng thư viện chuẩn có thể phức tạp như thế này; họ cũng có thể sử dụng phần mở rộng cho ngôn ngữ C (chẳng hạn như __builtin_expect) KHÔNG chuẩn và có thể KHÔNG hoạt động trên tất cả các trình biên dịch. Họ làm điều này bởi vì họ cần phải được nhanh chóng, và bởi vì họ có thể làm cho các giả định về những gì họ đang sử dụng trình biên dịch. Nói chung mã của riêng bạn không nên sử dụng các phần mở rộng như vậy trừ khi cần thiết, vì nó sẽ làm cho việc chuyển sang các nền tảng khác khó khăn hơn.

+0

Bạn có chắc chắn rằng 'unsigned char *' đúng là kiểu trả về không? Nó không phải là 'int'? –

+0

Ah, có thể. Không bận tâm tìm kiếm loại trả về '__uflow' là gì. Nhưng dù sao, nó chỉ là một ví dụ. – bdonlan

+0

Cảm ơn bạn, rất hữu ích. Tôi chỉ còn một câu hỏi nữa.Làm thế nào để 'char * _IO_read_ptr;' 'char * _IO_read_end;' thiết lập? – saint

1

Lý do có một thư viện chuẩn là bạn không cần phải biết các chi tiết cấy chính xác của các chức năng này. Mã thực hiện các cuộc gọi thư viện tại một số điểm phải sử dụng các cuộc gọi hệ thống không chuẩn mà phải xử lý các vấn đề mà bạn có thể không quan tâm. Nếu bạn đang học C chắc chắn rằng bạn có thể hiểu các chương trình C khác ngoài stdlib một khi bạn nhận được một cái nhìn trước hơn một chút tại stdlib, nhưng nó vẫn sẽ không làm cho rất nhiều ý nghĩa cho đến khi bạn hiểu các cuộc gọi hệ thống liên quan.

0

Định nghĩa của getchar() xác định lại yêu cầu dưới dạng yêu cầu cụ thể cho một ký tự từ stdin.

Định nghĩa của _IO_getc() thực hiện kiểm tra độ tin cậy để đảm bảo rằng TẬP_TIN * tồn tại và không phải là End-Of-File, sau đó nó khóa luồng để ngăn chặn các chủ đề khác làm hỏng cuộc gọi đến _IO_getc_unlocked().

Định nghĩa macro của _IO_getc_unlocked() chỉ cần kiểm tra xem con trỏ đã đọc ở cuối hoặc cuối điểm tệp hay không và cuộc gọi __uflow nếu đúng, hoặc trả về con trỏ tại con trỏ nếu nó không được.

Đây là công cụ chuẩn cho tất cả triển khai stdlib. Bạn không được phép nhìn vào nó. Trong thực tế, nhiều triển khai stdlib sẽ sử dụng ngôn ngữ assembly để xử lý tối ưu, thậm chí còn khó hiểu hơn.

2

Tôi rất khuyên bạn nên The Standard C Library bởi P.J. Plauger. Ông cung cấp nền tảng về các tiêu chuẩn và cung cấp một thực hiện của tất cả các chức năng. Việc triển khai thực hiện đơn giản hơn những gì bạn thấy trong glibc hoặc trình biên dịch C hiện đại, nhưng vẫn sử dụng các macro như số _IO_getc_unlocked() mà bạn đã đăng.

Macro sẽ kéo một ký tự từ dữ liệu đệm (có thể là bộ đệm ungetc) hoặc đọc nó từ luồng (có thể đọc và đệm nhiều byte).

+1

+1 để đề xuất sách. – saint

4

Đi từ pseudo-code để mã thực chúng ta có thể phá vỡ nó xuống:

if (there is a character in the buffer) 
    return (that character) 
else 
    call a function to refill the buffer and return the first character 
end 

Hãy sử dụng the ?: operator:

#define getc(f) (is_there_buffered_stuff(f) ? *pointer++ : refill()) 

Một chút gần gũi hơn:

#define getc(f) (is_there_buffered_stuff(f) ? *f->pointer++ : refill(f)) 

Bây giờ chúng ta gần như ở đó. Để xác định xem có cái gì đó đệm đã có, nó sử dụng con trỏ cấu trúc tập tin và một con trỏ đọc trong bộ đệm

_fp->_IO_read_ptr >= _fp->_IO_read_end ? 

Điều này thực sự kiểm tra các điều kiện đối diện với pseudo-code của tôi, "là bộ đệm trống rỗng", và nếu như vậy, nó gọi __uflow(_fp) // "underflow", nếu không, nó chỉ đạt trực tiếp vào bộ đệm với một con trỏ, được các nhân vật, và sau đó tăng con trỏ:

? __uflow (_fp) : *(unsigned char *) (_fp)->_IO_read_ptr++) 
Các vấn đề liên quan