2013-04-03 107 views
7

Tôi cố gắng để chạy mã này,Sự khác nhau giữa con trỏ float và địa chỉ con trỏ int là gì?

int *p; 
float q; 
q = 6.6; 
p = &q; 

Mặc dù nó sẽ là một lời cảnh báo, nhưng tôi nghĩ rằng &qp là của cùng một kích thước, vì vậy p có thể có một địa chỉ của q. Nhưng khi tôi in &qp Tôi nhận được kết quả khác nhau. Đây là sản phẩm của tôi

*p = 6.600000 
q = 0.000000, p = 0x40d33333, &q = 0x7fffe2fa3c8c 

Tôi đang thiếu gì? Và p&q là giống nhau khi cả con trỏ và loại biến đều giống nhau.

mã hoàn chỉnh của tôi là

#include<stdio.h> 
void main() 
{ 
    int *p; 
    float q; 
    q = 6.6; 
    p = &q; 
    printf("*p = %f \n q = %f, p = %p, &q = %p \n",*p,q,p,&q); 
} 
+3

Vui lòng hiển thị mã bạn đang sử dụng để có được kết quả đó. –

+0

Nếu tôi đưa bạn đúng, bạn có thể muốn sử dụng một công đoàn ... –

+4

Đó là 'int main (void)', không phải 'void main()'. 'void main()' rất hữu ích cho việc phát hiện các sách được viết bởi các tác giả không biết rõ C. –

Trả lời

7

Bạn cần cảnh báo trình biên dịch nghiêm túc hơn.

C không yêu cầu trình biên dịch để từ chối chương trình không hợp lệ, nó chỉ yêu cầu "chẩn đoán" cho vi phạm quy tắc. Chẩn đoán có thể là thông báo lỗi nghiêm trọng hoặc cảnh báo.

Thật không may, nó phổ biến cho các trình biên dịch đưa ra cảnh báo cho các bài tập kiểu con trỏ không tương thích.

void main() 

Điều này là sai; nó phải là int main(void). Trình biên dịch của bạn có thể cho phép bạn thoát khỏi nó, và nó có thể không gây ra bất kỳ vấn đề có thể nhìn thấy, nhưng không có điểm trong việc không viết nó một cách chính xác. (Nó không phải khá rằng đơn giản, nhưng đó là đủ gần.)

int *p; 
float q; 
q = 6.6; 

Đó là ok.

p = &q; 

p thuộc loại int*; &q thuộc loại float*. Chỉ định một cái khác (không có dàn diễn viên) là một vi phạm ràng buộc . Cách đơn giản nhất để nhìn vào nó là nó đơn giản là bất hợp pháp.

Nếu bạn thực sự muốn làm việc này, bạn có thể sử dụng một dàn diễn viên:

p = (int*)&q; /* legal, but ugly */ 

nhưng có ít lý do chính đáng để làm như vậy. p là con trỏ đến int; nó phải trỏ đến một đối tượng int trừ khi bạn có rất lý do chính đáng để làm cho nó trỏ đến một thứ khác. Trong một số trường hợp, chính chuyển đổi có thể có hành vi không xác định.

printf("*p = %f \n q = %f, p = %p, &q = %p \n",*p,q,p,&q); 

Định dạng %f đòi hỏi một lập luận double (một cuộc tranh luận float được thăng double trong bối cảnh này nên float sẽ là ok). Nhưng *p thuộc loại int. Gọi số printf với đối số sai khiến cho hành vi của chương trình của bạn không được xác định.

%p yêu cầu đối số thuộc loại void*, không chỉ bất kỳ loại con trỏ nào. Nếu bạn muốn in một giá trị con trỏ, bạn nên bỏ nó để void*:

printf("&q = %p\n", (void*)&q); 

Nó có khả năng để làm việc mà không có dàn diễn viên, nhưng một lần nữa, hành vi này là không xác định.

Nếu bạn nhận được bất kỳ cảnh báo nào khi bạn biên dịch chương trình, thậm chí không bận tâm chạy chương trình đó. Trước tiên hãy sửa các cảnh báo.

Đối với câu hỏi trong tiêu đề của bạn, con trỏ thuộc loại int*float* là các loại khác nhau. An int* phải trỏ đến đối tượng int; a float* phải trỏ đến một đối tượng float. Trình biên dịch của bạn có thể cho phép bạn trộn chúng, nhưng kết quả của việc làm như vậy hoặc là được thực hiện xác định hoặc không xác định. Ngôn ngữ C, và đặc biệt là nhiều trình biên dịch C, sẽ cho phép bạn loại bỏ rất nhiều thứ không có ý nghĩa nhiều.

Lý do chúng là các loại riêng biệt là (cố gắng) ngăn chặn hoặc ít nhất là phát hiện, lỗi khi sử dụng. Nếu bạn khai báo một đối tượng kiểu int*, bạn đang nói rằng bạn dự định cho nó trỏ đến một đối tượng int (nếu nó không phải là một con trỏ rỗng). Lưu trữ địa chỉ của đối tượng float trong đối tượng int* của bạn gần như chắc chắn là một sai lầm. Thực thi an toàn loại cho phép các lỗi như vậy được phát hiện càng sớm càng tốt (khi trình biên dịch của bạn in một cảnh báo thay vì khi chương trình của bạn bị treo trong một bản demo cho một khách hàng quan trọng).

Có khả năng (nhưng không được đảm bảo) rằng int*float* có cùng kích thước và có cùng biểu diễn nội bộ. Nhưng nghĩa là đối tượng int* không phải là "một bộ sưu tập gồm 32 (hoặc 64) bit chứa địa chỉ ảo", nhưng "cái gì trỏ đến đối tượng int".

+5

Vì bạn đang ném cuốn sách vào anh ta, đọc '* p' vi phạm bí danh nghiêm ngặt, do đó, chính UB của chính nó bất kể bạn làm gì với giá trị. –

+3

'p = (int *) & q;' không phải lúc nào cũng hợp pháp. C 2011 (n1570) 6.3.2.3 7 cho biết hành vi không xác định nếu kết quả không được căn chỉnh chính xác. Nó sẽ được trên nền tảng hiện tại điển hình, vì vậy đây là okay nếu bạn đang lập kế hoạch mục tiêu của bạn cho phù hợp nhưng không nếu bạn đang lập kế hoạch cho tiêu chuẩn C hoặc muốn hiểu ngữ nghĩa của đặc tả C. –

+0

@EricPostpischil: Đúng - ngoại trừ việc không rõ ràng "hợp pháp" nghĩa là gì trong ngữ cảnh này (tiêu chuẩn không bao giờ sử dụng từ đó). Tôi đã cập nhật câu trả lời của mình. –

7

Bạn đang nhận được hành vi không xác định, bởi vì bạn đang đi qua các loại sai lầm khi printf. Khi bạn nói nó mong đợi một phao, nó thực sự mong đợi một double - nhưng bạn vượt qua một int.

Kết quả là nó in thông tin sai, vì printf hoàn toàn dựa vào chuỗi định dạng để truy cập vào đối số bạn vượt qua.

0

Ngoài những gì được nói bởi teppic,

xem xét,

int a = 5; 
int *p = &a; 

Trong trường hợp này, chúng tôi chỉ để trình biên dịch rằng p sẽ trỏ đến một số nguyên. Vì vậy, nó được biết rằng khi chúng tôi làm một cái gì đó như *p, tại thời gian chạy, không có. số byte tương đương với kích thước của một số int sẽ được đọc.

Nếu bạn chỉ định địa chỉ của một kiểu dữ liệu chiếm số x số byte cho con trỏ được khai báo để giữ địa chỉ của kiểu dữ liệu ít byte hơn x, bạn đọc sai số byte khi sử dụng toán tử gián tiếp.

+1

Ai nói 'float' lớn hơn 'int'? Nó được cho phép, nhưng có lẽ không. –

+0

@Steve Có, tôi đồng ý không phải lúc nào cũng như vậy. Nhưng một số hệ thống theo cách đó. Bởi lớn hơn tôi có nghĩa là chiếm nhiều byte hơn. Có lẽ ví dụ của tôi không phải là ví dụ tốt nhất, để đưa điểm của tôi đi qua. Tôi chỉ muốn chỉ ra rằng chúng tôi xác định loại con trỏ cho một lý do [giống như chúng ta phải đánh máy con trỏ 'void' trước khi sử dụng indirection trên nó] –

+0

Tôi cho rằng kích thước không phải là điểm. Tôi đã làm việc trên các hệ thống mà 'sizeof (int) == sizeof (double)'; hành vi của chương trình là không xác định như nhau trên một hệ thống như vậy. Bạn có nguy cơ gây ấn tượng rằng việc trộn các kiểu con trỏ như thế này là ok nếu các kích thước phù hợp. Và 'p' đặc biệt cần trỏ đến một' int', không chỉ bất kỳ số nguyên nào. –

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