您的函数签名需要是:
const char * myFunction()
{
return "my String";
}
背景资料:
这对C&;C++,但是更多的讨论应该是有序的.
在C(& C++)中,字符串只是一个以零字节结尾的字节数组,因此,术语"字符串零"被用来表示字符串的这种特殊味道.还有其他种类的字符串,但在C(&;C++)中,这种风格本质上是由语言本身理解的.其他语言(Java、Pascal等)使用不同的方法来理解"my string"
.
如果你使用Windows API(C++),你会经常看到函数参数,比如:"LPCSTR lpszName"."sz"部分代表了"string zero"的概念:一个带有空(/zero)终止符的字节array.
澄清:
为了这个"介绍",我交替使用"字节"和"字符",因为这样更容易学习.请注意,还有其他方法(宽字符和多字节字符系统(mbcs))用于处理国际字符.UTF-8是mbcs的一个示例.为了介绍,我悄悄地"跳过"了这一切.
记忆:
这意味着像"my string"
这样的字符串实际上使用9+1(=10!)字节.当您最终开始动态分配字符串时,了解这一点很重要.
所以,如果没有这个"终止零",就没有字符串.内存中有一个字符数组(也称为缓冲区).
数据的生命周期 :
该函数的使用方法如下:
const char * myFunction()
{
return "my String";
}
int main()
{
const char* szSomeString = myFunction(); // Fraught with problems
printf("%s", szSomeString);
}
...通常会给您带来随机的未处理的异常/段错误等,特别是"以后".
简而言之,虽然我的答案是正确的--如果你这样使用程序,十有八九会导致程序崩溃,特别是如果你认为这样做是"好的做法".简而言之:通常不是这样的.
例如,想象future 某个时候,字符串现在需要以某种方式进行操作.通常,程序员会"走捷径",并(try )编写如下代码:
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}
也就是说,您的程序将崩溃,因为在调用main()
中的printf()
时,编译器(可能/可能没有)已经释放了szBuffer
使用的内存.(你的编译器也应该提前警告你这些问题.)
有两种方法可以返回不那么容易呕吐的字符串.
- 返回存在一段时间的缓冲区(静态或动态分配).在C++中,使用"帮助类"(例如,
std::string
)来处理数据的生命周期 (这需要改变函数的返回值),或者
- 将缓冲区传递给填充信息的函数.
请注意,在C语言中,如果不使用指针,就不可能使用字符串.正如我所展示的,它们是同义词.即使在C++中使用模板类,后台也总是使用缓冲区(即指针).
所以,为了更好地回答(现在修改的问题).(肯定有各种"其他答案"可以提供.)
更安全的答案:
Example 1, using statically allocated strings:
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"
}
在这里,static
所做的(许多程序员不喜欢这种类型的"分配")是将字符串放入程序的数据段.也就是说,它是永久分配的.
如果您转向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;
}
}
... 但是,如果您编写的代码是供自己使用的(而不是要与他人共享的库的一部分),那么使用helper类(例如std::string
)可能会更容易.
100
这是传递字符串的更"简单"的方式.返回的数据不受呼叫方的操纵.也就是说,示例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"
}
第二种方法更好的原因有很多,特别是如果您正在编写供其他人使用的库(您不需要锁定到特定的分配/释放方案,第三方不能 destruct 您的代码,您也不需要链接到特定的内存管理库),但是像所有代码一样,您最喜欢什么取决于您.出于这个原因,大多数人 Select 例如1,直到他们被烧了很多次,以至于他们拒绝再这样写了;)
免责声明:
几年前我退休了,现在我的C有点Rust 了.这个演示代码应该全部用C编译(虽然对于任何C++编译器都是可以的).