我试图从函数返回一个C字符串,但它不起作用.这是我的代码.
char myFunction()
{
return "My String";
}
Run Code Online (Sandbox Code Playgroud)
在主要我称之为:
int main()
{
printf("%s",myFunction());
}
Run Code Online (Sandbox Code Playgroud)
我也尝试了一些其他的方法,myFunction但他们没有工作.例如:
char myFunction()
{
char array[] = "my string";
return array;
}
Run Code Online (Sandbox Code Playgroud)
注意:我不允许使用指针!
关于这个问题的小背景:有一个功能是找出它是哪个月; 如果它1然后它返回1月等等
因此,当它打印时,它就是这样做的.printf("Month: %s",calculateMonth(month));.现在的问题是如何从calculateMonth函数中返回该字符串.
cmr*_*rgo 202
你的功能签名需要是:
const char * myFunction()
{
return "My String";
}
Run Code Online (Sandbox Code Playgroud)
编辑:
背景:
这篇文章已经有好几年了,从未想过它会被投票,因为它对C&C++来说是如此根本.尽管如此,还是应该进行更多的讨论.
在C(&C++)中,字符串只是一个以零字节结尾的字节数组 - 因此术语"字符串零"用于表示字符串的这种特殊风格.还有其他类型的字符串,但在C(&C++)中,这种风格本身就是语言本身所理解的.其他语言(Java,Pascal等)使用不同的方法来理解"我的字符串".
如果你曾经使用Windows API(使用C++),你会经常看到如下函数参数:"LPCSTR lpszName".'sz'部分代表了'string-zero'的概念:一个带有null(/ zero)终结符的字节数组.
澄清:
为了这个"介绍",我可以互换地使用"字节"和"字符"这个词,因为这种方式更容易学习.请注意,还有其他方法(宽字符和多字节字符系统 - 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);
}
Run Code Online (Sandbox Code Playgroud)
...通常会给你带来随机的未处理 - 例外/段故障等,特别是"在路上".
简而言之,虽然我的回答是正确的 - 如果你以这种方式使用它,你最终会得到一个崩溃的程序,特别是如果你认为以这种方式做到这一点是"好习惯"的话.简而言之:通常不是.
例如,想象一下将来的某个时间,现在需要以某种方式操纵字符串.通常,编码器将"采取简单的路径"并(尝试)编写如下代码:
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}
Run Code Online (Sandbox Code Playgroud)
也就是说,程序将崩溃,因为编译器(可能/可能没有)已经释放szBuffer了printf()在main()调用in 时使用的内存.(您的编译器也应事先警告您这些问题).
有两种方法可以返回不容易barf的字符串.
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"
}
Run Code Online (Sandbox Code Playgroud)
'静态'在这里做什么(许多程序员不喜欢这种类型的'分配')是字符串被放入程序的数据段.也就是说,它是永久分配的.
如果你转移到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;
}
}
Run Code Online (Sandbox Code Playgroud)
...但是使用帮助程序类可能更容易,例如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"
}
Run Code Online (Sandbox Code Playgroud)
第二种方法更好的原因有很多,特别是如果您正在编写一个供其他人使用的库(您不需要锁定特定的分配/解除分配方案,第三方不能破坏您的代码,你不需要链接到特定的内存管理库),但是像所有代码一样,它取决于你最喜欢的内容.出于这个原因,大多数人选择例如1,直到他们被烧了这么多次,以至于他们不再以这种方式写它了;)
免责声明:
几年前我退休了,我的C现在有点生疏了.这个演示代码应该全部用C编译(但对于任何C++编译器都可以).
Cra*_*rks 12
AC字符串定义为指向字符数组的指针.
如果你不能有指针,根据定义你不能有字符串.
注意这个新功能:
const char* myFunction()
{
static char array[] = "my string";
return array;
}
Run Code Online (Sandbox Code Playgroud)
我将"array"定义为static,否则当函数结束时,变量(以及返回的指针)超出范围.由于该内存在堆栈上分配,因此会被破坏.这种实现的缺点是代码不是可重入的而不是线程安全的.
另一种方法是使用malloc在堆中分配字符串,然后释放代码的正确位置.此代码将重新进入并且线程安全.
编辑:
正如评论中所指出的,这是一个非常糟糕的做法,因为攻击者可以向您的应用程序注入代码(他需要使用gdb打开代码,然后创建一个断点并修改返回变量的值以溢出和有趣只是开始了).
如果更推荐让调用者处理内存分配.看到这个新例子:
char* myFunction( char* output_str, size_t max_len )
{
const char *str = "my string";
size_t l = strlen(str);
if (l+1 > max_len) {
return NULL;
}
strcpy(str, str, l);
return input;
}
Run Code Online (Sandbox Code Playgroud)
请注意,唯一可以修改的内容是用户.另一个副作用 - 这个代码现在是线程安全的,至少从库的角度来看.调用此方法的程序员应验证所使用的内存部分是否是线程安全的.
你的问题是函数的返回类型 - 它必须是:
char *myFunction()
Run Code Online (Sandbox Code Playgroud)
...然后你的原始配方将起作用.
请注意,在行的某个位置,您不能使用没有指针的C字符串.
另外:打开你的编译器警告,它应该警告你关于转换char *为char没有显式转换的返回线.
根据您新添加的带有问题的背景故事,为什么不在本月返回1到12之间的整数,让main()函数使用switch语句或if-else梯形图来决定打印什么?它肯定不是最好的方式 - char*会 - 但在这样的类的背景下,我想它可能是最优雅的.