Ít nhất trong trường hợp thư viện tĩnh, bạn có thể làm việc xung quanh nó khá thuận tiện.
Hãy xem xét các tiêu đề thư viện foo và bar . Vì lợi ích của hướng dẫn này, tôi cũng sẽ cung cấp cho bạn các tệp nguồn
ví dụ / ex01 / foo.h
int spam(void);
double eggs(void);
example / ex01 / foo.c (cái này có thể không rõ ràng / không khả dụng)
int the_spams;
double the_eggs;
int spam()
{
return the_spams++;
}
double eggs()
{
return the_eggs--;
}
ví dụ / ex01 / bar.h
int spam(int new_spams);
double eggs(double new_eggs);
example / ex01 / bar.c (cái này có thể không rõ ràng / không khả dụng)
int the_spams;
double the_eggs;
int spam(int new_spams)
{
int old_spams = the_spams;
the_spams = new_spams;
return old_spams;
}
double eggs(double new_eggs)
{
double old_eggs = the_eggs;
the_eggs = new_eggs;
return old_eggs;
}
Chúng tôi muốn sử dụng chúng trong foobar chương trình
example / ex01 / foobar.c
#include <stdio.h>
#include "foo.h"
#include "bar.h"
int main()
{
const int new_bar_spam = 3;
const double new_bar_eggs = 5.0f;
printf("foo: spam = %d, eggs = %f\n", spam(), eggs() );
printf("bar: old spam = %d, new spam = %d ; old eggs = %f, new eggs = %f\n",
spam(new_bar_spam), new_bar_spam,
eggs(new_bar_eggs), new_bar_eggs );
return 0;
}
Một vấn đề trở nên rõ ràng ngay lập tức: C không biết quá tải. Vì vậy, chúng ta có hai lần hai hàm có tên giống hệt nhau nhưng khác chữ ký. Vì vậy, chúng ta cần một số cách để phân biệt chúng. Dù sao, hãy xem trình biên dịch phải nói gì về điều này:
example/ex01/ $ make
cc -c -o foobar.o foobar.c
In file included from foobar.c:4:
bar.h:1: error: conflicting types for ‘spam’
foo.h:1: note: previous declaration of ‘spam’ was here
bar.h:2: error: conflicting types for ‘eggs’
foo.h:2: note: previous declaration of ‘eggs’ was here
foobar.c: In function ‘main’:
foobar.c:11: error: too few arguments to function ‘spam’
foobar.c:11: error: too few arguments to function ‘eggs’
make: *** [foobar.o] Error 1
Được rồi, điều này không có gì ngạc nhiên, nó chỉ cho chúng tôi biết, những gì chúng tôi đã biết, hoặc ít nhất là nghi ngờ.
Vì vậy, bằng cách nào đó chúng ta có thể giải quyết xung đột nhận dạng đó mà không cần sửa đổi mã nguồn hoặc tiêu đề của thư viện ban đầu không? Trong thực tế, chúng tôi có thể.
Đầu tiên, hãy giải quyết các vấn đề về thời gian biên dịch. Đối với điều này, chúng tôi bao quanh tiêu đề bao gồm một loạt các #define
chỉ thị tiền xử lý có tiền tố tất cả các ký hiệu được xuất bởi thư viện. Sau đó, chúng tôi thực hiện điều này với một số tiêu đề wrapper ấm cúng đẹp mắt, nhưng chỉ vì mục đích chứng minh những gì đang diễn ra đã làm nó nguyên văn trong tệp nguồn foobar.c :
ví dụ / ex02 / foobar.c
#include <stdio.h>
#define spam foo_spam
#define eggs foo_eggs
# include "foo.h"
#undef spam
#undef eggs
#define spam bar_spam
#define eggs bar_eggs
# include "bar.h"
#undef spam
#undef eggs
int main()
{
const int new_bar_spam = 3;
const double new_bar_eggs = 5.0f;
printf("foo: spam = %d, eggs = %f\n", foo_spam(), foo_eggs() );
printf("bar: old spam = %d, new spam = %d ; old eggs = %f, new eggs = %f\n",
bar_spam(new_bar_spam), new_bar_spam,
bar_eggs(new_bar_eggs), new_bar_eggs );
return 0;
}
Bây giờ nếu chúng ta biên dịch cái này ...
example/ex02/ $ make
cc -c -o foobar.o foobar.c
cc foobar.o foo.o bar.o -o foobar
bar.o: In function `spam':
bar.c:(.text+0x0): multiple definition of `spam'
foo.o:foo.c:(.text+0x0): first defined here
bar.o: In function `eggs':
bar.c:(.text+0x1e): multiple definition of `eggs'
foo.o:foo.c:(.text+0x19): first defined here
foobar.o: In function `main':
foobar.c:(.text+0x1e): undefined reference to `foo_eggs'
foobar.c:(.text+0x28): undefined reference to `foo_spam'
foobar.c:(.text+0x4d): undefined reference to `bar_eggs'
foobar.c:(.text+0x5c): undefined reference to `bar_spam'
collect2: ld returned 1 exit status
make: *** [foobar] Error 1
... có vẻ như mọi thứ trở nên tồi tệ hơn. Nhưng hãy nhìn kỹ lại: Trên thực tế, giai đoạn biên dịch diễn ra tốt đẹp. Nó chỉ là trình liên kết hiện đang phàn nàn rằng có các biểu tượng va chạm và nó cho chúng ta biết vị trí (tệp nguồn và dòng) nơi điều này xảy ra. Và như chúng ta có thể thấy những biểu tượng đó không được định sẵn.
Hãy cùng xem các bảng ký hiệu với tiện ích nm :
example/ex02/ $ nm foo.o
0000000000000019 T eggs
0000000000000000 T spam
0000000000000008 C the_eggs
0000000000000004 C the_spams
example/ex02/ $ nm bar.o
0000000000000019 T eggs
0000000000000000 T spam
0000000000000008 C the_eggs
0000000000000004 C the_spams
Vì vậy, bây giờ chúng ta được thử thách với bài tập đặt tiền tố các ký hiệu đó trong một số nhị phân không rõ ràng. Vâng, tôi biết trong quá trình ví dụ này, chúng tôi có các nguồn và có thể thay đổi điều này ở đó. Nhưng hiện tại, hãy giả sử bạn chỉ có các tệp .o hoặc .a (thực ra chỉ là một loạt .o ).
phản đối sự giải cứu
Có một công cụ đặc biệt thú vị đối với chúng tôi: mục tiêu
objcopy hoạt động trên các tệp tạm thời, vì vậy chúng tôi có thể sử dụng nó như thể nó đang hoạt động tại chỗ. Có một tùy chọn / thao tác được gọi là - ký hiệu tiền tố và bạn có 3 lần đoán nó hoạt động.
Vì vậy, hãy ném cái này vào các thư viện cứng đầu của chúng ta:
example/ex03/ $ objcopy --prefix-symbols=foo_ foo.o
example/ex03/ $ objcopy --prefix-symbols=bar_ bar.o
nm cho chúng ta thấy rằng điều này dường như hoạt động:
example/ex03/ $ nm foo.o
0000000000000019 T foo_eggs
0000000000000000 T foo_spam
0000000000000008 C foo_the_eggs
0000000000000004 C foo_the_spams
example/ex03/ $ nm bar.o
000000000000001e T bar_eggs
0000000000000000 T bar_spam
0000000000000008 C bar_the_eggs
0000000000000004 C bar_the_spams
Hãy thử liên kết toàn bộ điều này:
example/ex03/ $ make
cc foobar.o foo.o bar.o -o foobar
Và thực sự, nó đã hoạt động:
example/ex03/ $ ./foobar
foo: spam = 0, eggs = 0.000000
bar: old spam = 0, new spam = 3 ; old eggs = 0.000000, new eggs = 5.000000
Bây giờ tôi để nó như một bài tập cho người đọc để thực hiện một công cụ / tập lệnh tự động trích xuất các ký hiệu của thư viện bằng cách sử dụng nm , viết tệp tiêu đề trình bao bọc của cấu trúc
#define spam foo_spam
#define eggs foo_eggs
#include <foo.h>
#undef spam
#undef eggs
và áp dụng tiền tố biểu tượng cho các tệp đối tượng của thư viện tĩnh bằng cách sử dụng objcopy .
Điều gì về thư viện chia sẻ?
Về nguyên tắc, điều tương tự cũng có thể được thực hiện với các thư viện dùng chung. Tuy nhiên, cái tên đã nói lên rằng các thư viện được chia sẻ được chia sẻ giữa nhiều chương trình, vì vậy việc xáo trộn một thư viện được chia sẻ theo cách này không phải là một ý kiến hay.
Bạn sẽ không phải lo lắng khi viết một tấm bạt lò xo. Tệ hơn nữa là bạn không thể liên kết với thư viện được chia sẻ ở cấp tệp đối tượng mà buộc phải thực hiện tải động. Nhưng điều này xứng đáng với bài viết của riêng nó.
Hãy theo dõi và viết mã vui vẻ.