2010-03-20 47 views
7

Tôi luôn luôn nhầm lẫn về việc trả về một chuỗi ký tự hoặc một chuỗi từ một hàm. Tôi đã nói rằng có thể có rò rỉ bộ nhớ bởi vì bạn không biết khi nào bộ nhớ sẽ bị xóa?Cách trả về một chuỗi ký tự từ một hàm

Ví dụ: trong mã bên dưới, cách triển khai foo() để làm cho đầu ra của mã là "Hello World"?

void foo (  )    // you can add parameters here. 
{ 

} 

int main() 
{ 
    char *c; 
    foo ( ); 
    printf ("%s",c); 
    return 0; 
} 

Ngoài ra, nếu các kiểu trả về của foo() là không có hiệu lực, nhưng bạn có thể quay trở lại char*, những gì nên nó được?

+0

Yêu cầu của bạn trả về 'char *' thay vì 'char const *' có hiệu lực buộc chúng ta giả định rằng một chuỗi ký tự đơn giản sẽ không đủ và do đó 'return" hello world ";' là một giải pháp không hợp lệ. Yêu cầu của bạn về constness là gì? –

Trả lời

9

Tôi giả định rằng chúng tôi không thể sửa đổi chính. Để chương trình của bạn hoạt động mà không bị rò rỉ, bạn cần một thứ gì đó để có bộ nhớ tĩnh:

void foo(char*& pC) // reference 
{ 
    static char theString[] = "thingadongdong"; 

    pC = theString; 
} 

Nhưng thực sự, đây không phải là mã C++ thông thường. Bạn sẽ sử dụng std::stringstd::cout, vì vậy bạn không phải lo lắng về bộ nhớ:

std::string foo(void) 
{ 
    return "better thingadongdong"; 
} 

int main(void) 
{ 
    // memory management is done 
    std::cout << foo() << std::endl; 
} 

Nếu bạn đang tự hỏi nếu có điều gì cần phải được deallocated bằng tay, nó được thực hiện sai.

+0

Không phải lúc nào. Sau khi tất cả, std :: string hiện việc phân bổ bằng tay. –

+1

Eh, khi bạn không ở trong "chế độ viết thư viện" tôi nên nói. :) – GManNickG

+0

Hiện chúng ta đi. Tốt hơn nhiều. Ý kiến ​​của tôi chính xác. –

2

Something như thế này:

void foo(char ** pChar) 
{ 
    // Make sure the string is shorter 
    // than the buffer 
    *pChar = new char[256]; 
    strcpy(*pChar,"Hello World!"); 
} 

Sau đó gọi nó là như thế này:

foo(&c); 

Như đã đề cập trong các bình luận, hãy cẩn thận chuỗi bạn đang lưu trữ nhỏ hơn so với bộ đệm hoặc bạn sẽ nhận được một ... tràn ngăn xếp! (Pun dự định)

+0

Và nếu tôi làm 'foo (0)'? – GManNickG

+1

Bạn không thể dereference '0' Điều đó sẽ sụp đổ chương trình của bạn. –

+0

Tại sao con trỏ trỏ đến con trỏ là cần thiết? Tại sao không chỉ là con trỏ? – skydoor

4

Tôi luôn luôn nhầm lẫn về trả về chuỗi ký tự bằng chữ hoặc chuỗi từ một hàm.

Bất biến, chuỗi chữ

Theo tôi được biết, bạn là an toàn để trả về một chuỗi chữ trực tiếp nếu kiểu trả về được khai báo const, tuyên bố rằng chuỗi không có ý định phải được thay đổi. Điều này có nghĩa là bạn không cần phải lo lắng về tuổi thọ của sự rò rỉ chuỗi/bộ nhớ.

Biên Đổi, chuỗi phi đen

Tuy nhiên, nếu bạn cần một chuỗi mà bạn có thể thay đổi tại chỗ, bạn cần phải xem xét tuổi thọ của chuỗi và kích thước của bộ nhớ phân bổ trong đó nó được lưu trữ. Điều này trở thành vấn đề, vì bạn không còn có thể trả về cùng một bộ nhớ chứa chuỗi cho mỗi lần gọi hàm, vì việc sử dụng trước đó có thể đã thay đổi nội dung của bộ nhớ đó và/hoặc vẫn có thể được sử dụng. Do đó một bộ nhớ mới phải được cấp phát để giữ chuỗi được trả về.

Đây là nơi tiềm năng xảy ra rò rỉ và cần lựa chọn nơi phân bổ và phân bổ sẽ xảy ra. Bạn có thể có chức năng tự phân bổ bộ nhớ và trạng thái trong tài liệu mà điều này xảy ra và quy định trong đó người gọi phải giải phóng bộ nhớ khi nó không còn cần thiết nữa (ngăn chặn rò rỉ). Điều này có nghĩa là hàm có thể đơn giản trả về một char *.

Tùy chọn khác là chuyển một số bộ nhớ đến chức năng được người gọi cấp phát và có chức năng đặt chuỗi bên trong bộ nhớ đó. Trong trường hợp này, người gọi cả hai phân bổ và chịu trách nhiệm giải phóng bộ nhớ đó.

Cuối cùng, tôi đã đề cập rằng kích thước bộ nhớ và chuỗi cần được quản lý khi sử dụng chuỗi có thể thay đổi. Việc phân bổ cần phải đủ lớn cho chuỗi ban đầu được thiết lập bởi hàm và cũng cho bất kỳ thay đổi nào được thực hiện sau hàm, trước khi bộ nhớ được giải phóng. Không thực hiện được điều này đúng cách có thể gây tràn bộ đệm bằng cách viết một chuỗi dài để vừa với bộ nhớ ban đầu được cấp phát; điều này cực kỳ nguy hiểm đối với sức khỏe và bảo mật của chương trình của bạn. Nó có thể gây ra lỗi và lỗ hổng bảo mật cực kỳ khó phát hiện (do nguồn của lỗi - tràn - có thể bị loại bỏ xa khỏi các triệu chứng được thấy khi chương trình không thành công).

5

Vì việc sử dụng cũ của char * không được chấp nhận, bạn có thể không đơn giản sử dụng chuỗi không?

const char* func1() {return "string literal";}

string func2() {return "another string literal";}

Cả hai hoạt động tốt, không có cảnh báo trình biên dịch.

Tuy nhiên

char* func3() {return "yet another string literal";}

sẽ không biên dịch ở tất cả.

"Một chuỗi đen được tĩnh được phân bổ để nó là an toàn để trở lại một từ một chức năng: Cũng sẽ

char* func4() {return &"a ref to a string literal?";}

Stroustrup nói trong "The C++ Programming Language"(Third Edition).

const char* error_message (int i)` 
{ 
//... 
return "range error"; 
} 

Việc giữ bộ nhớ lỗi phạm vi sẽ không biến mất sau khi cuộc gọi của error_messages(). "

Vì vậy, mỗi chuỗi ký tự trong một chương trình được cấp phát trong bộ nhớ nhỏ của chính nó kéo dài trong suốt thời gian của chương trình (tức là được phân bổ tĩnh). Đưa const vào trước char * cho phép trình biên dịch biết rằng bạn không có ý định (và không thể) thay đổi đoạn ký tự nhỏ của chuỗi ký tự có thể nguy hiểm, vì vậy chúng cho phép chuyển bài này mặc dù chuyển đổi từ chuỗi ký tự thành char * không được chấp nhận.

Trả về thay cho chuỗi phải sao chép chuỗi ký tự thành một đối tượng thuộc loại chuỗi, bộ nhớ mà người gọi chịu trách nhiệm.

Dù bằng cách nào cũng không có rò rỉ bộ nhớ: mọi chuỗi ký tự đều có phần bộ nhớ riêng được dọn sạch khi chấm dứt chương trình; trở về const char * trả về một con trỏ đến một phần của bộ nhớ của chữ (biết bạn không thể thay đổi nó); và trở về một chuỗi làm cho một bản sao thành một đối tượng chuỗi tồn tại trong mã của người gọi được làm sạch bởi người gọi.

Mặc dù có vẻ như một chút ký hiệu xấu xí, tôi đoán họ đã để lại từ khóa * để giữ giá rẻ thay thế (không có bản sao).

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