Là sizeof (một số con trỏ) luôn bằng bốn?


227

Ví dụ: sizeof(char*)trả 4. Khi nào int*, long long*, tất cả những gì tôi đã cố gắng. Có bất kỳ ngoại lệ cho điều này?


51
Tại sao đánh dấu điều này xuống? Câu hỏi hay cho bất kỳ người mới bắt đầu.
Martin York

2
Tôi nghi ngờ rằng một câu hỏi khác đang ẩn trong câu hỏi này: "sizeof là gì?" hoặc có thể là "Tại sao sizeof <bất kỳ con trỏ> == 4? Điều gì đặc biệt về 4?". Tôi có đúng không

2
Vâng, nó phụ thuộc vào nền tảng của bạn. Hầu hết các triển khai chia sẻ cùng một kích thước cho mọi loại con trỏ trên một nền tảng cụ thể.
phoeagon

Câu trả lời:


194

Sự đảm bảo bạn nhận được là vậy sizeof(char) == 1. Không có đảm bảo nào khác, kể cả không đảm bảo điều đó sizeof(int *) == sizeof(double *).

Trong thực tế, con trỏ sẽ có kích thước 2 trên hệ thống 16 bit (nếu bạn có thể tìm thấy một), 4 trên hệ thống 32 bit và 8 trên hệ thống 64 bit, nhưng không có gì đạt được khi dựa vào một hệ thống nhất định kích thước.


96
Và 3 byte trên hệ thống 24 bit. Vâng, tôi đã làm việc trên một. Chào mừng đến với thế giới của các thiết bị nhúng.
dwj

30
Tôi cũng đã làm việc trên các hệ thống 16 bit với con trỏ 20 bit. Tôi nên đi xem
kích thước

5
@monjardin: IIRC, 8086 là như thế. Có một địa chỉ 16 bit và thanh ghi phân đoạn 4 bit. Tôi tin rằng một con trỏ "NEAR" bình thường là 16 bit và một con trỏ được khai báo là "FAR" là nhiều hơn, có thể là 24, mặc dù tôi không chắc chắn.
rmeador

18
một bảo đảm khác là sizeof (char *) == sizeof (void *), bởi vì chúng phải có cùng các biểu diễn (đối tượng [kích thước] và giá trị [tập hợp các bit có liên quan đến giá trị của chúng]
Julian Schaub - litb

7
Vì câu hỏi yêu cầu ngoại lệ, cần lưu ý rằng các con trỏ hàm thành viên không tĩnh thường có kích thước khác với các con trỏ bình thường và cũng thay đổi theo nền tảng, loại, v.v. Khác với +1.
John5342

36

Ngay cả trên nền tảng x86 32 bit đơn giản, bạn có thể nhận được nhiều kích cỡ con trỏ khác nhau, hãy thử ví dụ này:

struct A {};

struct B : virtual public A {};

struct C {};

struct D : public A, public C {};

int main()
{
    cout << "A:" << sizeof(void (A::*)()) << endl;
    cout << "B:" << sizeof(void (B::*)()) << endl;
    cout << "D:" << sizeof(void (D::*)()) << endl;
}

Trong Visual C ++ 2008, tôi nhận được 4, 12 và 8 cho các kích thước của hàm con trỏ đến thành viên.

Raymond Chen đã nói về điều này ở đây .


4
Con trỏ đến các chức năng thành viên là một nỗi đau thực sự. Thật không may là không phải tất cả các trình biên dịch đều thích trình biên dịch Digital Mars C ++, trả về 4 trong mọi trường hợp.
dalle

gcc 4.72 in tất cả 8 ... Điều này có được xác định trong tiêu chuẩn c ++ không?
Gob00st

2
@ Gob00st: Điều duy nhất được xác định là char là 1. Các loại khác có thể là bất kỳ kích thước nào có liên quan đến trình biên dịch đó. Không có yêu cầu về tính nhất quán giữa các loại con trỏ.
Nhật thực

được rồi cảm ơn. Sau đó, không có thắc mắc gcc & VC có thực hiện khác nhau.
Gob00

5
@Eclipse có, có: char <= short <= int <= long <= long long
Cole Johnson

30

Chỉ là một ngoại lệ khác cho danh sách đã được đăng. Trên nền tảng 32 bit, con trỏ có thể mất 6, không phải 4 , byte:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char far* ptr; // note that this is a far pointer
    printf( "%d\n", sizeof( ptr));
    return EXIT_SUCCESS;
}

Nếu bạn biên dịch chương trình này với Open Watcom và chạy nó, bạn sẽ nhận được 6, bởi vì các con trỏ xa mà nó hỗ trợ bao gồm các giá trị phân đoạn 32 bit và 16 bit


5
Không phải phân khúc, mà là bộ chọn - không phải là một phần của địa chỉ bộ nhớ, mà là mục nhập chỉ mục trong LDT hoặc GDT và có một số cờ truy cập
Roee Shenberg

1
Tại sao có các phân đoạn và độ lệch trong x86 trong khi không gian địa chỉ bằng phẳng?
phuclv

@ LưuViênPhúc Vì nó tiết kiệm không gian cho trường hợp rất phổ biến của con trỏ gần, có thể được mã hóa ngắn hơn.
Christopher Creutzig

1
@ChristopherCreutzig có nghĩa là các phân đoạn được sử dụng để mở rộng không gian địa chỉ như PAE?
phuclv

@ LưuViênPhúc Đã lâu rồi tôi mới thực hiện lắp ráp trên bất cứ thứ gì 32 bit. Phần tôi dường như nhớ là bạn có thể tiết kiệm không gian cho các con trỏ trỏ gần mã bạn có. Ngoài ra, không phải tất cả các kiến ​​trúc 32 bit - chắc chắn không phải tất cả dựa trên x86 - sử dụng mô hình bộ nhớ phẳng. Xem, ví dụ: tenouk.com/Bufferoverflowc/Bufferoverflow1a.html để biết thêm một số thảo luận về vấn đề này, mặc dù, như tôi đã nói, đã được một lúc và tôi không thể chứng minh cho bất cứ điều gì.
Christopher Creutzig

24

nếu bạn đang biên dịch cho máy 64 bit, thì nó có thể là 8.


2
Trong khi điều này thường là trường hợp, nó không nhất thiết phải đúng. Ví dụ: nếu bạn đang biên dịch trên máy 64 bit có kích thước từ là 64 bit, thì sizeof (char *) có thể sẽ là 1. Không đề cập đến các loại con trỏ kỳ lạ hơn trong các máy thông thường, như Eclipse và dmityugov viết.
Kaz Dragon

@KazDragon , sizeof(char*)==1? Bạn có chắc không? Ý bạn là size(char)==1sao?
Aaron McDaid

3
@AaronMcDaid Tôi thực sự có nghĩa là sizeof (char *). sizeof (char) luôn là 1. Nhưng nếu từ máy của bạn là 64 bit và môi trường phát triển của bạn được triển khai theo cách CHAR_BITS = 64, thì có thể một con trỏ vừa trong cùng một không gian như char và do đó sẽ cũng là 1.
Kaz Dragon

điều đó không đúng trong các trang web
x32-abi.google.com/site/x32abi

1
@KazDragon Tôi đang xây dựng (rất chậm, khi không trì hoãn) một máy có các từ 16 bit và không có địa chỉ byte. Mặc dù nó không thể chạy C.
dùng253751

17

Về mặt kỹ thuật, tiêu chuẩn C chỉ đảm bảo sizeof (char) == 1, và phần còn lại tùy thuộc vào việc thực hiện. Nhưng trên các kiến ​​trúc x86 hiện đại (ví dụ như chip Intel / AMD) thì điều đó khá dễ đoán.

Bạn có thể đã nghe các bộ xử lý được mô tả là 16 bit, 32 bit, 64 bit, v.v. Điều này thường có nghĩa là bộ xử lý sử dụng N-bit cho các số nguyên. Vì con trỏ lưu địa chỉ bộ nhớ và địa chỉ bộ nhớ là số nguyên, điều này cho bạn biết có bao nhiêu bit sẽ được sử dụng cho con trỏ. sizeof thường được đo bằng byte, do đó mã được biên dịch cho bộ xử lý 32 bit sẽ báo cáo kích thước của con trỏ là 4 (32 bit / 8 bit mỗi byte) và mã cho bộ xử lý 64 bit sẽ báo cáo kích thước của con trỏ là 8 (64 bit / 8 bit mỗi byte). Đây là nơi giới hạn 4GB RAM cho bộ xử lý 32 bit xuất phát - nếu mỗi địa chỉ bộ nhớ tương ứng với một byte, để giải quyết nhiều bộ nhớ hơn, bạn cần số nguyên lớn hơn 32 bit.


"Bạn có thể đã nghe các bộ xử lý được mô tả là 16 bit, 32 bit, 64 bit, v.v. Điều này thường có nghĩa là bộ xử lý sử dụng N-bit cho các số nguyên." -> Tôi có máy 64 bit nhưng sizeof (int) là 4 byte. Nếu tuyên bố của bạn là đúng, làm sao điều này có thể xảy ra?!
Sangeeth Saravanaraj

6
@SangeethSaravanaraj: Để tương thích ngược với mã 32 bit, họ đã quyết định có int tiếp tục là 4 byte và yêu cầu bạn chọn tham gia sử dụng loại 8 byte bằng cách chỉ định 'dài'. dài thực sự là kích thước từ gốc trên x86-64. Một cách để thấy điều này là các trình biên dịch thông thường sẽ đệm các cấu trúc của bạn để làm cho chúng được căn chỉnh từ (mặc dù có thể có các kiến ​​trúc trong đó kích thước từ và căn chỉnh không liên quan), vì vậy nếu bạn tạo một cấu trúc có int (32 bit) trong đó, và gọi sizeof () trên đó, nếu bạn quay lại 8, bạn biết rằng nó đệm chúng thành kích thước từ 64 bit.
Joseph Garvin

@SangeethSaravanaraj: Lưu ý rằng về mặt lý thuyết kích thước từ gốc của CPU và trình biên dịch quyết định 'int' có thể khác nhau tùy ý, đó chỉ là quy ước cho 'int' là kích thước từ gốc trước khi x86-64 xuất hiện, trong đó nó dài để dễ dàng ngược lại compat.
Joseph Garvin

Cảm ơn đã giải thích! :)
Sangeeth Saravanaraj

7

Kích thước của con trỏ về cơ bản phụ thuộc vào kiến ​​trúc của hệ thống mà nó được thực hiện. Ví dụ, kích thước của một con trỏ trong 32 bit là 4 byte (32 bit) và 8 byte (64 bit) trong máy 64 bit. Các loại bit trong một máy không có gì ngoài địa chỉ bộ nhớ mà nó có thể có. Máy 32 bit có thể có 2^32không gian địa chỉ và máy 64 bit có thể có tối đa 2^64không gian địa chỉ. Vì vậy, một con trỏ (biến trỏ đến một vị trí bộ nhớ) sẽ có thể trỏ đến bất kỳ địa chỉ bộ nhớ ( 2^32 for 32 bit and 2^64 for 64 bit) nào mà máy giữ.

Vì lý do này, chúng tôi thấy kích thước của một con trỏ là 4 byte trong máy 32 bit và 8 byte trong máy 64 bit.


6

Ngoài sự khác biệt 16/32/64 bit, ngay cả những thứ khác cũng có thể xảy ra.

Đã có những máy mà sizeof (int *) sẽ là một giá trị, có thể là 4 nhưng trong đó sizeof (char *) lớn hơn. Các máy xử lý các từ tự nhiên thay vì byte phải "gia tăng" các con trỏ ký tự để chỉ định phần nào của từ bạn thực sự muốn để thực hiện đúng tiêu chuẩn C / C ++.

Điều này bây giờ rất bất thường vì các nhà thiết kế phần cứng đã học được giá trị của địa chỉ byte.


4
Trình biên dịch C cho các máy vectơ Cray, chẳng hạn như T90, làm một cái gì đó tương tự. Địa chỉ phần cứng là 8 byte và trỏ đến các từ 8 byte. void*char*được xử lý trong phần mềm và được tăng thêm phần bù 3 bit trong từ - nhưng vì thực tế không có không gian địa chỉ 64 bit, phần bù được lưu trữ trong 3 bit thứ tự cao của 64 bit từ. Vì vậy char*, int*có cùng kích thước, nhưng có các biểu diễn bên trong khác nhau - và mã giả định rằng các con trỏ là "thực sự" chỉ các số nguyên có thể thất bại nặng.
Keith Thompson

5

Con trỏ 8 bit và 16 bit được sử dụng trong hầu hết các bộ vi điều khiển cấu hình thấp. Điều đó có nghĩa là mọi máy giặt, micro, tủ lạnh, TV cũ và thậm chí cả ô tô.

Bạn có thể nói những điều này không liên quan gì đến lập trình trong thế giới thực. Nhưng đây là một ví dụ trong thế giới thực: Arduino với 1-2-4k ram (tùy thuộc vào chip) với con trỏ 2 byte.

Đó là gần đây, giá rẻ, có thể truy cập cho tất cả mọi người và đáng để mã hóa.


4

Ngoài những gì mọi người đã nói về các hệ thống 64 bit (hoặc bất cứ điều gì), còn có các loại con trỏ khác ngoài con trỏ tới đối tượng.

Một con trỏ thành viên có thể có kích thước gần như bất kỳ, tùy thuộc vào cách chúng được trình biên dịch của bạn triển khai: chúng không nhất thiết phải có cùng kích thước. Hãy thử một con trỏ đến thành viên của một lớp POD và sau đó một con trỏ thành thành viên được kế thừa từ một trong các lớp cơ sở của một lớp có nhiều cơ sở. Có gì vui


3

Từ những gì tôi nhớ lại, nó dựa trên kích thước của một địa chỉ bộ nhớ. Vì vậy, trên một hệ thống có sơ đồ địa chỉ 32 bit, sizeof sẽ trả về 4, vì đó là 4 byte.


4
Không có yêu cầu như vậy. Thậm chí không có yêu cầu nào về sizeof (unsign int) == sizeof (int int). Kích thước của một con trỏ tới một int sẽ luôn luôn, theo định nghĩa, sizeof (int *), đến một kích thước char (char *), v.v. Dựa vào bất kỳ giả định nào khác là một ý tưởng tồi cho tính di động.
Mihai Limbășan

À, tôi hiểu rồi. Cảm ơn bạn về thông tin.
Will Mc

1
Vẫn có thể trả về 2, nếu CHAR_BIT là 16. sizeof () tính theo số ký tự, không phải octet.
MSalters

5
@Mihai: Trong C ++ sizeof (unsigned int) == sizeof (signed int), yêu cầu này được tìm thấy trong 3.9.1 / 3. "Đối với mỗi tiêu chuẩn ký số nguyên chủng loại, có tồn tại một tiêu chuẩn tương ứng (nhưng khác nhau) kiểu dữ liệu integer unsigned: unsigned char, unsigned short int, unsigned int, unsigned long int, và unsigned long long int, mỗi trong số đó chiếm cùng một lượng lưu trữ và có yêu cầu tương tự liên kết là tương ứng ký số nguyên loại "
Ben Voigt

3

Nói chung, sizeof (khá nhiều thứ) sẽ thay đổi khi bạn biên dịch trên các nền tảng khác nhau. Trên nền tảng 32 bit, con trỏ luôn có cùng kích thước. Trên các nền tảng khác (64 bit là ví dụ rõ ràng) điều này có thể thay đổi.


3

Không, kích thước của một con trỏ có thể thay đổi tùy theo kiến ​​trúc. Có rất nhiều trường hợp ngoại lệ.


3

Kích thước của con trỏ và int là 2 byte trong trình biên dịch Turbo C trên máy Windows 32 bit.

Vì vậy, kích thước của con trỏ là trình biên dịch cụ thể. Nhưng nhìn chung hầu hết các trình biên dịch được triển khai để hỗ trợ biến con trỏ 4 byte trong biến con trỏ 32 bit và 8 byte trong máy 64 bit).

Vì vậy, kích thước của con trỏ không giống nhau trong tất cả các máy.


2

Lý do kích thước của con trỏ của bạn là 4 byte là do bạn đang biên dịch cho kiến ​​trúc 32 bit. Như FryGuy đã chỉ ra, trên kiến ​​trúc 64 bit, bạn sẽ thấy 8.


2

Trong Win64 (Cygwin GCC 5.4) , hãy xem ví dụ dưới đây:

Đầu tiên, kiểm tra cấu trúc sau:

struct list_node{
    int a;
    list_node* prev;
    list_node* next;
};

struct test_struc{
    char a, b;
};

Mã kiểm tra dưới đây:

std::cout<<"sizeof(int):            "<<sizeof(int)<<std::endl;
std::cout<<"sizeof(int*):           "<<sizeof(int*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(double):         "<<sizeof(double)<<std::endl;
std::cout<<"sizeof(double*):        "<<sizeof(double*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(list_node):      "<<sizeof(list_node)<<std::endl;
std::cout<<"sizeof(list_node*):     "<<sizeof(list_node*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(test_struc):     "<<sizeof(test_struc)<<std::endl;
std::cout<<"sizeof(test_struc*):    "<<sizeof(test_struc*)<<std::endl;    

Đầu ra dưới đây:

sizeof(int):            4
sizeof(int*):           8

sizeof(double):         8
sizeof(double*):        8

sizeof(list_node):      24
sizeof(list_node*):     8

sizeof(test_struc):     2
sizeof(test_struc*):    8

Bạn có thể thấy rằng trong 64-bit, sizeof(pointer)8.


1

Một con trỏ chỉ là một thùng chứa cho một địa chỉ. Trên máy 32 bit, phạm vi địa chỉ của bạn là 32 bit, do đó, một con trỏ sẽ luôn là 4 byte. Trên máy 64 bit, bạn có dải địa chỉ 64 bit, con trỏ sẽ là 8 byte.


1
Trên máy 32 bit có byte 32 bit, sizeof (char *) có thể là 1.
Robert Gamble

"... Với byte 32 bit". Tôi không biết những thứ như vậy tồn tại ... ưa thích đó.
Ed S.

1
Trên một con vịt 32 bit, sizeof (char *) trả về PI
Adriano Varoli Piazza

0

Chỉ để hoàn thiện và quan tâm lịch sử, trong thế giới 64 bit, có các quy ước nền tảng khác nhau về kích thước của các loại dài và dài, được đặt tên là LLP64 và LP64, chủ yếu giữa các hệ thống kiểu Unix và Windows. Một tiêu chuẩn cũ có tên ILP64 cũng tạo ra int = 64-bit.

Microsoft duy trì LLP64 trong đó longlong = 64 bit, nhưng vẫn duy trì ở mức 32, để chuyển dễ dàng hơn.

Type           ILP64   LP64   LLP64
char              8      8       8
short            16     16      16
int              64     32      32
long             64     64      32
long long        64     64      64
pointer          64     64      64

Nguồn: https://stackoverflow.com/a/384672/48026

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.