Неопределенная ссылка на gets_s?

Я использую gcc в Ubuntu 4.6.1 и SUSE 4.6.2 со следующей командой

gcc gets_s.c

Мой исходный код

// Read and Display Lines
// gets_s.c

#include <stdio.h>

int main(void)
{

    char first_name[11]; 
    char last_name[11]; 

    printf("First Name : ");
    gets_s(first_name, 11);
    printf("Last Name  : ");
    gets_s(last_name, 11);
    puts(first_name);
    puts(last_name);

    return 0;

}

Уточняю мой вопрос:

Принципиальной проблемой для меня является однозначное соответствие между введенными строками и сохраненными строками.

В случае успеха разница между fgets и gets_s заключается в том, что fgets включает в себя признак новой строки, а gets_s заменяет признак новой строки на нулевой признак, чтобы поддерживать однозначное соответствие между введенными строками и успешными вызовами gets_s.

Для ввода, превышающего длину буфера, fgets принимает количество символов, которое помещается в буфер, а остальные оставляет во входном буфере для следующих fgets.

В стандарте (K.3.5.4.1) указано, что с gets_s (в отличие от gets) требуется новая строка, конец строки или ошибка чтения в пределах n-1 символов. Следовательно, переполнение является нарушением ограничений времени выполнения. Если есть нарушение ограничения времени выполнения, первый символ в буфере устанавливается в нулевой символ, а символы во входном буфере stdin считываются и отбрасываются до тех пор, пока не будет прочитан символ новой строки, произойдет конец файла. или возникает ошибка чтения.

Соответственно в случае успеха я ожидал:

>fgets

First Name : Chris
Last Name  : Szalwinski
Chris

Szalwinski

>

>gets_s
First Name : Chris
Last Name  : Szalwinski
Chris
Szalwinski
>

При переполнении я ожидал другого поведения от fgets и gets_s. Другими словами,

>fgets
First Name : Christopher
Last Name  : Christophe
r


>

>gets_s
First Name : Christopher
Last Name  : Szalwinski

Szalwinski

>

Обратите внимание, как я ожидал, что get_s полностью удалит содержимое первой строки ввода.

Если основная проблема заключается во взаимно однозначном соответствии между введенными и сохраненными строками, что важно при отладке, нам все равно нужно написать собственную функцию (аналог getline K&R)

char *gets_s(char *s, int n) 
{
    int i, c;
    for (i = 0; i < n - 1 && (c = getchar()) != EOF && c != (int)'\n'; i++)
        s[i] = c;
    s[i] = '\0';
    while (n > 1 && c != EOF && c != (int)'\n')
        c = getchar();
    return c != EOF ? s : NULL;
}

С такой функцией сохраняется однозначное соответствие, буфер переполняется и не нарушается ограничение времени выполнения.

Правильно ли я делаю такой вывод.


person Chris Szalwinski    schedule 01.03.2013    source источник


Ответы (2)


Вы пробовали передать `-std=c11'?

Согласно этой странице, gets_s был введен в C11. Предполагая, что вы используете GCC, вы можете включить ограниченную поддержку C11, используя опцию `-std=c11'.

person Antimony    schedule 01.03.2013
comment
@Vlad: gets совершенно бесполезен. Вот почему C11 полностью удаляет его. - person Billy ONeal; 01.03.2013
comment
Я только что попробовал -std=c11 сам; он все еще не находит gets_s. И get_s, наряду с остальными интерфейсами проверки границ Приложения K C11, является необязательным, даже для полностью соответствующих реализаций C11 (которыми gcc еще не является). Реализация может определять макрос __STDC_LIB_EXT1__ и предоставлять интерфейсы — или нет. - person Keith Thompson; 01.03.2013
comment
Итак, в основном, используйте fgets. - person nneonneo; 01.03.2013
comment
fgets() останавливается на максимальном количестве символов, не стирая оставшуюся часть строки, если она не достигает символа новой строки. gets_s() завершается ошибкой, если не достигает символа новой строки. Означает ли это, что мне нужно написать свою собственную версию, пока gets_s() не будет реализована более широко. - person Chris Szalwinski; 01.03.2013
comment
@Keith: Это потому, что они сделали почти все в C11 необязательным. Например, VLA были обязательными в C99, но необязательны в C11. - person Billy ONeal; 01.03.2013

Первое, что вы должны сделать, это включить предупреждения компилятора. Например, передайте -Wall в GCC. После того, как это будет сделано, он выдаст следующее:

warning: implicit declaration of function ‘gets_s’

Что в основном означает, что компилятор понятия не имеет, что такое функция gets_s(). Так как же это компилятор в первую очередь? В C это называется неявным объявлением функции, которое в основном говорит о том, что если объявления функции нет, компилятор должен предположить, что существует функция, которая возвращает целое число и принимает любое количество параметров. Например:

int foo(...);

Итак, компилятор с радостью сгенерировал для вас код. Оттуда идет вниз по склону в вашем конкретном случае. Видите ли, если бы вы использовали реально существующую функцию (скажем, что-то вроде стандартного printf(), который существует в стандартной библиотеке), то все было бы хорошо (почти, есть еще подводные камни). На самом деле, в мире до C11 такой вещи, как gets_s(), не существует (если я не ошибаюсь, она появляется только в библиотеке Microsoft C). Поэтому компоновщик не может просто найти его. Вот почему он отказывается от попыток собрать программу и выдает сообщение об ошибке, которое вы получаете. Другими словами — не используйте gets_s. Вместо этого используйте fgets. Это почти то же самое — единственная разница в том, что он стандартный и ожидает, что вы укажете FILE * (для чего вы даете ему stdin). Вы можете прочитать документацию для них, набрав man fgets в своем терминале. В качестве альтернативы (поскольку справочные страницы могут быть не установлены в вашей системе) вы можете найти их в Интернете, здесь.

Как отметили несколько друзей, gets_s() было добавлено в C11. К сожалению, C11 не полностью реализован в GCC (и glibc), который использует Ubuntu. Эта работа еще не завершена, и вы можете проверить ее статус в C11 Status Wiki GCC.

Как упомянул @Keith Thompson, наряду с остальной частью C11 Приложение K «Интерфейсы проверки границ» является необязательным, даже для полностью соответствующих реализаций C11 (которых gcc еще нет). Реализация может определять макрос __STDC_LIB_EXT1__ и предоставлять интерфейсы — или нет.

Итак, в основном используйте fgets.

person Community    schedule 01.03.2013
comment
gets_s существует и является реальной вещью. Это в C1x, только не в C99. - person nneonneo; 01.03.2013
comment
Какую реализацию вы используете? - person Chris Szalwinski; 01.03.2013
comment
@ChrisSzalwinski: gcc 4.7.2 с glibc 2.16. Но C11 нет даже в самых новых (проверьте ссылку статуса). - person ; 01.03.2013