Chữ ký hàm của bạn cần phải là:
const char * myFunction()
{
return "My String";
}
Lý lịch:
Nó rất cơ bản đối với C & C ++, nhưng cần thảo luận thêm một chút.
Trong C (& C ++ cho vấn đề đó), một chuỗi chỉ là một mảng các byte được kết thúc bằng byte 0 - do đó thuật ngữ "string-zero" được sử dụng để biểu thị hương vị cụ thể này của chuỗi. Có những loại chuỗi khác, nhưng trong C (& C ++), hương vị này vốn dĩ được ngôn ngữ tự hiểu. Các ngôn ngữ khác (Java, Pascal, v.v.) sử dụng các phương pháp luận khác nhau để hiểu "chuỗi của tôi".
Nếu bạn đã từng sử dụng Windows API (bằng C ++), bạn sẽ thấy các tham số hàm khá thường xuyên như: "LPCSTR lpszName". Phần 'sz' đại diện cho khái niệm 'chuỗi-không': một mảng các byte với dấu chấm hết (/ không) null.
Làm rõ:
Vì lợi ích của 'phần giới thiệu' này, tôi sử dụng từ 'byte' và 'ký tự' thay thế cho nhau, vì nó dễ học hơn theo cách này. Lưu ý rằng có các phương pháp khác (hệ thống ký tự rộng và nhiều byte ( mbcs )) được sử dụng để xử lý các ký tự quốc tế. UTF-8 là một ví dụ về mbcs. Vì lợi ích của phần giới thiệu, tôi lặng lẽ 'bỏ qua' tất cả những điều này.
Ký ức:
Điều này có nghĩa là một chuỗi như "chuỗi của tôi" thực sự sử dụng 9 + 1 (= 10!) Byte. Điều quan trọng là bạn phải biết khi nào thì cuối cùng bạn cũng có thể phân bổ chuỗi động.
Vì vậy, nếu không có 'số không kết thúc' này, bạn không có một chuỗi. Bạn có một mảng ký tự (còn gọi là bộ đệm) quanh quẩn trong bộ nhớ.
Tuổi thọ của dữ liệu:
Việc sử dụng hàm theo cách này:
const char * myFunction()
{
return "My String";
}
int main()
{
const char* szSomeString = myFunction(); // Fraught with problems
printf("%s", szSomeString);
}
... thường sẽ khiến bạn gặp phải các lỗi ngẫu nhiên không có ngoại lệ / phân đoạn và tương tự, đặc biệt là 'xuống đường'.
Tóm lại, mặc dù câu trả lời của tôi là đúng - 9 lần trong số 10 bạn sẽ kết thúc với một chương trình bị treo nếu bạn sử dụng theo cách đó, đặc biệt nếu bạn nghĩ rằng nên làm theo cách đó. Tóm lại: Nói chung là không.
Ví dụ, hãy tưởng tượng một lúc nào đó trong tương lai, chuỗi bây giờ cần được thao tác theo một cách nào đó. Nói chung, một lập trình viên sẽ 'đi theo con đường dễ dàng' và (cố gắng) viết mã như thế này:
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}
Đó là, chương trình của bạn sẽ sụp đổ vì trình biên dịch (có thể / không) đã phát hành bộ nhớ được sử dụng bởi szBuffer
do thời gian printf()
trong main()
được gọi. (Trình biên dịch của bạn cũng nên cảnh báo trước cho bạn về những vấn đề như vậy.)
Có hai cách để trả về các chuỗi không quá dễ dàng.
- trả về bộ đệm (tĩnh hoặc phân bổ động) tồn tại trong một thời gian. Trong C ++ sử dụng 'các lớp trợ giúp' (ví dụ
std::string
:) để xử lý tuổi thọ của dữ liệu (yêu cầu thay đổi giá trị trả về của hàm) hoặc
- truyền một bộ đệm cho hàm được điền thông tin.
Lưu ý rằng không thể sử dụng chuỗi mà không sử dụng con trỏ trong C. Như tôi đã trình bày, chúng đồng nghĩa. Ngay cả trong C ++ với các lớp mẫu, luôn có các bộ đệm (nghĩa là con trỏ) được sử dụng trong nền.
Vì vậy, để trả lời tốt hơn (câu hỏi hiện đã được sửa đổi). (Chắc chắn sẽ có nhiều 'câu trả lời khác' có thể được cung cấp.)
Câu trả lời An toàn hơn:
Ví dụ 1, sử dụng các chuỗi được cấp phát tĩnh:
const char* calculateMonth(int month)
{
static char* months[] = {"Jan", "Feb", "Mar" .... };
static char badFood[] = "Unknown";
if (month<1 || month>12)
return badFood; // Choose whatever is appropriate for bad input. Crashing is never appropriate however.
else
return months[month-1];
}
int main()
{
printf("%s", calculateMonth(2)); // Prints "Feb"
}
Điều mà 'static' làm ở đây (nhiều lập trình viên không thích kiểu 'cấp phát' này) là các chuỗi được đưa vào phân đoạn dữ liệu của chương trình. Đó là, nó được phân bổ vĩnh viễn.
Nếu bạn chuyển sang C ++, bạn sẽ sử dụng các chiến lược tương tự:
class Foo
{
char _someData[12];
public:
const char* someFunction() const
{ // The final 'const' is to let the compiler know that nothing is changed in the class when this function is called.
return _someData;
}
}
... nhưng có lẽ sẽ dễ dàng hơn khi sử dụng các lớp trợ giúp, chẳng hạn như std::string
nếu bạn đang viết mã để sử dụng cho riêng mình (và không phải là một phần của thư viện để chia sẻ với người khác).
Ví dụ 2, sử dụng bộ đệm do người gọi xác định:
Đây là cách dễ dàng hơn để truyền các chuỗi xung quanh. Dữ liệu trả về không bị bên gọi thao túng. Đó là, ví dụ 1 có thể dễ dàng bị một bên gọi điện lạm dụng và khiến bạn gặp phải các lỗi ứng dụng. Bằng cách này, nó an toàn hơn nhiều (mặc dù sử dụng nhiều dòng mã hơn):
void calculateMonth(int month, char* pszMonth, int buffersize)
{
const char* months[] = {"Jan", "Feb", "Mar" .... }; // Allocated dynamically during the function call. (Can be inefficient with a bad compiler)
if (!pszMonth || buffersize<1)
return; // Bad input. Let junk deal with junk data.
if (month<1 || month>12)
{
*pszMonth = '\0'; // Return an 'empty' string
// OR: strncpy(pszMonth, "Bad Month", buffersize-1);
}
else
{
strncpy(pszMonth, months[month-1], buffersize-1);
}
pszMonth[buffersize-1] = '\0'; // Ensure a valid terminating zero! Many people forget this!
}
int main()
{
char month[16]; // 16 bytes allocated here on the stack.
calculateMonth(3, month, sizeof(month));
printf("%s", month); // Prints "Mar"
}
Có rất nhiều lý do tại sao phương pháp thứ hai lại tốt hơn, đặc biệt nếu bạn đang viết một thư viện để người khác sử dụng (bạn không cần phải khóa vào một lược đồ phân bổ / thỏa thuận cụ thể, các bên thứ ba không thể phá mã của bạn, và bạn không cần phải liên kết đến một thư viện quản lý bộ nhớ cụ thể), nhưng giống như tất cả các mã, tùy thuộc vào bạn về những gì bạn thích nhất. Vì lý do đó, hầu hết mọi người chọn ví dụ 1 cho đến khi họ bị cháy nhiều lần đến mức họ từ chối viết theo cách đó nữa;)
Tuyên bố từ chối trách nhiệm:
Tôi đã nghỉ hưu vài năm trước và giờ C của tôi hơi bị gỉ. Tất cả mã demo này phải được biên dịch đúng cách với C (mặc dù vậy, nó cũng được cho bất kỳ trình biên dịch C ++ nào).