我试图从一个函数返回一个C字符串,但它不起作用。这是我的代码。
char myFunction()
{
return "My String";
}在main中,我是这样命名的:
int main()
{
printf("%s", myFunction());
}我还尝试了myFunction的一些其他方法,但它们都不起作用。例如:
char myFunction()
{
char array[] = "my string";
return array;
}注意:我不允许使用指针!
关于这个问题的一些背景:
有一个函数可以找出是哪个月。例如,如果是1,则返回一月,依此类推。
所以当它要打印的时候,它是这样做的:printf("Month: %s",calculateMonth(month));。现在的问题是如何从calculateMonth函数返回该字符串。
发布于 2009-09-30 06:03:04
您的函数签名需要是:
const char * myFunction()
{
return "my String";
}背景:
它是C& C++的基础,但应该少做一些讨论。
在C中(& C++ ),字符串只是一个以零字节结尾的字节数组-因此术语“字符串-零”用来表示这种特殊风格的字符串。还有其他类型的字符串,但在C (& C++)中,这种风格是语言本身固有的。其他语言(Java、Pascal等)使用不同的方法来理解"my string"。
如果你曾经使用过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);
}..。通常会给你带来随机的未处理的异常/段错误等,特别是“在路上”。
简而言之,尽管我的答案是正确的--如果你以这种方式使用程序,那么10次中有9次会导致程序崩溃,特别是如果你认为这样做是“好的做法”。简而言之:通常不是这样的。
例如,想象一下在未来的某个时间,字符串现在需要以某种方式进行操作。通常,程序员会“选择最简单的方法”,并(尝试)编写如下代码:
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}也就是说,您的程序将崩溃,因为在调用szBuffer中的printf()时,编译器(可能会也可能没有)已经释放了main()使用的内存。(您的编译器也应该事先就此类问题发出警告。)
有两种方法可以返回不那么容易呕吐的字符串。
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"
}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;
}
}..。但是,如果您编写的是自己使用的代码(而不是与他人共享的库的一部分),那么使用帮助器类可能会更容易一些,比如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++编译器都可以)。
发布于 2009-09-30 06:06:05
C字符串被定义为指向字符数组的指针。
如果你不能有指针,根据定义,你也不能有字符串。
发布于 2009-09-30 06:10:30
请注意这个新函数:
const char* myFunction()
{
static char array[] = "my string";
return array;
}我将"array“定义为静态的。否则,当函数结束时,变量(以及返回的指针)会超出作用域。因为内存是在堆栈上分配的,所以它的将被破坏。这种实现的缺点是代码不是可重入的,也不是threadsafe。
另一种选择是使用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;
}请注意,唯一可以修改的内容是用户可以修改的内容。另一个副作用--这段代码现在是threadsafe了,至少从库的角度来看是这样。调用此方法的程序员应验证所使用的内存节是否为threadsafe。
https://stackoverflow.com/questions/1496313
复制相似问题