Loại dữ liệu của các loại dữ liệu mà con trỏ giữ trong ngôn ngữ C là gì?


30

Tôi biết rằng con trỏ giữ địa chỉ. Tôi biết rằng các loại con trỏ "thường" được biết đến dựa trên "loại" dữ liệu mà chúng trỏ đến. Nhưng, con trỏ vẫn là các biến và địa chỉ mà chúng giữ phải có "kiểu" dữ liệu. Theo thông tin của tôi, các địa chỉ ở định dạng thập lục phân. Nhưng, tôi vẫn không biết "loại" dữ liệu nào là hệ thập lục phân này. (Lưu ý rằng tôi biết thập lục phân là gì, nhưng khi bạn nói 10CBA20, ví dụ, đây có phải là chuỗi ký tự không? Số nguyên? Cái gì? Khi tôi muốn truy cập địa chỉ và thao tác với nó .. chính tôi, tôi cần biết loại của nó. là lý do tại sao tôi hỏi.)


17
Con trỏ không phải là biến , mà là giá trị . Các biến giữ các giá trị (và nếu loại của chúng là loại con trỏ, giá trị đó là con trỏ và có thể là địa chỉ của vùng nhớ chứa thứ gì đó có ý nghĩa). Một vùng bộ nhớ nhất định có thể được sử dụng để giữ các giá trị khác nhau của các loại khác nhau.
Basile Starynkevitch

29
"Các địa chỉ ở định dạng thập lục phân" Không, đó chỉ là trình gỡ lỗi hoặc bit định dạng thư viện. Với cùng một đối số, bạn có thể nói chúng ở dạng nhị phân hoặc bát phân.
usr

Bạn nên hỏi về định dạng chứ không phải kiểu . Do đó, một số câu trả lời ngắn gọn bên dưới ... (mặc dù Kilian là tại chỗ).
Cuộc đua nhẹ nhàng với Monica

1
Tôi nghĩ vấn đề sâu xa hơn ở đây là sự hiểu biết về loại hình của OP . Khi nói đến nó, các giá trị bạn thao tác trong chương trình của bạn chỉ là các bit trong bộ nhớ. Các kiểu là cách lập trình viên nói cho trình biên dịch cách xử lý các bit đó khi nó tạo mã lắp ráp.
Justin Lardinois

Tôi cho rằng đã quá muộn để chỉnh sửa nó với tất cả các câu trả lời đó, nhưng câu hỏi này sẽ tốt hơn nếu bạn giới hạn phần cứng và / hoặc hệ điều hành, ví dụ "trên x64 Linux".
hyde

Câu trả lời:


64

Loại của một biến con trỏ là .. con trỏ.

Các thao tác mà bạn chính thức được phép thực hiện trong C là so sánh nó (với các con trỏ khác hoặc giá trị NULL / zero đặc biệt), để thêm hoặc trừ các số nguyên hoặc chuyển nó sang các con trỏ khác.

Khi bạn chấp nhận hành vi không xác định , bạn có thể xem giá trị thực sự là gì. Nó thường sẽ là một từ máy, cùng loại với một số nguyên và thường có thể được truyền một cách dễ dàng đến và từ một loại số nguyên. (Khá nhiều mã Windows thực hiện điều này bằng cách ẩn các con trỏ trong DWORD hoặc HANDLE typedefs).

Có một số kiến ​​trúc mà con trỏ không đơn giản vì bộ nhớ không phẳng. DOS / 8086 'gần' và 'xa'; Bộ nhớ và không gian mã khác nhau của PIC.


2
Bạn cũng được phép lấy sự khác biệt giữa hai con trỏ p1-p2. Kết quả là một giá trị tích phân đã ký. Đặc biệt,&(array[i])-&(array[j]) == i-j
MSalters

13
Trên thực tế, chuyển đổi thành một loại tích phân cũng được chỉ định, cụ thể intptr_tuintptr_tđược đảm bảo là "đủ lớn" cho các giá trị con trỏ.
Matthieu M.

3
Bạn có thể phụ thuộc vào chuyển đổi để làm việc, nhưng ánh xạ giữa các số nguyên và con trỏ được xác định theo triển khai. (Ngoại lệ duy nhất là 0 -> null và thậm chí chỉ được chỉ định nếu 0 là hằng số IIRC.)
cHao

7
Việc bổ sung bộ pxác định vào printf làm cho việc thể hiện con người có thể đọc được của con trỏ void được xác định, nếu hành vi phụ thuộc thực hiện trong c.
dmckee

6
Câu trả lời này có ý kiến ​​chung, nhưng không thành công trên các yêu cầu cụ thể. Việc ép con trỏ vào kiểu tích phân không phải là hành vi không xác định và các kiểu dữ liệu Windows HANDLE không phải là giá trị con trỏ (chúng không phải là con trỏ ẩn trong các kiểu dữ liệu tích phân, chúng là các số nguyên ẩn trong các kiểu con trỏ, để ngăn số học).
Ben Voigt

44

Bạn đang quá phức tạp mọi thứ.

Địa chỉ chỉ là số nguyên, thời gian. Lý tưởng nhất là số lượng ô nhớ được tham chiếu (trong thực tế, điều này trở nên phức tạp hơn do các phân đoạn, bộ nhớ ảo, v.v.).

Cú pháp thập lục phân là một tiểu thuyết hoàn chỉnh chỉ tồn tại để thuận tiện cho các lập trình viên. 0x1A và 26 là cùng một số chính xác cùng loại và cũng không phải là những gì máy tính sử dụng - bên trong, máy tính luôn sử dụng 00011010 (một loạt tín hiệu nhị phân).

Trình biên dịch có cho phép bạn xử lý con trỏ hay không vì các con số phụ thuộc vào định nghĩa ngôn ngữ - ngôn ngữ "lập trình hệ thống" theo truyền thống minh bạch hơn về cách mọi thứ hoạt động dưới mui xe, trong khi các ngôn ngữ "cấp cao" thường cố gắng che giấu kim loại trần từ lập trình viên - nhưng điều đó không thay đổi gì về thực tế rằng con trỏ là số và thường là loại số phổ biến nhất (loại có nhiều bit như kiến ​​trúc bộ xử lý của bạn).


26
Địa chỉ chắc chắn nhất không chỉ là số nguyên. Cũng như các số dấu phẩy động chắc chắn không chỉ là số nguyên.
gnasher729

8
Thật. Ví dụ nổi tiếng nhất là Intel 8086, trong đó con trỏ là hai số nguyên.
MSalters

5
@Rob Trong mô hình bộ nhớ được phân đoạn, một con trỏ có thể là một giá trị đơn (một địa chỉ liên quan đến điểm bắt đầu của phân đoạn; phần bù) với phân đoạn được ngụ ý hoặc phân đoạn / bộ chọn và cặp bù . (Tôi nghĩ Intel đã sử dụng thuật ngữ "bộ chọn"; tôi quá lười để tìm kiếm nó.) Trên 8086, chúng được biểu diễn dưới dạng hai số nguyên 16 bit, kết hợp để tạo thành một địa chỉ vật lý 20 bit. (Có, bạn có thể xử lý cùng một ô nhớ theo nhiều cách khác nhau, nếu bạn đã quá nghiêng: address = (đoạn << 4 + offset) & 0xfffff.) Điều này được chuyển qua tất cả các tương thích x86 khi chạy ở chế độ thực.
một CVn

4
Là một lập trình viên biên dịch chương trình dài hạn, tôi có thể chứng thực rằng bộ nhớ của máy tính không là gì ngoài các vị trí bộ nhớ chứa các số nguyên. Tuy nhiên, đó là cách bạn đối xử với họ và theo dõi những gì các số nguyên đó thể hiện quan trọng. Ví dụ: Trên hệ thống của tôi, số thập phân 4075876853 được lưu dưới dạng x'F2F0F1F5 ', là chuỗi' 2015 'trong EBCDIC. Số thập phân 2015 sẽ được lưu trữ dưới dạng 000007DF trong khi x'0002015C 'đại diện cho số thập phân 2015 ở định dạng thập phân đóng gói. Là một lập trình viên biên dịch chương trình, bạn phải theo dõi những điều này; trình biên dịch thực hiện nó cho các ngôn ngữ HL.
Steve Ives

7
Địa chỉ có thể được đặt thành tương ứng một-một với số nguyên, nhưng mọi thứ khác trên máy tính cũng vậy :)
hobbs

15

Một con trỏ chỉ có thế - một con trỏ. Đó không phải là thứ gì khác. Đừng cố nghĩ rằng đó là một cái gì đó khác.

Trong các ngôn ngữ như C, C ++ và Objective-C, con trỏ dữ liệu có bốn loại giá trị có thể:

  1. Một con trỏ có thể là địa chỉ của một đối tượng.
  2. Một con trỏ có thể chỉ qua phần tử cuối cùng của một mảng.
  3. Một con trỏ có thể là một con trỏ rỗng, có nghĩa là nó không trỏ đến bất cứ thứ gì.
  4. Một con trỏ có thể có một giá trị không xác định, nói cách khác, đó là rác và bất cứ điều gì có thể xảy ra (bao gồm cả những điều xấu) nếu bạn cố gắng sử dụng nó.

Ngoài ra còn có các con trỏ hàm, xác định một hàm hoặc là các con trỏ hàm null hoặc có giá trị không xác định.

Các con trỏ khác là "con trỏ tới thành viên" trong C ++. Đây chắc chắn không phải là địa chỉ bộ nhớ! Thay vào đó, họ xác định một thành viên của bất kỳ trường hợp nào của một lớp. Trong Objective-C, bạn có các bộ chọn, giống như "con trỏ tới một phương thức cá thể với một tên phương thức và tên đối số đã cho". Giống như một con trỏ thành viên, nó xác định tất cả phương thức của tất cả các lớp miễn là chúng trông giống nhau.

Bạn có thể điều tra cách trình biên dịch cụ thể thực hiện con trỏ, nhưng đó là một câu hỏi hoàn toàn khác.


4
Có con trỏ tới các hàm và, trong C ++, con trỏ tới các thành viên.
sdenham

Con trỏ C ++ cho các thành viên không phải là địa chỉ bộ nhớ? Chắc chắn là có. class A { public: int num; int x; }; int A::*pmi = &A::num; A a; int n = a.*pmi;Biến pmisẽ không được sử dụng nhiều nếu nó không chứa địa chỉ bộ nhớ, cụ thể là dòng cuối cùng của mã được thiết lập, địa chỉ thành viên numcủa thể hiện acủa lớp A. Bạn có thể truyền nó tới một intcon trỏ bình thường (mặc dù trình biên dịch có thể đưa ra cảnh báo cho bạn) và hủy đăng ký thành công (chứng minh rằng đó là đường cú pháp cho bất kỳ con trỏ nào khác).
dodgethesteamler

9

Một con trỏ là một địa chỉ mẫu bit (xác định duy nhất cho mục đích đọc hoặc viết) một từ lưu trữ trong RAM. Vì lý do lịch sử và thông thường, đơn vị cập nhật là tám bit, được gọi bằng tiếng Anh là "byte" hoặc tiếng Pháp, thay vì logic hơn, là một Octet. Điều này có mặt khắp nơi nhưng không cố hữu; kích thước khác đã tồn tại.

Nếu tôi nhớ chính xác, có một máy tính sử dụng từ 29 bit; Đây không chỉ là sức mạnh của hai người, mà thậm chí còn là chính. Tôi nghĩ rằng đây là SILLIAC nhưng bài viết Wikipedia thích hợp không hỗ trợ điều này. CAN BUS sử dụng địa chỉ 29 bit nhưng theo địa chỉ mạng quy ước không được gọi là con trỏ ngay cả khi chúng giống nhau về mặt chức năng.

Mọi người cứ khẳng định rằng con trỏ là số nguyên. Điều này không phải là nội tại cũng không cần thiết, nhưng nếu chúng ta giải thích các mẫu bit là số nguyên thì chất lượng hữu ích của quy tắc xuất hiện, cho phép thực hiện rất trực tiếp (và do đó hiệu quả trên phần cứng nhỏ) của các cấu trúc như "chuỗi" và "mảng". Khái niệm bộ nhớ liền kề phụ thuộc vào sự phụ thuộc thứ tự, và định vị tương đối là có thể; so sánh số nguyên và các phép toán số học có thể được áp dụng một cách có ý nghĩa. Vì lý do này, gần như luôn luôn có mối tương quan mạnh mẽ giữa kích thước từ để đánh địa chỉ lưu trữ và ALU (điều thực hiện phép toán số nguyên).

Đôi khi cả hai không tương ứng. Trong các PC đầu tiên, bus địa chỉ rộng 24 bit.


Nitpick, ngày nay trong các HĐH thông thường, con trỏ xác định vị trí trong bộ nhớ ảo và không liên quan gì đến một từ RAM vật lý (vị trí bộ nhớ ảo thậm chí không tồn tại về mặt vật lý nếu nó nằm trong một trang bộ nhớ được tất cả các số 0 của HĐH ).
hyde

@hyde - Đối số của bạn có giá trị trong bối cảnh mà bạn rõ ràng dự định, nhưng hình thức chủ yếu của máy tính là bộ điều khiển nhúng, trong đó những điều kỳ diệu như bộ nhớ ảo là những đổi mới gần đây với việc triển khai hạn chế. Ngoài ra, những gì bạn đã chỉ ra không có cách nào giúp OP hiểu được con trỏ. Tôi nghĩ rằng một số bối cảnh lịch sử sẽ làm cho tất cả ít độc đoán hơn.
Peter Wone

Tôi không biết nếu nói về RAM sẽ giúp OP hiểu, vì câu hỏi cụ thể là về con trỏ thực sự là gì. Ồ, một nitpick khác, trong con trỏ c theo định nghĩa trỏ đến byte (có thể được truyền một cách an toàn, char*ví dụ như cho mục đích sao chép / so sánh bộ nhớ và sizeof char==1như được định nghĩa theo tiêu chuẩn C), không phải từ (trừ khi kích thước từ của CPU giống như kích thước byte).
hyde

Những gì con trỏ về cơ bản là khóa băm để lưu trữ. Đây là ngôn ngữ và nền tảng bất biến.
Peter Wone

Câu hỏi là về con trỏ c . Và con trỏ chắc chắn không phải là khóa băm, vì không có bảng băm, không có thuật toán băm. Chúng tự nhiên là một số loại khóa bản đồ / từ điển (cho định nghĩa đủ rộng về "bản đồ"), nhưng không phải là khóa băm .
hyde

6

Về cơ bản mỗi máy tính hiện đại là một máy đẩy bit. Thông thường, nó đẩy các bit xung quanh trong cụm dữ liệu, được gọi là byte, từ, từ hoặc qwords.

Một byte bao gồm 8 bit, một từ 2 byte (hoặc 16 bit), một từ 2 từ (hoặc 32 bit) và một từ 2 từ khóa (hoặc 64 bit). Đây không phải là cách duy nhất để sắp xếp bit. Thao tác 128 bit và 256 bit cũng xảy ra, thường là trong hướng dẫn SIMD.

Hướng dẫn lắp ráp hoạt động trên các thanh ghi và địa chỉ bộ nhớ thường hoạt động theo một trong các hình thức trên.

ALU (đơn vị logic số học) hoạt động trên các bó bit như thể chúng đại diện cho các số nguyên (thường là định dạng Bổ sung của hai) và FPU như thể chúng có các giá trị dấu phẩy động (thường là kiểu IEEE 754 floatdouble). Các phần khác sẽ hoạt động như thể chúng là dữ liệu được đóng gói theo một số định dạng, ký tự, mục nhập bảng, hướng dẫn CPU hoặc địa chỉ.

Trên máy tính 64 bit thông thường, các gói 8 byte (64 bit) là địa chỉ. Chúng tôi hiển thị các địa chỉ này theo cách thông thường ở định dạng hex (như 0xabcd1234cdef5678), nhưng đó chỉ là một cách dễ dàng để con người đọc các mẫu bit. Mỗi byte (8 bit) được viết dưới dạng hai ký tự hex (tương đương mỗi ký tự hex - 0 đến F - đại diện cho 4 bit).

Điều thực sự đang diễn ra (đối với một số mức thực tế) là có các bit, thường được lưu trữ trong một thanh ghi hoặc được lưu trữ ở các vị trí liền kề trong một ngân hàng bộ nhớ và chúng tôi chỉ cố gắng mô tả chúng cho một người khác.

Theo sau một con trỏ bao gồm yêu cầu bộ điều khiển bộ nhớ cung cấp cho chúng tôi một số dữ liệu tại vị trí đó. Bạn thường yêu cầu bộ điều khiển bộ nhớ cho một số byte nhất định tại một vị trí nhất định (tốt, hoàn toàn là một phạm vi vị trí, thường liền kề nhau) và nó được phân phối thông qua các cơ chế khác nhau mà tôi sẽ không tham gia.

Mã thường chỉ định đích để dữ liệu được tìm nạp - một thanh ghi, một địa chỉ bộ nhớ khác, v.v. - và thường thì việc tải dữ liệu dấu phẩy động vào một thanh ghi mong đợi một số nguyên hoặc ngược lại là một ý tưởng tồi.

Kiểu dữ liệu trong C / C ++ là thứ mà trình biên dịch theo dõi và nó thay đổi mã được tạo. Thông thường không có gì nội tại trong dữ liệu làm cho nó thực sự thuộc bất kỳ một loại nào. Chỉ là một tập hợp các bit (được sắp xếp thành byte) được xử lý theo cách giống như số nguyên (hoặc cách giống như float hoặc theo cách giống như địa chỉ) theo mã.

Có nhiều ngoại lệ cho cái này. Có những kiến ​​trúc trong đó những thứ nhất định là một loại bit khác nhau . Ví dụ phổ biến nhất là các trang thực thi được bảo vệ - trong khi các hướng dẫn cho CPU biết các bit thực hiện là gì, tại thời điểm chạy, các trang (bộ nhớ) chứa mã để thực thi được đánh dấu đặc biệt, không thể sửa đổi và bạn không thể thực thi các trang không được đánh dấu như các trang thực hiện.

Ngoài ra còn có dữ liệu chỉ đọc (đôi khi được lưu trữ trong ROM mà không thể ghi vào vật lý!), Các vấn đề căn chỉnh (một số bộ xử lý không thể tải doubles từ bộ nhớ trừ khi chúng được căn chỉnh theo các cách cụ thể hoặc hướng dẫn SIMD yêu cầu căn chỉnh nhất định) và vô số kiến trúc khác quirks.

Ngay cả mức độ chi tiết trên là một lời nói dối. Máy tính không "thực sự" đẩy xung quanh bit, chúng thực sự đang đẩy xung quanh điện áp và dòng điện. Các điện áp và dòng điện này đôi khi không làm những gì chúng được cho là "phải làm" ở mức độ trừu tượng của các bit. Các chip được thiết kế để phát hiện hầu hết các lỗi như vậy và sửa chúng mà không cần sự trừu tượng hóa ở cấp độ cao hơn.

Thậm chí đó là một lời nói dối.

Mỗi cấp độ trừu tượng ẩn cái bên dưới và cho phép bạn suy nghĩ về việc giải quyết vấn đề mà không cần phải ghi nhớ sơ đồ Feynman để in ra "Hello World".

Vì vậy, ở mức độ trung thực đủ, máy tính đẩy các bit và các bit đó được cho ý nghĩa bằng cách chúng được sử dụng.


3

Mọi người đã thực hiện một vấn đề lớn về việc con trỏ có phải là số nguyên hay không. Thực sự có câu trả lời cho những câu hỏi này. Tuy nhiên, bạn sẽ phải bước một bước vào vùng đất của thông số kỹ thuật, vốn không dành cho người yếu tim. Chúng ta sẽ xem xét thông số kỹ thuật C, ISO / IEC 9899: TC2

6.3.2.3 Con trỏ

  1. Một số nguyên có thể được chuyển đổi thành bất kỳ loại con trỏ. Trừ khi được chỉ định trước đó, kết quả được xác định theo triển khai, có thể không được căn chỉnh chính xác, có thể không trỏ đến một thực thể của loại được tham chiếu và có thể là biểu diễn bẫy.

  2. Bất kỳ loại con trỏ có thể được chuyển đổi thành một loại số nguyên. Trừ khi được chỉ định trước đó, kết quả được xác định theo thực hiện. Nếu kết quả không thể được biểu diễn trong kiểu số nguyên, hành vi không được xác định. Kết quả không cần phải nằm trong phạm vi giá trị của bất kỳ loại số nguyên nào.

Bây giờ để làm điều này, bạn sẽ cần phải biết một số thuật ngữ thông số chung. "Thực hiện được xác định" có nghĩa là mọi trình biên dịch đơn lẻ được phép định nghĩa nó khác nhau. Trong thực tế, một trình biên dịch thậm chí có thể định nghĩa nó theo nhiều cách khác nhau tùy thuộc vào cài đặt trình biên dịch của bạn. Hành vi không xác định có nghĩa là trình biên dịch được phép thực hiện hoàn toàn mọi thứ, từ đưa ra lỗi thời gian biên dịch đến các hành vi không thể giải thích được, cho đến hoạt động hoàn hảo.

Từ đó, chúng ta có thể thấy rằng hình thức lưu trữ cơ bản không được chỉ định, ngoài việc có thể có một chuyển đổi thành một loại số nguyên. Bây giờ sự thật được nói, hầu như mọi trình biên dịch dưới ánh mặt trời đại diện cho con trỏ dưới mui xe là địa chỉ số nguyên (với một số trường hợp đặc biệt có thể được biểu diễn dưới dạng 2 số nguyên thay vì chỉ 1), nhưng đặc tả cho phép hoàn toàn mọi thứ, chẳng hạn như đại diện địa chỉ dưới dạng chuỗi 10 ký tự!

Nếu chúng ta nhanh chóng chuyển tiếp ra khỏi C và nhìn vào thông số C ++, chúng ta sẽ hiểu rõ hơn một chút reinterpret_cast, nhưng đây là một ngôn ngữ khác, vì vậy giá trị của nó đối với bạn có thể khác nhau:

Thông số kỹ thuật dự thảo ISO / IEC N337: C ++ 11 (Tôi chỉ có bản nháp trên tay)

5.2.10 Diễn viên diễn giải lại

  1. Một con trỏ có thể được chuyển đổi rõ ràng thành bất kỳ loại tích phân nào đủ lớn để giữ nó. Hàm ánh xạ được xác định theo thực hiện. [Lưu ý: Dự định sẽ không gây ngạc nhiên cho những người biết cấu trúc địa chỉ của máy bên dưới. Ghi chú của người dùng] Một giá trị của loại std :: nullptr_t có thể được chuyển đổi thành một loại tích phân; chuyển đổi có cùng ý nghĩa và hiệu lực như chuyển đổi (void *) 0 thành loại tích phân. [Lưu ý: Không thể sử dụng reinterpret_cast để chuyển đổi giá trị của bất kỳ loại nào thành loại std :: nullptr_t. Lưu ý

  2. Một giá trị của kiểu tích phân hoặc kiểu liệt kê có thể được chuyển đổi rõ ràng thành một con trỏ. Một con trỏ được chuyển đổi thành một số nguyên có kích thước đủ (nếu có tồn tại trong quá trình thực hiện) và trở lại cùng loại con trỏ sẽ có giá trị ban đầu; ánh xạ giữa con trỏ và số nguyên được xác định theo cách khác. [Lưu ý: Trừ khi được mô tả trong 3.7.4.3, kết quả của việc chuyển đổi như vậy sẽ không phải là giá trị con trỏ có nguồn gốc an toàn. Lưu ý

Như bạn có thể thấy ở đây, với một vài năm nữa, C ++ nhận thấy rằng an toàn khi giả sử rằng ánh xạ tới các số nguyên tồn tại, do đó không còn nói về hành vi không xác định (mặc dù có một mâu thuẫn thú vị giữa các phần 4 và 5 với cụm từ "nếu có như vậy tồn tại trên thực hiện")


Bây giờ bạn nên lấy gì từ điều này?

  • Các đại diện chính xác của con trỏ là thực hiện được xác định. (trên thực tế, chỉ để làm cho nó lộn xộn hơn, một số máy tính nhúng nhỏ đại diện cho con trỏ null, (void ) 0, như địa chỉ 255 để hỗ trợ một số thủ thuật bí danh địa chỉ mà chúng sử dụng) *
  • Nếu bạn phải hỏi về sự thể hiện của con trỏ trong bộ nhớ, có lẽ bạn không phải là một điểm trong sự nghiệp lập trình của bạn, nơi bạn muốn đấu tranh với chúng.

Đặt cược tốt nhất: truyền tới một (char *). Các thông số kỹ thuật của C và C ++ có đầy đủ các quy tắc chỉ định việc đóng gói các mảng và cấu trúc, và cả hai luôn cho phép truyền bất kỳ con trỏ nào vào char *. char luôn là 1 byte (không được bảo đảm bằng C, nhưng bởi C ++ 11, nó đã trở thành một phần bắt buộc của ngôn ngữ, vì vậy nó tương đối an toàn khi giả sử nó là 1 byte ở mọi nơi). Điều này cho phép bạn thực hiện một số số học con trỏ ở cấp độ byte theo byte mà không cần phải thực sự cần biết các biểu diễn cụ thể thực hiện của con trỏ.


Bạn có thể nhất thiết phải đúc một con trỏ hàm đến một char *? Tôi đang nghĩ về một máy giả định có không gian địa chỉ riêng cho mã và dữ liệu.
Philip Kendall

@PhilipKendall Điểm tốt. Tôi không bao gồm phần đó của thông số kỹ thuật, nhưng các con trỏ hàm được coi là một thứ hoàn toàn khác với các con trỏ dữ liệu trong đặc tả vì chính xác vấn đề bạn nêu ra. Con trỏ thành viên cũng được đối xử khác nhau (nhưng họ cũng hành động rất khác nhau)
Cort Ammon - Tái lập Monica

A charluôn có 1 byte trong C. Trích dẫn từ tiêu chuẩn C: "Toán tử sizeof mang lại kích thước (tính bằng byte) của toán hạng của nó" và "Khi sizeof được áp dụng cho toán hạng có kiểu char, char không dấu hoặc char đã ký, (hoặc một phiên bản đủ điều kiện) kết quả là 1. " Có lẽ bạn đang nghĩ một byte dài 8 bit. Đó không nhất thiết phải là trường hợp. Để tuân thủ tiêu chuẩn, một byte phải chứa ít nhất 8 bit.
David Hammen

Thông số kỹ thuật mô tả chuyển đổi giữa các loại con trỏ và số nguyên. Cần phải luôn nhớ rằng "chuyển đổi" giữa các loại không bao hàm sự bình đẳng của các loại, thậm chí cả biểu diễn nhị phân của hai loại trong bộ nhớ sẽ có cùng một mẫu bit. (ASCII có thể được "chuyển đổi" thành EBCDIC. Big-endian có thể được "chuyển đổi" thành endian nhỏ.
V.v.

1

Trên hầu hết các kiến ​​trúc, loại con trỏ không còn tồn tại sau khi chúng được dịch thành mã máy (ngoại trừ "con trỏ béo"). Do đó, một con trỏ tới một intsẽ không thể phân biệt được từ một con trỏ đến một double, ít nhất là trên chính nó. *

[*] Mặc dù, bạn vẫn có thể đoán dựa trên các loại hoạt động mà bạn áp dụng cho nó.


1

Một điều quan trọng để hiểu về C và C ++ là loại thực sự là gì. Tất cả những gì họ thực sự làm là chỉ cho trình biên dịch cách diễn giải một tập hợp bit / byte. Hãy bắt đầu với đoạn mã sau:

int var = -1337;

Tùy thuộc vào kiến ​​trúc, một số nguyên thường được cung cấp 32 bit không gian để lưu trữ giá trị đó. Điều đó có nghĩa là không gian trong bộ nhớ nơi var được lưu trữ sẽ trông giống như "11111111 11111111 11111010 11000111" hoặc trong hex "0xFFFFFAC7". Đó là nó. Đó là tất cả những gì được lưu trữ tại vị trí đó. Tất cả các loại làm là nói cho trình biên dịch làm thế nào để giải thích thông tin đó. Con trỏ không khác nhau. Nếu tôi làm một cái gì đó như thế này:

int* var_ptr = &var;   //the ampersand is telling C "get the address where var's value is located"

Sau đó, trình biên dịch sẽ lấy vị trí của var và sau đó lưu địa chỉ đó theo cùng một cách đoạn mã đầu tiên lưu giá trị -1337. Không có sự khác biệt trong cách chúng được lưu trữ, chỉ là cách chúng được sử dụng. Nó thậm chí không quan trọng rằng tôi đã biến var_ptr thành một con trỏ tới int. Nếu bạn muốn, bạn có thể làm.

unsigned int var2 = *(unsigned int*)var_ptr;

Điều này sẽ sao chép giá trị hex ở trên của var (0xFFFFFAC7) vào vị trí lưu trữ giá trị của var2. Nếu sau đó chúng tôi sử dụng var2, chúng tôi sẽ thấy rằng giá trị sẽ là 4294965959. Các byte trong var2 giống như var, nhưng giá trị số khác nhau. Trình biên dịch diễn giải chúng khác nhau bởi vì chúng tôi đã nói với nó rằng các bit đó biểu thị một dấu dài không dấu. Bạn cũng có thể làm tương tự cho giá trị con trỏ.

unsigned int var3 = (unsigned int)var_ptr;

Cuối cùng, bạn sẽ diễn giải giá trị đại diện cho địa chỉ của var là một số nguyên không dấu trong ví dụ này.

Hy vọng rằng điều này làm rõ mọi thứ cho bạn và cung cấp cho bạn cái nhìn sâu sắc hơn về cách thức hoạt động của C. Xin lưu ý rằng bạn KHÔNG NÊN làm bất kỳ điều điên rồ nào tôi đã làm trong hai dòng dưới đây trong mã sản xuất thực tế. Đó chỉ là để trình diễn.


1

Số nguyên.

Không gian địa chỉ trong máy tính được đánh số liên tục, bắt đầu từ 0 và tăng thêm 1. Vì vậy, một con trỏ sẽ giữ một số nguyên tương ứng với một địa chỉ trong không gian địa chỉ.


1

Các loại kết hợp.

Cụ thể, một số loại nhất định kết hợp, gần như thể chúng được tham số hóa với giữ chỗ. Các kiểu mảng và con trỏ giống như thế này; họ có một trình giữ chỗ như vậy, đó là loại phần tử của mảng hoặc vật được chỉ ra tương ứng. Các loại chức năng cũng như thế này; họ có thể có nhiều trình giữ chỗ cho các tham số và một trình giữ chỗ cho kiểu trả về.

Một biến được khai báo để giữ một con trỏ tới char có kiểu "con trỏ tới char". Một biến được khai báo để giữ một con trỏ tới con trỏ tới int có kiểu "con trỏ tới con trỏ tới int".

Một (giá trị của) loại "con trỏ tới con trỏ tới int" có thể được thay đổi thành "con trỏ thành int" bằng một phép toán quy định. Vì vậy, khái niệm loại không chỉ là từ mà là một cấu trúc có ý nghĩa toán học, chỉ ra những gì chúng ta có thể làm với các giá trị của loại (chẳng hạn như tham chiếu hoặc chuyển dưới dạng tham số hoặc gán cho biến; nó cũng xác định kích thước (số byte) của lập chỉ mục, số học và các hoạt động tăng / giảm).

PS Nếu bạn muốn tìm hiểu sâu về các loại, hãy thử blog này: http://www.goodmath.org/blog/2015/05/13/expressions-and-arity-part-1/

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.