【问题标题】:Returning char* in function在函数中返回 char*
【发布时间】:2011-01-27 09:17:07
【问题描述】:

我有功能:

char *zap(char *ar) {

    char pie[100] = "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '";
    char dru[] = "' )";
    strcat(pie, ar);
    strcat(pie, dru);
    return pie;
}

主要有:

printf("%s", zap( argv[1] )  );

编译时出现警告:

test.c: In function ‘zap’:
test.c:17: warning: function returns address of local variable

我应该如何正确返回 char*?

【问题讨论】:

  • 您似乎正在与数据库进行交互。你必须使用纯C吗?此外,大多数数据库都有 API 来安全地构造 SQL 语句。你能用这些吗?
  • 谢谢,是的 - 我使用 MySQL C API,但我必须创建一个字符串用作查询,因为我不能在查询本身中使用变量。

标签: c function char


【解决方案1】:

mallocpie分配内存

【讨论】:

  • 正如你提到的malloc,你能帮我如何为这个函数写一个合适的malloc吗?
【解决方案2】:

您最好的选择可能是根本不返回它 - 而是将要填充的缓冲区作为参数传递给函数。

void zap(char * pie, const char *ar) {
    strcpy( pie, "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '");
    char dru[] = "' )";
    strcat(pie, ar);
    strcat(pie, dru);
}

然后这样称呼它:

char pie[100];
zap( pie, "foo" );

要对这个函数进行防弹,你还需要传入缓冲区的长度,然后在每次要添加新的查询元素时进行检查。

【讨论】:

  • 缓冲区溢出问好
  • 如果这段代码中存在缓冲区溢出,那么原始代码中也会存在。我试图说明一个概念,而不是编写完美的代码。
  • 很公平。你仍然可以使用 strncpy 编写更好的代码。顺便说一句,你在第 2 行缺少一个括号;)
【解决方案3】:
char pie[100];

void zap(char* pie, char *ar) {

    char pies[100] = "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '";
    char dru[] = "' )";
    strcpy(pie, pies);
    strcat(pie, ar);
    strcat(pie, dru);
}

zap(pie, argv[1]);
printf("%s", pie  );

【讨论】:

    【解决方案4】:

    我强烈建议更改此函数,让用户同时传递一个缓冲区和一个长度,并改用该缓冲区。或者,您可以分配返回值的新实例,即使用malloc,但请确保给用户留下评论以再次释放它。

    【讨论】:

      【解决方案5】:

      声明你的 char 数组是静态的

      static char pie[100] = "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '";
      

      【讨论】:

      • 多线程冲突问好,缓冲区溢出的可能性还是存在的。
      【解决方案6】:

      发布的解决方案都有效,但只是为了回答您为什么会收到警告的问题:

      当您在函数中将 pie 声明为缓冲区时,您并没有分配堆内存,而是在堆栈中创建变量。该内存内容仅在该功能范围内得到保证。一旦你离开函数(在返回之后),内存可以被重用于任何事情,你可以发现你指向的内存地址随时被覆盖。因此,您会被警告您正在返回一个指向内存的指针,该指针不能保证保留。

      如果你想在一个 c 函数中分配持久内存,你可以在该函数之外引用,你需要使用 malloc(或其他风格的堆内存分配函数)。这将为堆上的该变量分配内存,并且它将一直存在,直到使用 free 函数释放内存为止。如果您不清楚堆栈与堆内存的区别,您可能需要 google 一下,这将使您的 C 体验更加流畅。

      【讨论】:

      【解决方案7】:
      #include #include /** * 返回缓冲区,只是为了方便。 */ char *generateSQL(char *buf, size_t bufsize, const char *ar) { 诠释n; n = snprintf(buf, bufsize, "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '%s')", ar); /* FIXME: 正确转义参数,以防它包含撇号。 */ 断言(0

      【讨论】:

      • 没关系,但你通常不想使用 assert() 进行运行时错误检查...
      • 我知道...但是很简单... :-/
      【解决方案8】:

      略有不同的方法:

      void zap(char **stmt, char *argument, size_t *stmtBufLen)
      {
        char *fmt="INSERT INTO test(nazwa, liczba) VALUES ('nowy wpis', '%s')";
        /**
         * Is our current buffer size (stmtBufLen) big enough to hold the result string?
         */
        size_t newStmtLen = strlen(fmt) + strlen(argument) - 2;
        if (*stmtBufLen < newStmtLen)
        {
          /**
           * No.  Extend the buffer to accomodate the new statement length.
           */
          char *tmp = realloc(*stmt, newStmtLen + 1);
          if (tmp)
          {
            *stmt = tmp;
            *stmtLen = newStmtLen+1;
          }
          else
          {
            /**
             * For now, just write an error message to stderr; the statement
             * buffer and statement length are left unchanged.
             */
            fprintf(stderr, "realloc failed; stmt was not modified\n");
            return;
          }
        }
        /**
         * Write statement with argument to buffer.
         */
        sprintf(*stmt, fmt, argument);
      }
      
      int main(void)
      {
        char *stmtBuffer = NULL;
        size_t stmtBufferLen = 0;
        ...
        zap(&stmtBuffer, "foo", &stmtBufferLen);
        ...
        zap(&stmtBuffer, "blurga", &stmtBufferLen);
        ...
        zap(&stmtBuffer, "AReallyLongArgumentName", &stmtBufferLen);
        ...
        zap(&stmtBuffer, "AnEvenLongerRidiculouslyLongArgumentName", &stmtBufferLen);
        ...
        free(stmtBuffer);
        return 0;
      }
      

      此版本使用动态内存分配来根据需要调整缓冲区大小,从 NULL 缓冲区指针 (realloc(NULL, size) == malloc(size)) 开始。这样您就不必担心从“足够大”的缓冲区开始。唯一的缺点是你需要记住在完成后释放缓冲区(我通常不喜欢像这样在调用者和被调用者之间划分内存管理职责;如果我考虑了超过 10 分钟,我d 想出更好的东西)。

      【讨论】:

        【解决方案9】:

        我进行此类操作的方式是使本地缓冲区成为静态线程特定变量:

        const int max_pie_cnt = 100;
        const char *zap(char *ar) {
        
            static __declspec(thread) char pie[max_pie_cnt]; // use TLS to store buffer
            strcpy(pie, "INSERT INTO test (nazwa, liczba) VALUES ('nowy wpis', '");
            char dru[] = "' )";
            strcat(pie, ar);
            strcat(pie, dru);
            return pie;
        }
        

        我对专家的cmets很好奇。

        顺便说一句,让我们暂时忘记缓冲区溢出问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-27
          • 1970-01-01
          • 1970-01-01
          • 2012-05-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多