【问题标题】:How to detect the passing of a string literal to a function in C?如何检测将字符串文字传递给C中的函数?
【发布时间】:2010-02-23 23:44:54
【问题描述】:

我正在尝试在 C 中实现 perl 的 chomp() 函数的等效版本,我遇到了一个极端情况,其中作为参数传递的字符串文字会导致分段错误(理所当然)。

例如chomp("some literal string\n");

C99 中是否有定义的方法来检测我的函数是否传递了字符串文字,这样我就可以 return 而不尝试将其 NUL 出来?

char* chomp(char *s)
{
    char *temp = s;

    if (s && *s)
    {
        s += strlen(s) - 1;
        if (*s == '\n')
        {
            *s = '\0';
        }
    }
    return temp;
}

【问题讨论】:

  • 奇数。您的编译器不应该阻止字符串文字作为未声明为 const char * 的参数传入吗?
  • @Adam Maras:标准没有要求。但是,尝试修改相同的是UB。
  • Adam Maras:在纯 C 中,字符串文字的类型为 char *,而不是 const char *

标签: c string literals


【解决方案1】:

C99 中是否有定义的方法来检测我的函数是否传递了字符串文字,以便我可以返回而不尝试将其 NUL 输出?

你不应该。

您的 API 不应该试图为调用者捏造东西,而只是让它稍后中断。 如果来电者违反了规则,他们应该及时发现。

如果调用者将非可变字符串传递给期望可变字符串的函数,则它应该出现段错误。其他任何东西都是糟糕的设计。

(附录:最好的设计当然是返回调用者负责释放的字符串的副本。)

【讨论】:

  • 除了文档之外,是否有推荐的做法来提醒调用者如果不是free()'d 这可能是潜在的内存泄漏?
  • @SiegeX - 我喜欢做的一件事是在执行此操作的函数名称中包含 alloc char *chomp_alloc(const char *);
  • @SiegeX:我自己会选择error_t chomp(const char* input, char** out_result)。很明显,其中有一些不适合语法的东西,所以很明显你必须释放它,你也会得到一个特定的错误。
  • 一个自愿崩溃的 API 不仅是糟糕的设计,而且是可怕的。真正糟糕的设计是 C 规范允许字符串文字降级为 char * 而无法检测到它。调用者可能甚至不知道他们传递了一个不可变的字符串,它可能已经给了他们。
【解决方案2】:

您的chomp 理想情况下应该创建一个新字符串并返回它。无法确定您是否传递了字符串文字。事实上,我建议对chomp 使用以下签名:

char *chomp(const char *s); /* do not modify input parameters */

或者,您可以创建两个不同的函数并为客户端记录它们:对非文字使用 chomp,对文字字符串使用 chompl

【讨论】:

  • 除了文档之外,是否有推荐的做法来提醒调用者如果不是free()'d 这可能是潜在的内存泄漏?
  • 不完全是。你怎么知道客户何时使用了这个对象?例如,请参阅 strdup 文档。
【解决方案3】:

有一种非常危险/糟糕的方式 => 通常字符串文字存储在只读数据部分中。所以一种方法是尝试写入目标字符串——如果segmentation fault 被接收到signal callback——那么这意味着你的字符串是文字的,并返回到带有longjmp 的测试函数。 比如:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>

static jmp_buf jbuf;

static void catch_segv() {
    longjmp(jbuf, 1);
}

int isLiteral(char * ptr) {
  if (setjmp(jbuf) == 0)
    return  (*ptr = *ptr, 0);
  else
    return 1;
  }

int main()
{
    char writableString[] = "some writable string";

    signal(SIGSEGV, catch_segv);

    printf("is literal = %d\n", isLiteral(writableString));
    printf("is literal = %d\n", isLiteral("read-only string"));

    return 0;
}

但是考虑到在SIGSEGV 之后恢复程序是非常危险的事情,并且考虑到字符串文字并不总是存储在只读数据部分 - 这个解决方案非常不推荐用于生产。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-05
    • 2019-03-18
    • 2014-03-19
    相关资源
    最近更新 更多