您的功能签名必须为:
const char * myFunction()
{
return "My String";
}
背景:
对于C&C ++来说,它是如此基础,但是应该进行更多的讨论。
在C语言中(字符串和C ++),字符串只是以零字节结尾的字节数组-因此,术语“字符串-零”用于表示字符串的这种特殊形式。还有其他种类的字符串,但是在C(&C ++)中,这种语言本身是语言固有的理解。其他语言(Java,Pascal等)使用不同的方法来理解“我的字符串”。
如果您曾经使用Windows API(C ++),则会经常看到类似“ LPCSTR lpszName”的函数参数。“ sz”部分表示“字符串零”的概念:带有空(/零)终止符的字节数组。
澄清:
为了这个“介绍”,我将“字节”和“字符”一词互换使用,因为这样更容易学习。请注意,还有其他方法(宽字符和多字节字符系统(mbcs))用于应对国际字符。UTF-8是mbcs的示例。为了介绍起见,我悄悄地“跳过”了所有这一切。
记忆:
这意味着像“我的字符串”之类的字符串实际上使用9 + 1(= 10!)个字节。重要的是要知道何时最终可以动态分配字符串。
因此,没有此“终止零”,您就没有字符串。您有一个字符数组(也称为缓冲区)在内存中徘徊。
数据寿命:
该函数的使用方式如下:
const char * myFunction()
{
return "My String";
}
int main()
{
const char* szSomeString = myFunction(); // Fraught with problems
printf("%s", szSomeString);
}
...通常会使您陷入随机的,未处理的异常/段故障等,尤其是“下坡路”。
简而言之,尽管我的回答是正确的-如果以这种方式使用它,则十分之九会导致程序崩溃,尤其是如果您认为以这种方式进行操作是“好的习惯”的话。简而言之:通常不是。
例如,假设在将来的某个时间,现在需要以某种方式来操作字符串。通常,编码人员会“走简单的路”并(尝试)编写如下代码:
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}
也就是说,你的程序会崩溃,因为编译器(可以/不可以)已经发布了使用的内存szBuffer
由时间printf()
中main()
被调用。(您的编译器也应事先警告您此类问题。)
有两种返回字符串的方法,这些字符串不会那么容易发怒。
- 返回存在一段时间的缓冲区(静态或动态分配)。在C ++中,使用“帮助程序类”(例如
std::string
)来处理数据的寿命(这需要更改函数的返回值),或者
- 将缓冲区传递给填充了信息的函数。
注意,如果不使用C中的指针,就不可能使用字符串。正如我所展示的,它们是同义词。即使在具有模板类的C ++中,在后台始终会使用缓冲区(即指针)。
因此,为了更好地回答(现在已修改的问题)。(肯定会提供各种“其他答案”。)
安全答案:
示例1,使用静态分配的字符串:
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"
}
这里的“静态”功能(许多程序员不喜欢这种类型的“分配”)是将字符串放入程序的数据段中。也就是说,它是永久分配的。
如果转到C ++,则将使用类似的策略:
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;
}
}
...,但是std::string
如果您要编写自己使用的代码(而不是要与他人共享的库的一部分),则使用帮助器类(例如)可能会更容易。
示例2,使用调用方定义的缓冲区:
这是传递字符串的更“简单”的方法。返回的数据不受主叫方的操纵。也就是说,示例1容易被主叫方滥用,并使您面临应用程序故障。这样,它就更安全(尽管使用更多的代码行):
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"
}
第二种方法更好的原因有很多,特别是如果您要编写供其他人使用的库(您无需锁定特定的分配/取消分配方案,则第三方无法破坏您的代码,并且您不需要链接到特定的内存管理库),但是像所有代码一样,它取决于您最喜欢的代码。出于这个原因,大多数人选择1作为示例,直到他们被烧了很多遍,以至于他们不再以这种方式编写它;)
免责声明:
我几年前退休了,现在我的C有点生锈了。此演示代码都应使用C正确编译(尽管对于任何C ++编译器都可以)。