2010-08-26 32 views
25

Bất cứ ai có thể giải thích cho tôi những gợi ý này bằng một ví dụ phù hợp ... và khi các con trỏ này được sử dụng?Con trỏ gần, xa và rất lớn?

+7

có mùi giống như bài tập về nhà – Spence

+0

đặc biệt dành cho lứa tuổi teen "any1" –

+2

Tôi nhớ những điều đó trong Turbo-C, trước đây ... – 0x6adb015

Trả lời

11

Trong những ngày cũ, theo hướng dẫn sử dụng Turbo C, con trỏ gần chỉ là 16 bit khi toàn bộ mã và dữ liệu của bạn vừa với một đoạn. Một con trỏ xa bao gồm một phân đoạn cũng như một bù trừ nhưng không có sự chuẩn hóa nào được thực hiện. Và một con trỏ khổng lồ được tự động chuẩn hóa. Hai con trỏ xa có thể hình dung đến cùng một vị trí trong bộ nhớ nhưng khác nhau trong khi các con trỏ lớn được chuẩn hóa trỏ đến cùng một vị trí bộ nhớ sẽ luôn bằng nhau.

+0

cảm ơn .. bt u có thể cho tôi biết ví dụ .. vì vậy họ có khả năng hiện tại cũng có. –

+7

@Vishwanath: Không, chúng không thực sự có thể sử dụng được mã mới. Chúng chỉ dành cho các nền tảng Intel 16 bit, vốn đã lỗi thời một thời gian dài trước đây (Tôi tin rằng Intel 386 là chip đầu tiên hỗ trợ mô hình bộ nhớ 32 bit hiệu quả). Nếu bạn đang viết mã phải quan tâm đến điều này, bạn đang viết mã kế thừa. –

+0

cảm ơn PP và Billy .. tôi hiểu rồi nw. –

2

Thuật ngữ này được sử dụng trong kiến ​​trúc 16 bit.

Trong các hệ thống 16 bit, dữ liệu được phân đoạn thành các phân đoạn 64Kb. Mỗi mô-đun có thể tải (tệp chương trình, thư viện được tải động, v.v.) có một đoạn dữ liệu liên quan - có thể lưu trữ tối đa 64Kb dữ liệu.

Con trỏ NEAR là con trỏ có bộ nhớ 16 bit và được tham chiếu đến dữ liệu (chỉ) trong phân đoạn dữ liệu mô-đun hiện tại.

Các chương trình 16bit có hơn 64Kb dữ liệu theo yêu cầu có thể truy cập các trình phân bổ đặc biệt sẽ trả về con trỏ FAR - id phân đoạn dữ liệu trong 16 bit trên và con trỏ vào phân đoạn dữ liệu đó 16 bit.

Các chương trình lớn hơn sẽ muốn xử lý hơn 64Kb dữ liệu liền kề. Một con trỏ HUGE trông giống như một con trỏ xa - nó có bộ nhớ 32 bit - nhưng bộ cấp phát đã cẩn thận sắp xếp một loạt các phân đoạn dữ liệu, với các ID liên tiếp, do đó đơn giản là tăng bộ chọn đoạn dữ liệu, dữ liệu 64Kb tiếp theo có thể đạt được.

C cơ bản và tiêu chuẩn ngôn ngữ C++ bao giờ thực sự nhận ra những khái niệm chính thức trong các mô hình bộ nhớ của họ - tất cả các con trỏ trong một chương trình C hoặc C++ có nghĩa vụ phải có cùng kích thước. Vì vậy, các thuộc tính NEAR, FAR và HUGE là các phần mở rộng được cung cấp bởi các nhà cung cấp trình biên dịch khác nhau.

3

Tất cả nội dung trong câu trả lời này chỉ liên quan đến mô hình bộ nhớ phân đoạn 8086 và 80286 cũ.

gần: một con trỏ 16 bit có thể giải quyết bất kỳ byte trong một phân đoạn 64k

xa: một con trỏ 32 bit có chứa một phân đoạn và một bù đắp. Lưu ý rằng vì các phân đoạn có thể trùng lặp, hai con trỏ xa khác nhau có thể trỏ đến cùng một địa chỉ.

khổng lồ: một con trỏ 32 bit, trong đó phân khúc này là "bình thường" để không có hai con trỏ xa trỏ đến cùng một địa chỉ, trừ khi họ có cùng giá trị.

tee: đồ uống có mứt và bánh mì.

Điều đó sẽ đưa chúng ta trở lại doh oh oh oh

và khi các con trỏ được sử dụng?

vào những năm 1980 và 90' cho đến khi 32 bit của Windows trở nên phổ biến,

+0

^^ cuối cùng có nội dung ngắn gọn! –

28

Ví dụ chính là kiến ​​trúc Intel X86.

Intel 8086 là, trong nội bộ, một bộ xử lý 16-bit: tất cả các thanh ghi của nó là 16 bit rộng. Tuy nhiên, bus địa chỉ rộng 20 bit (1 MiB).Điều này có nghĩa rằng bạn không thể giữ toàn bộ địa chỉ trong sổ đăng ký, giới hạn bạn với 64 kiB đầu tiên.

Giải pháp của Intel là tạo 16 thanh ghi phân đoạn "có nội dung sẽ được dịch chuyển sang trái bốn bit và được thêm vào địa chỉ. Ví dụ:

DS ("Data Segment") register: 1234 h 
DX ("D eXtended") register: + 5678h 
           ------ 
Actual address read:   179B8h 

Điều này tạo ra khái niệm phân đoạn 64 kiB. Do đó một con trỏ "gần" sẽ chỉ là nội dung của thanh ghi DX (5678h), và sẽ không hợp lệ trừ khi thanh ghi DS đã được đặt chính xác, trong khi con trỏ "xa" là 32 bit (12345678h, DS theo sau là DX) và sẽ luôn hoạt động (nhưng chậm hơn vì bạn phải tải hai thanh ghi và sau đó khôi phục thanh ghi DS khi hoàn thành).

(Như supercat lưu ý dưới đây, một bù đắp cho DX mà tràn sẽ "cuộn qua" trước được thêm vào DS để có được địa chỉ chính thức. Điều này cho phép offsets 16-bit để truy cập bất kỳ địa chỉ trong phân khúc 64 KiB, không chỉ một phần là ± 32 kiB từ nơi DX chỉ, như được thực hiện trong các kiến ​​trúc khác với địa chỉ bù tương đối 16 bit trong một số hướng dẫn.)

Tuy nhiên, lưu ý rằng bạn có thể có hai con trỏ "xa" các giá trị khác nhau nhưng trỏ đến cùng một địa chỉ. Ví dụ, con trỏ xa 100079B8h trỏ đến cùng một vị trí như 12345678h. Vì vậy, so sánh con trỏ trên con trỏ xa là một hoạt động không hợp lệ: các con trỏ có thể khác nhau, nhưng vẫn trỏ đến cùng một vị trí.

Đây là nơi tôi quyết định rằng máy Mac (với bộ vi xử lý Motorola 68000 vào thời điểm đó) không quá tệ sau khi tất cả, vì vậy tôi bỏ lỡ những con trỏ rất lớn. IIRC, chúng chỉ là các con trỏ xa đảm bảo rằng tất cả các bit chồng chéo trong phân đoạn đăng ký là 0, như trong ví dụ thứ hai. Motorola không gặp vấn đề gì với 6800 bộ vi xử lý của họ, vì chúng chỉ giới hạn ở 64 kiB, khi họ tạo ra kiến ​​trúc 68000, họ đã đi thẳng tới các thanh ghi 32 bit, và do đó không bao giờ cần gần, xa hoặc con trỏ lớn. (Thay vào đó, vấn đề của họ là chỉ 24 bit dưới cùng của địa chỉ thực sự quan trọng, vì vậy một số lập trình viên (nổi tiếng là Apple) sẽ sử dụng 8 bit cao như "cờ con trỏ", gây ra vấn đề khi xe buýt địa chỉ mở rộng tới 32 bit (4 GiB) .)

Linus Torvalds chỉ được giữ cho đến 80386, cung cấp "chế độ được bảo vệ" trong đó địa chỉ là 32 bit và đăng ký phân đoạn là phần cao của địa chỉ và không cần thêm, và đã viết Linux ngay từ đầu để chỉ sử dụng chế độ được bảo vệ, không có phân đoạn kỳ lạ, và đó là lý do tại sao bạn không hỗ trợ con trỏ gần và xa trong Linux (và tại sao không có công ty nào thiết kế kiến ​​trúc mới sẽ quay lại với họ nếu họ muốn hỗ trợ Linux) . Và họ ăn tối của Robin, và có rất nhiều hân hoan. (Yay ...)

+0

Thật thú vị, nhiều chương trình dựa trên 68000 kết thúc với 32K giới hạn ở những nơi mà 8088 phần mềm sẽ có giới hạn 64K. Cho rằng bộ nhớ thường khan hiếm, và 8088 có thể nhận được với số lượng 16 bit ở những nơi 68000 sẽ yêu cầu sử dụng 32 bit hoặc chấp nhận giới hạn 32K, 8088 thực sự là một thiết kế đáng kể. – supercat

+0

"Giới hạn 32k" cho 68k là khi bạn sử dụng bù đắp tương đối 16 bit cho các nhánh và nhảy. Nếu bạn không cố gắng để đặt hàng các chức năng trong phân đoạn mã để cố gắng giữ mọi thứ lại với nhau, bạn chỉ cần từ bỏ và tát giới hạn 32k vào mọi thứ khi bạn có thể có giới hạn 64k. Nó vẫn còn ít thảm họa hơn so với hệ thống của Intel, nơi các con trỏ có giá trị khác nhau thực sự có thể bằng nhau. Một lần nữa, đó là tất cả lịch sử ngay bây giờ, và thậm chí cả các hệ thống nhúng cũng không gặp vấn đề gì khi thực hiện địa chỉ 32 bit ngay trong những ngày này. –

+0

Trên Macintosh cổ điển, nhiều cấu trúc dữ liệu bị giới hạn ở 32K vì hệ điều hành đã chọn sử dụng loại 16 bit cho nhiều mục đích thay vì loại 32 bit; Tôi hy vọng điều tương tự sẽ đúng với rất nhiều phần mềm hạn chế bộ nhớ cho nền tảng 68000 (đặc biệt là các biến thể 16-bit-bus). Hướng dẫn addr + disp16 của 68000 tất cả các dấu hiệu mở rộng chuyển dịch để cho phép cả chuyển tích cực và tiêu cực. Tuy nhiên, 8088 có thể cho phép dịch chuyển 16 bit đạt +/- 65535 byte trong một đối tượng duy nhất, thay vì +/- 32767 byte, nếu bù đắp phù hợp trong một phân khúc. – supercat

2

Trong một số kiến ​​trúc, một con trỏ có thể trỏ tới mọi đối tượng trong hệ thống sẽ lớn hơn và chậm hơn để làm việc với hơn một cái có thể trỏ đến một tập hợp con hữu ích. Nhiều người đã đưa ra câu trả lời liên quan đến kiến ​​trúc x86 16 bit. Các kiểu con trỏ khác nhau phổ biến trên hệ thống 16 bit, mặc dù sự phân biệt gần/sợ có thể xuất hiện lại trong hệ thống 64 bit, tùy thuộc vào cách chúng được triển khai (tôi sẽ không ngạc nhiên nếu nhiều hệ thống phát triển đi tới con trỏ 64 bit) tất cả mọi thứ, mặc dù thực tế là trong nhiều trường hợp sẽ rất lãng phí). Trong nhiều chương trình, rất dễ chia nhỏ việc sử dụng bộ nhớ thành hai loại: những thứ nhỏ với tổng số lượng khá nhỏ (64K hoặc 4GB) nhưng sẽ được truy cập thường xuyên và những thứ lớn hơn có thể tăng lên. với số lượng lớn hơn nhiều, nhưng không cần phải truy cập thường xuyên. Khi một ứng dụng cần làm việc với một phần của một đối tượng trong vùng "những thứ lớn", nó sẽ sao chép phần đó vào vùng "những thứ nhỏ", làm việc với nó, và nếu cần thiết hãy viết nó trở lại.

Một số lập trình viên có thể phân biệt giữa bộ nhớ "gần" và "xa", nhưng trong nhiều trường hợp, việc phân biệt này có thể cho phép trình biên dịch tạo ra mã tốt hơn nhiều.

(lưu ý: Ngay cả trên nhiều hệ thống 32 bit, một số khu vực nhất định của bộ nhớ có thể được truy cập trực tiếp mà không cần thêm hướng dẫn, trong khi các khu vực khác không thể. lưu trữ biến toàn cầu, nó sẽ có thể trực tiếp tải bất kỳ biến nào trong 32K (68000) hoặc 2K (ARM) đầu tiên của thanh ghi đó. Việc tìm nạp một biến được lưu trữ ở nơi khác sẽ yêu cầu một hướng dẫn bổ sung để tính toán địa chỉ. tại các khu vực ưu tiên và cho phép trình biên dịch biết sẽ cho phép hệ mã hiệu quả hơn

+0

Điều đó có nghĩa là con trỏ khổng lồ nào được tự động chuẩn hóa trong khi con trỏ xa không? Từ ** "normalize" ** có nghĩa là gì ở đây? – Destructor

+0

@Destructor: Mỗi con trỏ trên 8086 có hai bộ phận 16 bit - một phân đoạn khó xử lý, và một phần bù có thể được thao tác thuận tiện hơn nhiều. Các địa chỉ phần cứng được thực hiện bằng cách nhân phân đoạn với 16 và thêm phần bù. Đối tượng lên đến 65536 byte được căn chỉnh trên ranh giới 16 byte có thể dễ dàng thao tác bằng cách thiết lập phân đoạn để nó xác định khởi đầu của đối tượng và sau đó sử dụng bù đắp để truy cập vị trí bên trong nó, nhưng thực tế là mỗi vị trí có thể được xác định 4096 các cách khác nhau đôi khi có thể là vấn đề. – supercat

+0

@Destructor: Bình thường hóa một con trỏ có nghĩa là thay thế nó bằng một con trỏ xác định cùng một vị trí vật lý nhưng có độ lệch trong khoảng 0-15. Đối với một số mẫu sử dụng, các toán tử quan hệ và số học con trỏ bỏ qua phân đoạn hoàn toàn sẽ tuân thủ chuẩn C và sẽ hoạt động, nhưng có nghĩa là có thể có hai con trỏ khác nhau có chênh lệch bằng 0 và không có giá trị nào lớn hơn giá trị kia, nhưng dù sao cũng không bình đẳng và truy cập những thứ khác nhau. Có các toán tử quan hệ xử lý các con trỏ dưới dạng các giá trị 32 bit (với phân đoạn là từ trên) ... – supercat

18

sự khác nhau giữa con trỏ xa và lớn:.

Như chúng ta biết theo mặc định, con trỏ là near ví dụ: int *p là con trỏ near. Kích thước của near con trỏ là 2 byte trong trường hợp trình biên dịch 16 bit. Và chúng tôi đã biết kích thước rất tốt khác nhau trình biên dịch để trình biên dịch; họ chỉ lưu trữ bù đắp của địa chỉ con trỏ nó đang tham chiếu. Một địa chỉ bao gồm chỉ một offset có phạm vi từ 0 - 64K byte.

Farhuge con trỏ:

Farhuge con trỏ có kích thước 4 byte. Chúng lưu trữ cả phân khúc và độ lệch của địa chỉ mà con trỏ đang tham chiếu. Sau đó, sự khác biệt giữa chúng là gì?

Giới hạn của con trỏ đến nay:

Chúng ta không thể thay đổi hoặc sửa đổi địa chỉ phân đoạn của địa chỉ xa được bằng cách áp dụng bất kỳ hoạt động số học trên đó. Đó là bằng cách sử dụng toán tử số học, chúng ta không thể nhảy từ một phân đoạn này sang phân đoạn khác.

Nếu bạn sẽ tăng địa chỉ xa vượt quá giá trị lớn nhất của địa chỉ offset thay vì địa chỉ phân đoạn tăng dần, nó sẽ lặp lại địa chỉ bù của nó theo thứ tự tuần hoàn. Điều này cũng được gọi là gói, tức là nếu bù đắp là 0xffff và chúng tôi thêm 1 thì đó là 0x0000 và tương tự nếu chúng tôi giảm 0x0000 xuống 1 thì đó là 0xffff và nhớ rằng không có thay đổi trong phân khúc.

Bây giờ tôi sẽ so sánh rất lớn và xa con trỏ:

1. Khi một con trỏ đến nay được tăng lên hoặc giảm đi CHỈ sự bù đắp của con trỏ là thực sự tăng lên hoặc giảm đi nhưng trong trường hợp của con trỏ lớn cả giá trị phân khúc và giá trị offset sẽ thay đổi.

Hãy xem xét ví dụ sau, lấy từ HERE:

int main() 
    { 
    char far* f=(char far*)0x0000ffff; 
    printf("%Fp",f+0x1); 
    return 0; 
    } 

sau đó đầu ra là:

0000:0000 

Không có thay đổi về giá trị phân khúc.

Và trong trường hợp con trỏ lớn:

int main() 
{ 
char huge* h=(char huge*)0x0000000f; 
printf("%Fp",h+0x1); 
return 0; 
} 

Đầu ra là:

0001:0000 

Điều này là do hoạt động tăng không chỉ có giá trị bù đắp nhưng giá trị phân khúc cũng change.That nghĩa phân khúc sẽ không thay đổi trong trường hợp của far con trỏ nhưng trong trường hợp con trỏ huge, con trỏ có thể di chuyển từ phân đoạn này sang đoạn khác.

2.Khi các toán tử quan hệ được sử dụng trên các con trỏ xa, chỉ các phép so sánh được so sánh. Các từ khác sẽ chỉ hoạt động trên các con trỏ xa nếu giá trị phân đoạn của con trỏ được so sánh giống nhau. Và trong trường hợp khổng lồ này sẽ không xảy ra, trên thực tế so sánh các địa chỉ tuyệt đối mất place.Let chúng ta hiểu với sự giúp đỡ của một ví dụ về far con trỏ:

int main() 
{ 
char far * p=(char far*)0x12340001; 
char far* p1=(char far*)0x12300041; 
if(p==p1) 
printf("same"); 
else 
printf("different"); 
return 0; 
} 

Output:

different 

Trong huge con trỏ :

int main() 
{ 
char huge * p=(char huge*)0x12340001; 
char huge* p1=(char huge*)0x12300041; 
if(p==p1) 
printf("same"); 
else 
printf("different"); 
return 0; 
} 

Output:

same 

Giải thích: Như chúng ta thấy địa chỉ tuyệt đối cho cả pp112341 (1234*10+1 hoặc 1230*10+41) nhưng họ không được coi là bình đẳng trong trường hợp 1 vì trong trường hợp far con trỏ chỉ offsets được so sánh tức là nó sẽ kiểm tra xem 0001==0041. Đó là sai.

Và trong trường hợp các con trỏ lớn, thao tác so sánh được thực hiện trên các địa chỉ tuyệt đối bằng nhau.

  1. Con trỏ xa không bao giờ được chuẩn hóa nhưng con trỏ huge được chuẩn hóa. Con trỏ chuẩn hóa là con trỏ có nhiều địa chỉ nhất có thể trong phân đoạn, có nghĩa là chênh lệch không bao giờ lớn hơn 15.

    giả sử chúng ta có 0x1234:1234 thì dạng chuẩn hóa là 0x1357:0004 (địa chỉ tuyệt đối là 13574)). Một con trỏ khổng lồ được chuẩn hóa chỉ khi một số thao tác số học được thực hiện trên nó, và không được chuẩn hóa trong khi gán.

    int main() 
    { 
        char huge* h=(char huge*)0x12341234; 
        char huge* h1=(char huge*)0x12341234; 
        printf("h=%Fp\nh1=%Fp",h,h1+0x1); 
        return 0; 
    } 
    

    Output:

    h=1234:1234 
    
    h1=1357:0005 
    

    Giải thích: huge con trỏ là không bình thường trong trường hợp assignment.But nếu một hoạt động số học được thực hiện trên nó, nó sẽ được normalized.So, h1234:1234h11357:0005 được chuẩn hóa.

    4. bù đắp của con trỏ khổng lồ nhỏ hơn 16 vì bình thường hóa và không phải như vậy trong trường hợp con trỏ xa.

    cho phép lấy một ví dụ để hiểu những gì tôi muốn nói:

    int main() 
        { 
        char far* f=(char far*)0x0000000f; 
        printf("%Fp",f+0x1); 
        return 0; 
        } 
    

Output:

0000:0010 

Trong trường hợp huge con trỏ:

 int main() 
     { 
     char huge* h=(char huge*)0x0000000f; 
     printf("%Fp",h+0x1); 
     return 0; 
     } 

     Output: 
     0001:0000 

Giải thích: như chúng ta nt xa con trỏ bởi 1 nó sẽ là 0000:0010. Và khi chúng ta tăng con trỏ rất lớn bằng 1 thì nó sẽ là 0001:0000 bởi vì nó bù không thể lớn hơn 15 nói cách khác nó sẽ được chuẩn hóa.

+1

+1 cho nội dung. Đảm bảo định dạng câu trả lời của bạn đúng vào lần tiếp theo. –

+0

Cuối cùng tôi đã hiểu được sự khác biệt. Cảm ơn bạn. – hkBattousai

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