2008-10-13 39 views
33

Tôi đã cố gắng để hiểu điều gì đó với con trỏ, vì vậy tôi đã viết mã này:con trỏ In ấn trong C

#include <stdio.h> 

int main(void) 
{ 
    char s[] = "asd"; 
    char **p = &s; 

    printf("The value of s is: %p\n", s); 
    printf("The direction of s is: %p\n", &s); 

    printf("The value of p is: %p\n", p); 
    printf("The direction of p is: %p\n", &p); 

    printf("The direction of s[0] is: %p\n", &s[0]); 
    printf("The direction of s[1] is: %p\n", &s[1]); 
    printf("The direction of s[2] is: %p\n", &s[2]); 

    return 0; 
} 

Khi biên dịch nó với gcc tôi nhận được những cảnh báo này:

$ gcc main.c -o main-bin -ansi -pedantic -Wall -lm 
main.c: In function ‘main’: 
main.c:6: warning: initialization from incompatible pointer type 
main.c:9: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char (*)[4]’ 
main.c:11: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char **’ 
main.c:12: warning: format ‘%p’ expects type ‘void *’, but argument 2 has type ‘char ***’ 

(Cờ cho gcc là vì tôi phải là C89)

Tại sao các loại con trỏ không tương thích? Không phải là tên của một mảng một con trỏ đến phần tử đầu tiên của nó? Vì vậy, nếu s là con trỏ đến 'a', &s phải là char **, không? Và tại sao tôi nhận được các cảnh báo khác? Tôi có phải đúc các con trỏ với (void *) để in chúng không?

Và khi chạy tôi nhận được một cái gì đó như thế này:

$ ./main-bin 
The value of s is: 0xbfb7c860 
The direction of s is: 0xbfb7c860 
The value of p is: 0xbfb7c860 
The direction of p is: 0xbfb7c85c 
The direction of s[0] is: 0xbfb7c860 
The direction of s[1] is: 0xbfb7c861 
The direction of s[2] is: 0xbfb7c862 

thế nào có thể giá trị của s và nó hướng (và tất nhiên giá trị của p) có giống nhau không?

Trả lời

24

"s" không phải là "char *", đó là "char [4]". Và như vậy, "& s" không phải là một "char **", nhưng thực sự "một con trỏ đến một mảng của 4 characater". Trình biên dịch của bạn có thể xử lý "& s" như thể bạn đã viết "& s [0]", gần như giống nhau, nhưng là "char *".

Khi bạn viết "char ** p = & s;" bạn đang cố gắng nói "Tôi muốn p được đặt thành địa chỉ của thứ hiện đang trỏ tới" asd ". Nhưng hiện tại không có gì là điểm thành" asd ". Chỉ có một mảng mà giữ" asd ";.

char s[] = "asd"; 
char *p = &s[0]; // alternately you could use the shorthand char*p = s; 
char **pp = &p; 
+0

Cảm ơn bạn rất nhiều James ... – alcuadrado

+0

một mảng cũng có nghĩa là địa chỉ bộ nhớ tiếp giáp của loại cụ thể vì 's' phân rã thành con trỏ, chúng ta có thể nói đó là con trỏ tới' char * '[phần tử đầu tiên của mảng] –

+0

@ CholthiPaulTtiopic - Không. Trước hết, đó là "chúng ta có thể nói đó là một con trỏ tới' char' "(không phải' char * '). NHƯNG, bạn có thể sử dụng nó như một con trỏ để 'char' trong một số trường hợp, nhưng điều đó không làm cho nó một. "5.0" có thể là một số nguyên, nhưng nó không phải là một 'int', mặc dù bạn có thể sử dụng nó ở một số nơi có một' int'. –

0

Bạn đã sử dụng:

char s[] = "asd"; 

Dưới đây là thực sự chỉ đến các byte "asd". Địa chỉ của s, cũng sẽ trỏ đến vị trí này.

Nếu bạn sử dụng:

char *s = "asd"; 

giá trị của s và & s sẽ khác nhau, như s sẽ thực sự là một con trỏ đến các byte "asd".

Bạn sử dụng:

char s[] = "asd"; 
char **p = &s; 

Đây là điểm để các byte "asd". p là một con trỏ trỏ đến một con trỏ tới các ký tự và đã được đặt thành một địa chỉ của các ký tự. Nói cách khác, bạn có quá nhiều số không trong p. Nếu bạn đã sử dụng char * s = "asd", bạn có thể sử dụng tính năng bổ sung này.

+0

Nhưng tôi muốn p là một con trỏ tới, chứ không phải để ' a '. – alcuadrado

+0

@alcuadrado: Nhưng s LÀ 'a'. Sự hiểu lầm cơ bản của bạn về vấn đề này là vấn đề gốc. –

+0

Rất tiếc - Tôi đang gõ bận khi đăng tin nhắn. – selwyn

13

Có, trình biên dịch của bạn đang chờ void *. Chỉ cần bỏ chúng để làm mất hiệu lực *.

/* for instance... */ 
printf("The value of s is: %p\n", (void *) s); 
printf("The direction of s is: %p\n", (void *) &s); 
+0

Điều đó là sai. Việc truyền tới void * chỉ đơn thuần là che giấu vấn đề, không giải quyết nó. –

+1

Suy nghĩ đầu tiên của tôi là anh ta chỉ muốn in các số để xem các giá trị mà trình biên dịch đã gán, và việc bỏ trống sẽ cho phép anh ta, bất kể có vấn đề gì trong các tuyên bố của anh ta. Nhưng có, đó là chắc chắn tránh được vấn đề. – indiv

+7

Truyền tới 'void *' đối với định dạng '% p' trong hàm variadic thực sự là __required__ theo tiêu chuẩn ngôn ngữ. – Jens

1

dòng thay đổi:

char s [] = "asd";

tới:

char * s = "asd";

và mọi thứ sẽ trở rõ ràng hơn

+2

Lỗi sẽ biến mất, nhưng không có gì rõ ràng hơn, khi bạn sửa chữa tránh được lời giải thích về vấn đề ban đầu. –

4

Nếu bạn vượt qua tên của một mảng như một tham số để một chức năng, nó được coi như là nếu bạn đã vượt qua địa chỉ của mảng. Vì vậy, & s và s là các đối số giống nhau. Xem K & R 5.3. & s [0] cũng giống như & s, vì nó lấy địa chỉ của phần tử đầu tiên của mảng, giống như lấy địa chỉ của chính mảng đó.

Đối với tất cả những người khác, mặc dù tất cả các con trỏ về cơ bản là các vị trí bộ nhớ chúng vẫn được nhập và trình biên dịch sẽ cảnh báo về việc gán một loại con trỏ cho một loại khác.

  • void* p; nói p là một địa chỉ bộ nhớ, nhưng tôi không biết những gì trong bộ nhớ
  • char* s; nói s là một địa chỉ bộ nhớ, và byte đầu tiên chứa một nhân vật
  • char** ps; nói ps là một địa chỉ bộ nhớ và bốn byte ở đó (đối với hệ thống 32 bit) chứa một con trỏ kiểu char *.

cf (phiên bản e-book của K & R) http://www.oberon2005.ru/paper/kr_c.pdf

1

Bạn không thể thay đổi giá trị (địa chỉ ví dụ của) một mảng tĩnh. Theo thuật ngữ kỹ thuật, giá trị của một mảng là địa chỉ của phần tử đầu tiên của nó. Do đó s == &s. Nó chỉ là một ngôn ngữ của ngôn ngữ.

1

Thông thường, nó được coi là phong cách nghèo để cast không cần thiết các con trỏ tới (void *) ở đây, tuy nhiên, bạn cần các diễn viên đến (void *) trên các đối số printf vì printf là variadic Nguyên mẫu. không cho trình biên dịch biết loại nào để chuyển đổi con trỏ đến trang web cuộc gọi

2

Nó không phải là con trỏ đến ký tự char* mà là một con trỏ tới mảng 4 ký tự: char* [4].Với g ++ nó không biên dịch:

main.cpp: In function ‘int main(int, char**)’: main.cpp:126: error: cannot convert ‘char (*)[4]’ to ‘char**’ in initialization

Hơn nữa, các trang linux man says:

p

The void * pointer argument is printed in hexadecimal (as if by %#x or %#lx). It shoud be pointer to void.

Bạn có thể thay đổi mã của bạn để:

char* s = "asd"; 
char** p = &s; 

printf("The value of s is: %p\n", s); 
printf("The address of s is: %p\n", &s); 

printf("The value of p is: %p\n", p); 
printf("The address of p is: %p\n", &p); 

printf("The address of s[0] is: %p\n", &s[0]); 
printf("The address of s[1] is: %p\n", &s[1]); 
printf("The address of s[2] is: %p\n", &s[2]); 

kết quả:

Giá trị của s là: 0x403f00

Địa chỉ của s là: 0x7fff2df9d588

Giá trị của p là: 0x7fff2df9d588

Địa chỉ của p là: 0x7fff2df9d580

Địa chỉ của s [0] là: 0x403f00

Các địa chỉ của s [1] là: 0x403f01

địa chỉ của s [2] là: 0x403f02

+0

Xin chào, cảm ơn bạn đã trả lời câu hỏi cũ này. May mắn là tôi đã học được nhiều hơn về lập trình trong 4 năm qua. Nhưng tôi nghĩ ý định của bạn là cung cấp một cái nhìn sâu sắc hơn cho những người đến câu hỏi này. Xem xét rằng, tôi sẽ phải nói rằng biên dịch mã C với một trình biên dịch C++ không phải là một điều tốt, họ không phải là cùng một ngôn ngữ, và khó hiểu nó làm cho lỗi. PS: hộp bình luận này hút haha ​​ – alcuadrado

+0

@alcuadrado oh, không nhìn thấy ngày, nó là thời tiền sử! – 4pie0

+0

Tôi hiểu, không sao cả! – alcuadrado