Недефинирана препратка към 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) изисква нов ред, EOF или грешка при четене в рамките на 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

>

Обърнете внимание как очаквах gets_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, заедно с останалите интерфейси за проверка на границите на C11 Annex K, е незадължителен, дори за напълно отговарящи на C11 реализации (което gcc все още не е). Една реализация може да дефинира макроса __STDC_LIB_EXT1__ и да предостави интерфейсите - или не. - person Keith Thompson; 01.03.2013
comment

Зависи дали сте го дефинирали, например:

Ако процесът бъде убит, тогава всички статични променливи ще бъдат повторно инициализирани до техните стойности по подразбиране.

Така че каквато и стойност да сте задали в Дейност А, няма да се запази. Същото важи и когато една дейност е унищожена.

- 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(), който съществува в стандартната библиотека), тогава всичко щеше да е хубаво (почти, има още проблеми). В действителност обаче няма такова нещо като gets_s() в света преди C11 (ако не греша, то се появява само в C библиотеката на Microsoft). Следователно линкерът не може просто да го намери. Ето защо той се отказва от опитите да сглоби програмата заедно и изплюва съобщението за грешка, което получавате. С други думи - не използвайте gets_s. Вместо това използвайте fgets. Това е почти същото — единствената разлика е, че е стандартно и очаква да посочите FILE * (за което му давате stdin). Можете да прочетете документация за тях, като напишете man fgets във вашия терминал. Като алтернатива (тъй като страниците с ръководство може да не са инсталирани на вашата система), можете да ги намерите онлайн, тук.

Както малко приятели тук посочиха, gets_s() беше добавен в C11. За съжаление, C11 не е напълно внедрен в GCC (и glibc), който Ubuntu използва. Това е в процес на работа и можете да проверите състоянието му в C11 Status Wiki на GCC.

Както спомена @Keith Thompson, заедно с останалата част от C11 Annex 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