2015-11-17 14 views
10
  • Without #include<ctype.h>, nghiệm thu sản phẩm sau 1 và 0.
  • Với sự bao gồm, nó sẽ tạo ra 1 và 1.

Tôi đang sử dụng TDM -GCC 4.9.2 64-bit. Tôi tự hỏi triển khai của isdigit là gì trong trường hợp đầu tiên và lý do tại sao nó có thể liên kết.Tại sao tôi có thể liên kết mà không bao gồm ctype.h

#include<stdio.h> 
//#include<ctype.h> 
int main() 
{ 
    printf("%d %d\n",isdigit(48),isdigit(48.4)); 
    return 0; 
} 
+0

Có thể là examplepe.h redefines isdigit dưới dạng macro không? –

+0

@WouterHuysentruit nope, tôi không giấu bất cứ điều gì. Hơn nữa, nếu bạn chỉ cần sao chép chương trình và dán vào ideone có vẻ như biên dịch và liên kết tốt trên GCC 5.1, nhưng nó bị treo. – user1537366

Trả lời

6

Theo mặc định GCC sử dụng tiêu chuẩn C90 (với phần mở rộng GNU (reference)) cho phép khai báo ngầm. Vấn đề với trường hợp của bạn là bạn có hai cuộc gọi đến isdigit với hai đối số khác nhau có thể gây nhầm lẫn trình biên dịch khi nó tạo ra khai báo tiềm ẩn của hàm và có thể chọn int isdigit(double) ở bên an toàn. Đó là tất nhiên nguyên mẫu sai cho hàm, có nghĩa là khi hàm thư viện được gọi vào thời gian chạy nó sẽ được gọi với các đối số sai và bạn sẽ có hành vi không xác định.

Khi bạn đưa file <ctype.h> tiêu đề, có một nguyên mẫu chính xác, và sau đó trình biên dịch biết rằng isdigit có một đối int và có thể chuyển đổi các double đen 48.4 đến số nguyên 48 cho cuộc gọi.


Đối với lý do tại sao nó được liên kết, đó là bởi vì trong khi các chức năng này có thể được thực hiện như các macro, đó không phải là một yêu cầu. Điều một yêu cầu là các chức năng đó, ít nhất là trong tiêu chuẩn C11 (hiện tại tôi không có bất kỳ phiên bản cũ nào), phải nhận thức được miền địa phương hiện tại sẽ làm cho việc triển khai macro trở nên khó khăn hơn nhiều, và dễ dàng hơn nhiều như các chức năng thư viện thông thường. Và như là thư viện chuẩn luôn luôn được liên kết (trừ khi bạn nói với GCC nếu không) các chức năng sẽ có sẵn.

2

Đầu tiên của tất cả các câu hỏi #include không liên quan gì đến linking. Hãy nhớ bất cứ điều gì với một # ở phía trước trong C có nghĩa là cho bộ tiền xử lý, không phải trình biên dịch hoặc trình liên kết.

Nhưng điều đó cho biết chức năng phải được liên kết phải không?

Hãy thực hiện các bước theo các bước riêng biệt.

$ gcc -c -Werror --std=c99 st.c 
st.c: In function ‘main’: 
st.c:5:22: error: implicit declaration of function ‘isdigit’ [-Werror=implicit-function-declaration] 
    printf("%d %d\n",isdigit(48),isdigit(48.4)); 
        ^
cc1: all warnings being treated as errors 

Cũng như bạn thấy lint của gcc (máy phân tích tĩnh) đang hoạt động!

Dù chúng tôi sẽ tiến hành bỏ qua nó ...

$ gcc -c --std=c99 st.c 
st.c: In function ‘main’: 
st.c:5:22: warning: implicit declaration of function ‘isdigit’ [-Wimplicit-function-declaration] 
    printf("%d %d\n",isdigit(48),isdigit(48.4)); 

này thời gian chỉ là một cảnh báo. Bây giờ chúng ta có một tệp đối tượng tại thư mục hiện tại. Hãy kiểm tra nó ...

$ nm st.o 
       U isdigit 
0000000000000000 T main 
       U printf 

Như bạn có thể thấy cả hai printfisdigit được liệt kê là không xác định. Vì vậy, mã phải đến từ đâu đó phải không?

hãy tiếp tục liên kết nó ...

$ gcc st.o 
$ nm a.out | grep 'printf\|isdigit' 
       U [email protected]@GLIBC_2.2.5 
       U [email protected]@GLIBC_2.2.5 

Cũng như bạn có thể thấy tình hình được cải thiện nhẹ. Là isdigitprintf không phải là những người cô đơn bất lực như họ đang ở trong số st.o. Bạn có thể thấy cả hai chức năng được cung cấp bởi GLIBC_2.2.5. Nhưng đâu là GLIBC?

Vâng chúng ta hãy xem xét thực thi chính thức hơn một chút ...

$ ldd a.out 
     linux-vdso.so.1 => (0x00007ffe58d70000) 
     libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb66f299000) 
     /lib64/ld-linux-x86-64.so.2 (0x000055b26631d000) 

AHA ... có mà libc. Vì vậy, nó quay ra, mặc dù bạn đã không đưa ra bất kỳ hướng dẫn, liên kết là liên kết với 3 thư viện theo mặc định, một trong số họ là libc có chứa cả hai printfisdigit.

Bạn có thể thấy hành vi mặc định của mối liên kết bởi:

$gcc -dumpspec 
*link: 
%{!r:--build-id} %{!static:--eh-frame-hdr} %{!mandroid|tno-android-ld:%{m16|m32|mx32:;:-m elf_x86_64}     %{m16|m32:-m elf_i386}     %{mx32:-m elf32_x86_64} --hash-style=gnu --as-needed %{shared:-shared} %{!shared:  %{!static:  %{rdynamic:-export-dynamic}  %{m16|m32:-dynamic-linker %{muclibc:/lib/ld-uClibc.so.0;:%{mbionic:/system/bin/linker;:/lib/ld-linux.so.2}}}  %{m16|m32|mx32:;:-dynamic-linker %{muclibc:/lib/ld64-uClibc.so.0;:%{mbionic:/system/bin/linker64;:/lib64/ld-linux-x86-64.so.2}}}  %{mx32:-dynamic-linker %{muclibc:/lib/ldx32-uClibc.so.0;:%{mbionic:/system/bin/linkerx32;:/libx32/ld-linux-x32.so.2}}}}  %{static:-static}};:%{m16|m32|mx32:;:-m elf_x86_64}     %{m16|m32:-m elf_i386}     %{mx32:-m elf32_x86_64} --hash-style=gnu --as-needed %{shared:-shared} %{!shared:  %{!static:  %{rdynamic:-export-dynamic}  %{m16|m32:-dynamic-linker %{muclibc:/lib/ld-uClibc.so.0;:%{mbionic:/system/bin/linker;:/lib/ld-linux.so.2}}}  %{m16|m32|mx32:;:-dynamic-linker %{muclibc:/lib/ld64-uClibc.so.0;:%{mbionic:/system/bin/linker64;:/lib64/ld-linux-x86-64.so.2}}}  %{mx32:-dynamic-linker %{muclibc:/lib/ldx32-uClibc.so.0;:%{mbionic:/system/bin/linkerx32;:/libx32/ld-linux-x32.so.2}}}}  %{static:-static}} %{shared: -Bsymbolic}} 

hai thư viện khác là gì?

Hãy nhớ khi bạn đào vào a.out, cả printfisdigit vẫn được hiển thị là U có nghĩa là không xác định. Nói cách khác, không có địa chỉ memory được liên kết với các biểu tượng này.

Thực tế đây là nơi ma thuật nằm. Các thư viện này thực sự được nạp trong thời gian chạy, không phải trong thời gian liên kết như các hệ thống cũ hơn.

Cách triển khai? Vâng, nó có một biệt ngữ liên kết với, giống như liên kết lười biếng. Những gì nó làm, là khi quá trình gọi một hàm, nếu không có địa chỉ bộ nhớ (phần TEXT), nó tạo ra một Trap (Giống như một Ngoại lệ trong thuật ngữ ngôn ngữ cấp cao, khi điều khiển được chuyển giao cho công cụ ngôn ngữ). Hạt nhân chặn Trap và đưa nó cho bộ tải động tải thư viện và trả về địa chỉ bộ nhớ liên quan đến quá trình người gọi.

Có nhiều lý do lý thuyết, lý do tại sao làm mọi thứ dễ dàng hơn là làm điều đó trước. Tôi đoán đó là một chủ đề hoàn toàn mới, mà chúng ta sẽ thảo luận vào một thời điểm khác.

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