我正在尝试从函数返回一个C字符串但它不起作用。这是我的代码。
char myFunction()
{
return "My String";
}
主要是我这样称呼:
int main()
{
printf("%s",myFunction());
}
我还尝试了myFunction
的其他方法,但它们没有用。 E.g:
char myFunction()
{
char array[] = "my string";
return array;
}
注意:我不允许使用指针!
这个问题的背景很少: 有找出哪个月的功能;如果它1然后它返回1月等等。
因此,当它打印时,它就是这样做的。 printf("Month: %s",calculateMonth(month));
。现在问题是如何从calculateMonth
函数返回该字符串。
答案 0 :(得分:182)
您的功能签名必须是:
const char * myFunction()
{
return "My String";
}
修改
<强>背景
这篇文章已经有好几年了。从来没有想过它会被投票,因为它对C&amp; amp; C ++。尽管如此,还是应该进行更多的讨论。
在C(&amp; C ++)中,字符串只是一个以零字节结尾的字节数组 - 因此术语“字符串零”用于表示字符串的这种特殊风格。还有其他类型的字符串,但在C(&amp; C ++)中,这种风格本身就是语言本身所理解的。其他语言(Java,Pascal等)使用不同的方法来理解“我的字符串”。
如果您使用的是Windows API(使用的是C ++),您会看到非常有规律的函数参数,例如:“LPCSTR lpszName”。 'sz'部分代表了'string-zero'的概念:一个带有null(/ zero)终结符的字节数组。
<强>澄清:
为了这个'介绍',我可以互换地使用'bytes'和'characters'这个词,因为这样学习起来比较容易。请注意,还有其他方法(宽字符和多字节字符系统 - 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);
}
...通常会给你带来随机的未处理 - 例外/段故障等,特别是“在路上”。
简而言之,虽然我的回答是正确的 - 但如果你以这种方式使用它,那么你最终会得到一个崩溃的程序,特别是如果你认为这样做是“好习惯”的话。简而言之:通常不是。
例如,想象一下将来的某个时间,现在需要以某种方式操纵字符串。通常,编码器将“采取简单的路径”并(尝试)编写如下代码:
const char * myFunction(const char* name)
{
char szBuffer[255];
snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
return szBuffer;
}
也就是说,您的程序会崩溃,因为编译器(可能/可能没有)在调用szBuffer
中的printf()
时释放main()
使用的内存。 (您的编译器也应事先警告您这些问题)。
有两种方法可以返回不会很容易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"
}
这里的'静态'是什么(许多程序员不喜欢这种'分配')是字符串被放入程序的数据段。也就是说,它是永久分配的。
如果你转到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 ++编译器都可以)。答案 1 :(得分:12)
C字符串被定义为指向字符数组的指针。
如果你没有指针,根据定义你就不能有字符串。
答案 2 :(得分:8)
注意这个新功能:
const char* myFunction()
{
static char array[] = "my string";
return array;
}
我将“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;
}
请注意,唯一可以修改的内容是用户。另一个副作用 - 这个代码现在是线程安全的,至少从库的角度来看。调用此方法的程序员应验证所使用的内存部分是否是线程安全的。
答案 3 :(得分:7)
问题在于函数的返回类型 - 它必须是:
char *myFunction()
......然后你的原始配方就可以了。
请注意,不能在没有指针的情况下使用C字符串,就在某处。
另外:提出你的编译器警告,它应该警告你关于将char *
转换为char
而没有明确演员的返回行。
答案 4 :(得分:5)
根据您新添加的带有问题的背景故事,为什么不在本月返回1到12之间的整数,让main()函数使用switch语句或if-else梯形图来决定打印什么?它肯定不是最好的方式 - char *会 - 但在这样的类的上下文中,我想它可能是最优雅的。
答案 5 :(得分:3)
您可以在调用者(主函数)中创建数组,并将数组传递给被调用者,即myFunction()。因此myFunction可以将字符串填充到数组中。但是,您需要将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')*/
但是仍然使用指针。
答案 6 :(得分:2)
您的函数返回类型是一个char。您应该返回指向字符数组的第一个元素的指针。如果你不能使用指针,那你就搞砸了。 :(
答案 7 :(得分:2)
或者这个怎么样:
void print_month(int month)
{
switch (month)
{
case 0:
printf("january");
break;
case 1:
printf("february");
break;
...etc...
}
}
用你在其他地方计算的月份来调用它。
此致
Sebastiaan
答案 8 :(得分:1)
char
只是一个单字节字符。它不能存储字符串,也不是指针(你显然不能拥有)。因此,如果不使用指针(char[]
是语法糖),就无法解决问题。
答案 9 :(得分:1)
如果你真的不能使用指针,可以这样做:
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很糟糕,这不是一个好的编程的例子。但你明白了。请注意,指针和数组是相同的(有点),所以这有点作弊。
希望这有帮助!
答案 10 :(得分:1)
在您的代码中,您正在尝试返回String
(在C
中,这只是空终止的字符数组),但函数的返回类型为char
,这会导致给你带来的麻烦。相反,你应该这样写:
const char* myFunction() { return "My String"; }
使用const
对类型进行限定总是好的,同时将C中的文字指定为指针,因为C中的文字是不可修改的。
答案 11 :(得分:0)
您的函数原型声明您的函数将返回一个char。因此,您无法在函数中返回字符串。
答案 12 :(得分:0)
char* myFunction()
{
return "My String";
}
在C字符串中,文字是具有静态常量内存类的数组,因此返回指向该数组的指针是安全的。此处有更多详细信息:"life-time" of string literal in C
答案 13 :(得分:0)
从函数返回字符串
#include <stdio.h>
const char* greet() {
return "Hello";
}
int main(void) {
printf("%s", greet());
}
答案 14 :(得分:0)
要记住的另一件事是,您不能从 C 函数返回定义为局部变量的字符串,因为该变量会在函数执行完成后自动销毁(释放)。
#include <stdio.h>
#include <stdlib.h>
char *myfunc(){
char *myvar = (char *)malloc(20);
printf("Plese enter some text \n");
fgets(myvar, 20, stdin);
return myvar;
}
int main(){
printf("You entered: %s", myfunc());
}