从函数返回Cstring

我想从一个函数返回一个Cstring,但它不工作。 这是我的代码。

char myFunction() { return "My String"; } 

主要我这样称呼它:

 int main() { printf("%s",myFunction()); } 

我也尝试了一些其他的方式myFunction但他们不工作。 例如:

 char myFunction() { char array[] = "my string"; return array; } 

注意:我不允许使用指针!

关于这个问题的小背景:有找出它是哪个月份的函数。 如果它的1则返回1月等等

所以当它打印时,就是这样做的。 printf("Month: %s",calculateMonth(month)); 。 现在的问题是如何从calculateMonth函数返回该string。

你的函数签名需要是:

 const char * myFunction() { return "My String"; } 

编辑:

背景:

这个post已经有好几年了,从来没有想过它会被投票,因为它是C&C ++的基础。 不过,多一点讨论应该是为了。

在C(&C ++)中,一个string只是一个以零字节结尾的字节数组,因此术语“string零”用来表示这种特殊的string。 还有其他types的string,但是在C(&C ++)中,这种风格本质上是由语言本身所理解的。 其他语言(Java,Pascal等)使用不同的方法来理解“我的string”。

如果您曾经使用Windows API(使用C ++),您将会看到相当有规律的函数参数,如:“LPCSTR lpszName”。 'sz'部分表示'string-zero'的概念:一个带有空(/零)终止符的字节数组。

澄清:

为了这个“介绍”,我可以互换地使用“字节”和“字符”这个词,因为这样更容易学习。 请注意,还有其他方法(宽字符和多字节字符系统 – mbcs)用于处理国际字符。 UTF-8是一个mbcs的例子。 为了介绍,我悄悄地“跳过”所有这一切。

记忆:

这意味着像“我的string”string实际上使用9 + 1(= 10!)个字节。 知道何时终于dynamic地分配string是很重要的。 所以,没有这个“终止零”,你没有一个string。 你有一个字符数组(也称为缓冲区)挂在内存中。

数据的长寿:

这种方法的使用:

 const char * myFunction() { return "My String"; } int main() { const char* szSomeString = myFunction(); // fraught with problems printf("%s", szSomeString); } 

…一般会给你带来随机的未处理的exception/段错误等,特别是“在路上”。

简而言之,虽然我的答案是正确的,但是如果你以这种方式使用它,那么最终会出现一个崩溃的程序,特别是如果你认为这样做是一个好的做法。 总之:一般不会。

例如,假设将来有一段时间,string现在需要以某种方式操纵。 一般来说,一个编码器将“采取简单的path”和(尝试)像这样写代码:

 const char * myFunction(const char* name) { char szBuffer[255]; snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name); return szBuffer; } 

也就是说,你的程序会崩溃,因为在调用printf()中的printf()的时候,编译器(可能/不可能)已经释放了szBuffer使用的内存。 (您的编译器也应事先警告您这样的问题)。

有两种方法可以返回不会很容易出错的string。

  1. 返回生活一段时间的缓冲区(静态或dynamic分配)。 在C ++中,使用'helper classes'(例如std::string )来处理数据的长久性(这需要改变函数的返回值),或者
  2. 将缓冲区传递给获取信息的函数。

请注意,如果不使用C中的指针,就不可能使用string。正如我所说的,它们是同义词。 即使在使用模板类的C ++中,在后台也总是使用缓冲区(即指针)。

所以,要更好地回答(现在修改的问题)。 (肯定会有各种可以提供的“其他答案”)。

更安全的答案:

例如1.使用静态分配的string:

 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" } 

这里的“静态”是什么(许多程序员不喜欢这种types的“分配”)是将string放到程序的数据段中。 也就是说,它是永久分配的。

如果你转向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.使用呼叫者定义的缓冲区:

这是传递string的更“不错的方式”。 返回的数据不受主叫方的操纵。 也就是说,例如,1可能很容易被主叫方滥用,并使您面临应用程序故障。 这样,它更安全(尽pipe使用更多的代码行):

 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" } 

第二种方法更好的原因有很多,特别是如果你正在编写一个供别人使用的库(你不需要locking一个特定的分配/解除分配scheme,第三方不能破坏你的代码,你不需要链接到一个特定的内存pipe理库),但是像所有的代码一样,这取决于你最喜欢什么。 出于这个原因,大多数人select,例如1,直到他们被烧了很多次,他们拒绝这样写了;)

免责声明:

我退休了几年,现在我的C有点生疏。 这个演示代码应该都可以用C编译(对于任何C ++编译器来说都可以)。

ACstring被定义为一个指向字符数组的指针。

如果你不能有指针,根据定义你不能有string。

你的问题是函数的返回types – 它必须是:

 char *myFunction() 

…然后你的原始配方将工作。

请注意,如果没有涉及指针的Cstring, 则不能使用Cstring。

另外:打开你的编译器警告,它应该已经警告过你将返回行转换char *char而没有明确的转换。

注意这个新function:

 const char* myFunction() { static char array[] = "my string"; return array; } 

我将“数组”定义为静态,否则当函数结束时,variables(以及返回的指针)超出范围。 由于内存分配在堆栈上,所以被损坏。 这个实现的缺点是代码不可重入,也不是线程安全的。

另一种方法是使用malloc在堆中分配string,然后释放代码的正确位置。 这段代码将会重新生成并且线程安全。

编辑:

正如在注释中指出的那样,这是一个非常糟糕的做法,因为攻击者可以将代码注入到应用程序中(他需要使用gdb打开代码,然后创build一个断点并修改返回的variables的值,开始)。

如果更多的build议让调用者处理内存分配。 看到这个新的例子:

 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; } 

请注意,唯一可以修改的内容是用户的内容。 另一个副作用 – 这个代码现在是线程安全的,至less从图书馆的angular度来看。 调用此方法的程序员应该validation所使用的内存部分是否是线程安全的。

基于你新添加的问题,为什么不直接返回一个从1到12的整数,并让main()函数使用switch语句或if-else阶梯来决定打印什么? 这当然不是最好的方式 – char *会 – 但是在这样的一个class级的情况下,我想它可能是最优雅的。

你可以在调用者(这是主函数)中创build数组,然后将数组传递给被调用者myFunction()。 因此,myFunction可以将string填充到数组中。 但是你需要声明myFunction()为

 char* myFunction(char * buf, int buf_len){ strncpy(buf, "my string", buf_len); return buf; } 

在main函数中,myFunction应该这样调用

 char array[51]; memset(array,0,51);/*all bytes are set to '\0'*/ printf("%s", myFunction(array,50));/*buf_len arguement is 50 not 51. This is to make sure the string in buf is always null-terminated(array[50] is always '\0')*/ 

但是指针仍然被使用。

你的函数返回types是一个字符。 你应该返回一个指向字符数组的第一个元素的指针。 如果你不能使用指针,那么你是拧紧的。 🙁

或者这个怎​​么样:

 void print_month(int month) { switch (month) { case 0: printf("january"); break; case 1: printf("february"); break; ...etc... } } 

用你在其他地方计算的月份来调用它。

问候,

Sebastiaan

char只是一个单字节字符。 它不能存储string,也不是一个指针(你显然不能这样做)。 因此,如果不使用指针( char[]是语法糖),就无法解决问题。

那么在你的代码中,你正试图返回一个String (在C中,它只是以空字符结尾的字符数组),但是函数的返回types是char ,它会给你带来所有的麻烦。 相反,你应该这样写:

 const char * myFunction()
 {

    返回“我的string”;

 }

而且用const来限定你的types总是好的,同时把C中的文字分配给指针,因为C中的文字是不可修改的。

如果你真的不能使用指针,像这样做:

 char get_string_char(int index) { static char array[] = "my string"; return array[index]; } int main() { for (int i = 0; i < 9; ++i) printf("%c", get_string_char(i)); printf("\n"); return 0; } 

神奇的数字9是可怕的,这不是一个好的编程的例子。 但是你明白了。 请注意,指针和数组是相同的东西(有点),所以这是有点作弊。

希望这可以帮助!

你的函数原型声明你的函数将返回一个字符。 因此,你不能在你的函数中返回一个string。