Как обнаружить передачу строкового литерала в функцию в C? - PullRequest
5 голосов
/ 24 февраля 2010

Я пытаюсь реализовать эквивалентную версию chomp() функции perl в C, и я столкнулся с угловым случаем, когда строковый литерал, переданный в качестве аргумента, вызовет ошибку сегментации (справедливо).

Пример chomp("some literal string\n");

Есть ли в C99 определенный способ определения, была ли моя функция передана строковому литералу, чтобы я мог return, не пытаясь обнулить ее?

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

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

Ответы [ 3 ]

5 голосов
/ 24 февраля 2010

Есть ли в C99 определенный способ определения, была ли моя функция передана строковому литералу, чтобы я мог вернуться без попытки обнулить ее?

Ты не должен.

Ваш API не должен пытаться выдумать что-либо для вызывающего, только чтобы он позже сломался. Если вызывающий нарушает правила, он должен тут же выяснить.

Если вызывающая сторона передает неизменяемую строку в функцию, которая ожидает изменяемую, она должна segfault. Все остальное - плохой дизайн.

(Приложение. Конечно, лучшим вариантом было бы вернуть копию строки, которую вызывающий абонент несет ответственность за освобождение.)

3 голосов
/ 24 февраля 2010

Ваш chomp в идеале должен создать новую строку и вернуть ее.Нет способа определить, передан ли вам строковый литерал или нет.На самом деле, я бы предложил использовать следующую подпись для chomp:

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

Или вы можете создать две разные функции и документировать их для клиентов: используйте chomp для не-литералов и chompl для буквенных строк.

1 голос
/ 20 апреля 2012

Существует один ОЧЕНЬ рискованный / плохой способ => Обычно строковые литералы хранятся в разделе данных только для чтения. Таким образом, один из способов - попытаться записать в целевую строку - если 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 очень рискованно, и учитывая, что строковые литералы не всегда хранятся в разделе данных только для чтения - это решение крайне не рекомендуется для производства.

...