2009-12-14 20 views
5

Tôi muốn biết lý do kỹ thuật (về bộ nhớ) tại sao đoạn mã này sẽ không làm việc:bộ nhớ động tạo ra bên trong một hàm

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

int* fun(int*); 
int main() 
{ 
    int a=5; 
    int* ptr; 
    // ptr=(int*)malloc(sizeof(int)); 
    fun(ptr); 
    a=*ptr; 

    printf("\n the val of a is:%d",a); 
    return 0; 
} 

void fun(int* ptr) 
{ 

    ptr = (int*)malloc(sizeof(int)); 
    *ptr = 115; 


} 

Tại sao điều này không làm việc? Tôi nghĩ rằng đống (quan trọng hơn là các địa chỉ) là phổ biến cho tất cả các biến của hàm trong ngăn xếp.

Ngoài ra, tại sao tính năng này hoạt động. Nếu tôi nhận xét việc cấp phát bộ nhớ bên trong chức năng vui vẻ và bỏ ghi chú trong phần chính. Nó hoạt động tốt.

+0

+1, nhân tiện, để có câu hỏi rõ ràng và chính xác. – DevSolar

+0

+1 Đây là một sai lầm cực kỳ phổ biến, tôi nhớ đã làm điều tương tự từ lâu rồi (trong một thiên hà cách xa rất xa) –

+0

@Andreas Brinck: :) – tomkaith13

Trả lời

13

Trong C, tất cả mọi thứ được truyền theo giá trị.

Những gì bạn đang chuyển đến niềm vui() là sao chép của con trỏ bạn có trong chính().

Điều đó có nghĩa là sao chép của ptr nhằm vào bộ nhớ được cấp phát và bộ nhớ đó được đặt thành 115.

Ptr trong main() vẫn trỏ vào vị trí chưa xác định vì chưa bao giờ được gán.

Hãy thử qua một con trỏ đến con trỏ, vì vậy mà trong vòng fun() bạn có quyền truy cập vào các con trỏ chính nó:

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

int* fun(int**); // <<-- CHANGE 
int main() 
{ 
    int a=5; 
    int* ptr; 
    // ptr=(int*)malloc(sizeof(int)); 
    fun(&ptr); // <<-- CHANGE 
    a=*ptr; 

    printf("\n the val of a is:%d",a); 
    return 0; 
} 

int* fun(int** another_ptr) // <<-- CHANGE 
{ 

    *another_ptr = (int*)malloc(sizeof(int)); // <<-- CHANGE 
    **another_ptr = 115; // <<-- CHANGE 
    return *another_ptr; 
} 

Các tùy chọn khác sẽ được thực hiện fun() thực sự trở lại con trỏ cập nhật (như quảng cáo) , và gán này để ptr:

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

int* fun(int*); 
int main() 
{ 
    int a=5; 
    int* ptr; 
    // ptr=(int*)malloc(sizeof(int)); 
    ptr = fun(ptr); // <<-- CHANGE 
    a=*ptr; 

    printf("\n the val of a is:%d",a); 
    return 0; 
} 

int* fun(int* another_ptr) 
{ 
    another_ptr = (int*)malloc(sizeof(int)); 
    *another_ptr = 115; 
    return another_ptr; // <<-- CHANGE 
} 

Edit: tôi đổi tên biến trong fun() để làm cho nó rõ ràng rằng nó là khác nhau từ một trong những bạn sử dụng trong main(). Cùng một tên không có nghĩa là bất cứ điều gì ở đây.

+0

@Devsolar: Có, nó sẽ là một giá trị vượt qua trong trường hợp malloc ở bên trong niềm vui(). Nhưng nó vẫn sẽ là một giá trị vượt qua nếu malloc được thực hiện trong main(). Bạn có thể vui lòng cho tôi biết lý do tại sao một người sẽ làm việc và một người sẽ không? – tomkaith13

+0

*** Mọi thứ *** đều vượt qua giá trị! Đọc lại dòng đầu tiên. –

+0

Trong mã của bạn: Giả sử bạn sẽ khởi tạo 'ptr' trong main(), thành 0x12345678 hoặc bất kỳ thứ gì. Dòng bình luận của bạn gọi 'malloc()' và gán giá trị trả về của nó cho 'ptr', làm cho nó trỏ tới vị trí bộ nhớ được phân bổ bởi' malloc(). '- Ngược lại, hàm của bạn' fun() ' một ** bản sao ** của 'ptr' (trỏ tới 0x12345678), gọi' malloc() ', gán giá trị trả về của nó cho bản sao của' ptr' và trả về. (Erroneously mà không trả lại 'int *' như quảng cáo.) Sau khi hàm trả về, bạn lại làm việc trên * real * 'ptr' - vẫn trỏ tới 0x12345678 ... – DevSolar

1

Bạn cần phải vượt qua địa chỉ của con trỏ trong chính nếu bạn muốn thay đổi nó:

fun(&ptr); 

(và thay đổi fun một cách thích hợp, tất nhiên)

Tại thời điểm này, nó làm thay đổi cục bộ biến ptr bên trong hàm và dĩ nhiên thay đổi đó không xuất hiện bất kỳ nơi nào khác.

3

Thông số chức năng fun() là bản sao của biến mà bạn đã chuyển vào fun(). Vì vậy, khi bạn thực hiện:

ptr = (int*)malloc(sizeof(int)); 
*ptr = 115; 

bạn chỉ thay đổi bản sao đó. Bạn nên thay đổi chữ ký chức năng:

int* fun(int** ptr) 
{ 
    *ptr = (int*)malloc(sizeof(int)); 
    **ptr = 115; 
} 

và thay đổi cách bạn gọi nó theo đó.

+0

@sharptooth: Vâng, nó sẽ là một đường chuyền giá trị trong trường hợp malloc là bên trong niềm vui(). Nhưng nó vẫn sẽ là một giá trị vượt qua nếu malloc được thực hiện trong main(). Bạn có thể vui lòng cho tôi biết lý do tại sao một người sẽ làm việc và một người sẽ không? – tomkaith13

+0

Bởi vì trong một, bạn khởi tạo con trỏ để trỏ đến bộ nhớ bên phải, sau đó sao chép con trỏ, và thiết lập những gì bản sao điểm đến 115. Trong khác, bạn sao chép con trỏ uninitialized, khởi tạo bản sao để trỏ đến bộ nhớ phải , và thiết lập những gì bản sao chỉ đến 115. Sự khác biệt trong trường hợp thứ hai là ** con trỏ ban đầu vẫn chưa được khởi tạo ** –

0

Bạn đang chuyển số ptr theo giá trị đến fun. fun sẽ nhận được một bản sao của ptr sẽ được sửa đổi. Bạn cần phải vượt qua ptr làm int**.

void fun(int** ptr) 
{ 
    *ptr = (int*)malloc(sizeof(int)); 
    **ptr = 115; 
} 

và gọi nó với:

fun(&ptr); 

(Tôi cũng loại bỏ các giá trị trả về từ fun vì nó không được sử dụng)

+0

@Andreas: Có, nó sẽ là một giá trị vượt qua trong trường hợp malloc ở bên trong vui vẻ(). Nhưng nó vẫn sẽ là một giá trị vượt qua nếu malloc được thực hiện trong main(). Bạn có thể vui lòng cho tôi biết lý do tại sao một người sẽ làm việc và một người sẽ không? – tomkaith13

0

Biến int* ptr được thông qua bởi giá trị cho các chức năng fun . Vì vậy, giá trị được gán cho ptr bên trong hàm sử dụng ptr = (int*)malloc(sizeof(int)); sẽ không được phản ánh bên ngoài hàm. Vì vậy, khi bạn thực hiện a = *ptr; trong main(), bạn đang cố gắng sử dụng con trỏ chưa được khởi tạo. Nếu bạn muốn để phản ánh những thay đổi được thực hiện để ptr ngoài chức năng thì bạn cần phải thay đổi chữ ký của thú vị để fun(int** ptr) và làm *ptr = (int*)malloc(sizeof(int));

1

Bạn đang bối rối về một vài điều ở đây, nhưng một cách dễ dàng để viết chức năng là:

int * fun() 
{ 
    int * ptr = (int*)malloc(sizeof(int)); 
    * ptr = 115; 
    return ptr; 
} 

Bây giờ bạn chịu trách nhiệm giải phóng bộ nhớ, vì vậy trong main():

int * ip = fun(); 
printf("%d", * ip); 
free(ip); 

Cách khác là chuyển địa chỉ của apointer (con trỏ tới con trỏ) tới hàm:

void fun(int ** pp) 
{ 
    * pp = (int*)malloc(sizeof(int)); 
    ** pp = 115; 
} 

thì mã của bạn trong main() trông giống như:

int * ip; 
fun(& ip); 
printf("%d", * ip); 
free(ip); 

Tôi nghĩ bạn có thể thấy rằng hàm đầu tiên đơn giản hơn để sử dụng.

0

Hãy nhớ rằng nếu bạn muốn một hàm sửa đổi giá trị của đối số, bạn phải chuyển một con trỏ tới đối số đó. Điều này áp dụng cho các giá trị con trỏ; nếu bạn muốn có một chức năng để sửa đổi một giá trị con trỏ (không phải những gì các điểm con trỏ đến), bạn phải vượt qua một con trỏ đến con trỏ rằng:

void fun (int **ptr) 
{ 
    /** 
    * Do not cast the result of malloc() unless you are 
    * working with a *very* old compiler (pre-C89). 
    * Doing so will supress a valuable warning if you 
    * forget to include stdlib.h or otherwise don't have 
    * a prototype for malloc in scope. 
    * 
    * Also, use the sizeof operator on the item you're 
    * allocating, rather than a type expression; if you 
    * change the base type of ptr (say from int to long), 
    * then you don't have to change all the corresponding 
    * malloc() calls as well. 
    * 
    * type of ptr = int ** 
    * type of *ptr = int * 
    * type of **ptr = int 
    */ 
    *ptr = malloc(sizeof **ptr); 
    *ptr = 115; 
} 

int main(void) 
{ 
    int *p; 
    fun(&p); 
    printf("Integer value stored at %p is %d\n", (void *) p, *p); 
    return 0; 
} 

BTW, bạn có một loại không phù hợp trong ví dụ của bạn; khai báo ban đầu của bạn về fun trả về một int *, nhưng định nghĩa trả về vô hiệu.

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