2012-09-12 31 views
11

Tôi mới bắt đầu với C và tôi tự học. tôi đang tạo ra các chức năng sau:C "lỗi: hàm trả về địa chỉ của biến cục bộ"

char *foo(int x){ 
    if(x < 0){ 
     char a[1000]; 
     char b = "blah"; 
     x = x - 1; 
     char *c = foo(x); 
     strcpy(a, b); 
     strcat(a, c); 
     return a; 
     } 
    blah ... 
} 

tôi về cơ bản cố gắng để trả về một chuỗi nối, nhưng tôi nhận được lỗi sau:

"lỗi: hàm trả về địa chỉ của biến cục bộ", bất cứ đề nghị, làm thế nào để sửa lỗi này?

+0

thể trùng lặp của [C Cảnh báo: Chức năng trả về địa chỉ của biến cục bộ] (http://stackoverflow.com/questions/6897914/c-warning-function-returns-address-of-local-variable) – netcoder

+0

Khi bạn viết câu hỏi của bạn, nó gợi ý một vài bản sao dựa trên nó. Có lẽ bạn nên kiểm tra chúng. – netcoder

+0

tôi nghĩ rằng nó có thể hữu ích http://stackoverflow.com/a/6897993 –

Trả lời

1

a được định nghĩa cục bộ trong hàm và không thể được sử dụng bên ngoài hàm. Nếu bạn muốn trả về một mảng char từ chức năng, bạn sẽ cần phải phân bổ nó tự động:

char *a = malloc(1000); 

Và tại một số điểm gọi free trên con trỏ trở lại.

Bạn cũng sẽ thấy cảnh báo tại dòng này: char b = "blah";: bạn đang cố gán một chuỗi ký tự cho một char.

2

a là một mảng cục bộ cho hàm. Khi hàm trả về nó không tồn tại nữa và do đó bạn không nên trả lại địa chỉ của biến cục bộ.
Nói cách khác, đời của a là trong phạm vi ({, }) của hàm và nếu bạn trả về một con trỏ đến nó những gì bạn có là một trỏ trỏ đến một số bộ nhớ mà không phải là hợp lệ. Các biến số này cũng được gọi là các biến số tự động vì tuổi thọ của chúng được tự động quản lý mà bạn không cần quản lý một cách rõ ràng.

Vì bạn cần mở rộng biến để tồn tại vượt quá phạm vi của hàm, bạn cần phân bổ một mảng trên heap và trả về một con trỏ tới nó.

char *a = malloc(1000); 

Bằng cách này, mảng a nằm trong bộ nhớ cho đến khi bạn gọi một free() trên cùng một địa chỉ.
Đừng quên làm như vậy hoặc bạn sẽ bị rò rỉ bộ nhớ.

28

Biến cục bộ có tuổi thọ chỉ kéo dài bên trong khối mà nó được xác định. Thời điểm anh ta kiểm soát đi bên ngoài khối trong đó biến cục bộ được xác định, dung lượng lưu trữ cho biến không được phân bổ nhiều hơn (không được bảo đảm). Do đó việc sử dụng địa chỉ bộ nhớ của biến ngoài vùng đời của biến sẽ là hành vi không xác định. am, có

Mặt khác, bạn có thể thực hiện các thao tác sau.

char *str_to_ret = malloc (sizeof (char) * required_size); 
    . 
    . 
    . 
return str_to_ret; 

Thay vào đó, hãy sử dụng str_to_ret. Và khi return ing str_to_ret, địa chỉ được phân bổ bởi malloc sẽ được trả lại. Bộ nhớ được phân bổ bởi malloc được cấp phát từ heap, trong đó có vòng đời kéo dài toàn bộ quá trình thực hiện của chương trình. Vì vậy, bạn có thể truy cập vị trí bộ nhớ từ bất kỳ khối nào và bất kỳ lúc nào trong khi chương trình đang chạy.

Cũng lưu ý rằng, thực hành tốt là sau khi bạn đã thực hiện với khối bộ nhớ được cấp phát, free để lưu khỏi rò rỉ bộ nhớ. Khi bạn giải phóng bộ nhớ, bạn không thể truy cập lại khối đó.

+0

Sitenote: Encapsulation/Lifetime/Responsibility Design-Lỗ hổng trong giải pháp: CALLER khởi tạo một Malloc - nhưng CALLED phải giải phóng nó. Ngoài ra nếu bạn không khử trùng/kiểm tra các giá trị cho chức năng bạn có thể dễ dàng malloc một khối lớn dữ dội trong heap ... – Gewure

+0

Chỉ là một ví dụ để giải thích tuổi thọ của một đối tượng. – phoxis

3

dòng này:

char b = "blah"; 

là không tốt - giá trị trái của bạn cần phải được một con trỏ.

Mã của bạn cũng có nguy cơ tràn ngăn xếp, vì kiểm tra đệ quy của bạn không bị ràng buộc giá trị giảm của x.

Dù sao, thông báo lỗi thực tế bạn nhận được là vì char a là một biến tự động; thời điểm bạn return nó sẽ chấm dứt tồn tại. Bạn cần một cái gì đó khác hơn là một biến tự động.

-1
char b = "blah"; 

nên là:

char *b = "blah"; 
+0

điều này sẽ không tạo ra sự khác biệt. Đọc câu trả lời của tôi tại sao! – Gewure

2

THỰC HÀNH:

Tôi hiện đang giảng dạy một người bạn của tôi những khái niệm cơ bản của C - và đến với điều này rất đơn giản và thẳng về phía trước (tôi hy vọng như vậy) mã ví dụ mà về cơ bản giải thích tất cả mọi thứ. Không muốn giấu nó khỏi các bạn! :)

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

/* function header definitions */ 
char* getString();      //<- with malloc (good practice) 
char * getStringNoMalloc(); //<- without malloc (fails! don't do this!) 
void getStringCallByRef(char* reference); //<- callbyref (good practice) 

/* the main */ 
int main(int argc, char*argv[]) { 

    //######### calling with malloc 
    char * a = getString(); 
    printf("MALLOC### a = %s \n", a); 
    free(a); 

    //######### calling without malloc 
    char * b = getStringNoMalloc(); 
    printf("NO MALLOC### b = %s \n", b); //this doesnt work, question to yourself: WHY? 
    //HINT: the warning says that a local reference is returned. ??! 
    //NO free here! 

    //######### call-by-reference 
    char c[100]; 
    getStringCallByRef(c); 
    printf("CALLBYREF ### c = %s \n", c); 

    return 0; 
} 

//WITH malloc 
char* getString() { 

    char * string; 
    string = malloc(sizeof(char)*100); 

    strcat(string, "bla"); 
    strcat(string, "/"); 
    strcat(string, "blub"); 

    printf("string : '%s'\n", string); 

    return string; 
} 

//WITHOUT malloc (watch how it does not work this time) 
char* getStringNoMalloc() { 

    char string[100] = {}; 

    strcat(string, "bla"); 
    strcat(string, "/"); 
    strcat(string, "blub"); 
    //INSIDE this function "string" is OK 
    printf("string : '%s'\n", string); 

    return string; //but after returning.. it is NULL? :) 
} 

// ..and the call-by-reference way to do it (prefered) 
void getStringCallByRef(char* reference) { 

    strcat(reference, "bla"); 
    strcat(reference, "/"); 
    strcat(reference, "blub"); 
    //INSIDE this function "string" is OK 
    printf("string : '%s'\n", reference); 
    //OUTSIDE it is also OK because we hand over a reference defined in MAIN 
    // and not defined in this scope (local), which is destroyed after the function finished 
} 

Khi biên dịch nó, bạn sẽ có được [dự định] cảnh báo:

[email protected]:~$ gcc -o example.o example.c 
example.c: In function ‘getStringNoMalloc’: 
example.c:58:16: warning: function returns address of local variable [-Wreturn-local-addr] 
     return string; //but after returning.. it is NULL? :) 
      ^~~~~~ 

... về cơ bản những gì chúng ta đang thảo luận ở đây!

chạy ví dụ của tôi mang lại kết quả này:

[email protected]:~$ ./example.o 
string : 'bla/blub' 
MALLOC### a = bla/blub 
string : 'bla/blub' 
NO MALLOC### b = (null) 
string : 'bla/blub' 
CALLBYREF ### c = bla/blub 

LÝ THUYẾT:

này đã được trả lời rất độc đáo bởi @phoxis tài khoản. Về cơ bản hãy suy nghĩ theo cách này: Mọi thứ ở giữa {}phạm vi địa phương, do đó, C-Standard là "không xác định" bên ngoài. Bằng cách sử dụng malloc bạn lấy bộ nhớ từ HEAP (phạm vi chương trình) chứ không phải từ STACK (phạm vi chức năng) - do đó có thể nhìn thấy từ bên ngoài. Cách chính xác thứ hai để thực hiện là gọi theo tham chiếu. Ở đây bạn định nghĩa var bên trong parent-scope, do đó nó đang sử dụng STACK (vì phạm vi cha là main()).

TÓM TẮT:

3 cách để làm điều đó, Một trong số đó sai. C là loại vụng về để chỉ có một hàm trả về một chuỗi có kích thước động. Hoặc bạn phải malloc và sau đó giải phóng nó, hoặc bạn phải gọi theo tham chiếu. Hoặc sử dụng C++;)

1

Không cần sử dụng malloc hoặc gọi theo tham chiếu.Bạn có thể khai báo một con trỏ trong hàm và đặt nó vào chuỗi/mảng mà bạn muốn trả về.

Sử dụng @ đang Gewure như cơ sở:

char *getStringNoMalloc(void){ 
    char string[100] = {}; 
    char *s_ptr = string; 

    strcat(string, "bla"); 
    strcat(string, "/"); 
    strcat(string, "blub"); 
    //INSIDE this function "string" is OK 
    printf("string : '%s'\n", string); 

    return s_ptr; 
} 

tác phẩm hoàn hảo.

Với một phiên bản không lặp của mã trong câu hỏi ban đầu:

char *foo(int x){  
    char a[1000]; 
    char *a_ptr = a; 
    char *b = "blah";  

    strcpy(a, b); 

    return a_ptr; 
} 
Các vấn đề liên quan