在C中,声明并初始化一个小函数中的变量或只返回一个响应

时间:2009-07-07 13:28:28

标签: c memory-leaks

来自PHP背景,我习惯于编写返回字符串的小函数(或来自其他函数的响应),如下所示:

function get_something(){
    return "foo";
}

然而,我是C的新手,我正试图想办法如何做一些非常基本的事情。

人们可以检讨以下类似的功能,并告诉我它们有何不同,哪一个是最好/最干净的?

char *get_foo(){
    char *bar;
    bar = "bar";
    return bar;
}

char *get_foo(){
    char *bar = "bar";
    return bar;
}

char *get_foo(){
    char *bar = NULL;
    bar = "bar";
    return bar;
}

char *get_foo(){
    return "bar";
}

这些功能之间有什么区别,还是风格问题?

另一件事。如果我有两个函数而另一个调用另一个函数,这样做可以吗?

char *get_foo(){
    return "bar";
}

char *get_taz(){
    return get_foo();
}

UPDATE :如果get_foo()没有返回const char *,这些函数将如何更改?如果get_foo()调用另一个具有不同长度的char *的函数,该怎么办?

7 个答案:

答案 0 :(得分:5)

这四个是等价的,尤其是前三个 - 编译器可能会将它们编译为完全相同的代码。所以我会选择最后一个,因为它更小。

说完了 - 你要返回一个const char *,而不是char *,所以这个特殊的代码可能会破坏一切,具体取决于你如何使用它(如果它完全编译,你可以强迫它)。问题是,您正在查找指向未动态分配的字符串的指针,而是指向可执行映像的一部分。所以修改它可能很危险。

作为一般规则,永远不要返回指向堆栈上分配的东西的指针(即不是使用new或malloc创建的),因为一旦函数结束,该变量的范围也会结束,被破坏,所以你得到指向无效(释放)内存的指针。

答案 1 :(得分:3)

这样的差异无论如何都会被编译器优化 ...我会投票支持:

char *get_foo(){
    char *bar = "bar";
    return bar;
}

const char *get_foo(){
    return "bar";
}

或类似的东西(但显然更具防御性,并且在GNU系统上):

char *get_foo(){
    return strdup("bar");
}

取决于将来的使用和扩展功能。实际上,由于优化,它是一个可读性问题,以及您希望字符串(可变/不可用)以供将来使用。

因为您正在将变量初始化为程序数据中的常量。如果我动态创建一个字符串,我会做不同的事情。

答案 2 :(得分:1)

与其他人已经说过的一样,编译器可能会为替代品生成相同的代码。但是:你强迫使用C?为什么不在可以使用std :: string类的地方使用C ++。我已经多年没有声明新的char数组 - 太容易出错了。在进入C ++之前,您不需要学习/掌握C语言。

答案 3 :(得分:1)

我总是警惕返回指向较低范围级别上存在的变量的指针。当我几年前第一次学习C-X时,我记得返回一个指向一个用本地范围声明的变量的指针,在我调用printf之前,调试器告诉我一切正常,但它从未打印出正确的值。发生的事情是:变量在printf调用之前是正确的,但是当你调用一个函数时,局部变量在堆栈上被分配,并在返回时被释放,所以我有指针的变量在调用printf之前存在于堆栈上并且是当引发printf函数时,内存被重新分配给printf,从而覆盖了以前的变量。

在你的情况下,你给出的例子将指定一个指向常量表的指针,该表作为可执行文件的一部分加载,并且可能没问题,这取决于实际程序正在做什么,但我会建议保留更高级别范围内的字符串,以防止在您调整时将一个简单的错误隐藏到您的代码中。根据您给出的示例,您可能在此调用上方的范围内分配了一个字符串表,并且只是分配变量而不是调用函数。

#define FOO 0

#define BAR 1

#define FOOBAR 2

#define BARFOO 3

char * MyFooStrings [4] = {“Foo”,“Bar”,“FooBar”,“BarFoo”};

//相反:myFoo = get_foo();

myFoo = MyFooStrings [FOO];

皮特

答案 4 :(得分:0)

  

这些之间有什么区别吗?   功能还是风格问题?

输出方面没有差异。正如其他人所提到的,编译器无论如何都可能优化代码。我的偏好是使用:

char *get_foo(){
    char *bar = "bar";
    return bar;
}

如果您的返回值比简单赋值更复杂,那么如果您需要单步执行代码,则有助于获得中间变量。

  

另一件事。如果我有两个   功能和一个调用另一个,是   这样做好吗?

只要您确保两个函数的返回类型兼容,这不是问题。

答案 5 :(得分:0)

  

更新:这些功能将如何需要   如果get_foo()没有返回则更改   一个const char *?如果get_foo()调用怎么办?   另一个有char *的函数   不同的长度?

get_taz()只需要一个与get_foo()赋值兼容的返回类型。例如,如果get_foo()返回一个int,那么get_taz()必须返回一些你可以赋予int的东西 - 比如int,long int或类似的东西。

“不同长度的char *”实际上没有任何意义,因为“char *”不真正意味着“字符串” - 它意味着“某些的位置炭的”。无论该位置是三个字符还是三十个字符,“char *”仍然是“char *”,所以这完全没问题:

const char *get_zero(void)
{
    return "Zero";
}

const char *get_nonzero(void)
{
    return "The number is non-zero";
}

const char *get_n(int n)
{
    if (n == 0)
    {
        return get_zero();
    }
    else
    {
        return get_nonzero();
    }
}

答案 6 :(得分:-2)

首先,其中一些会导致程序崩溃。

功能:

 char *get_foo(){
    char *bar;
    bar = "bar";
    return bar;
}

是不正确的C代码(它可能不会崩溃,但你永远不会知道)

char *bar 

在堆栈上分配1个指针的内存。

就个人而言,我会这样做。

1

char *get_foo1(void) {
   char *bar;
   bar = malloc(strlen("bar")+1);
   sprintf(bar,"bar");
   return bar;
}

2。或者传递你分配的变量。

   void get_foo2(char **bar) {
     sprintf(*bar,"bar);
   }
  1. 合并1和2,提供用户选项
  2. 在C中使用字符串时,几乎总是需要malloc()内存才能使用。 除非弦的长度提前知道或非常小。另外,你可以使用上面的#2来避免内存分配,比如这个

    int main(int argc, char *argv[]) {
       char bar[4];
       get_foo2(&bar);
    }