Содержание
- Why there is no «gets» function in C standard library?
- The solution
- How to solve C++ Error C4996 ‘getch’: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name: _getch
- Solution
- Атрибут deprecated в С++14
- Modern C++ best practices for exceptions and error handling
- Use exceptions for exceptional code
- Basic guidelines
- Exceptions and performance
- Exceptions versus assertions
- C++ exceptions versus Windows SEH exceptions
- Exception specifications and noexcept
Why there is no «gets» function in C standard library?
If you are new to learn C programming, you may find there are many functions like «getc», «putc», «getchar», «putchar», «fgets», «fputs» : every «put» function comes with a corresponding «get» function. But there is a «puts» function while no «gets» function. Why?
Well, the short anwer is, the «gets» function was there before in C89 standard, then it got deprecated in C99 and removed in C11. But let’s see why it got removed.
First let’s check the function’s declaration:
Exit fullscreen mode
Basically we pass a pre-allocated «str» buffer to this function, gets will get user’s input and save it into this buffer. So we can write some code like this:
Exit fullscreen mode
If we use gcc to compile the code, we can see the error something like this:
main.c:(.text+0x164): warning: the ‘gets’ function is dangerous and should not be used.
We can see using this function is dangerous, but if we ignore this warning and go ahead run our program: if we type «hello world» as our input, the program can correctly put this string out.
So why is it dangerous? In our program we allocated a char array which has 20 slots, and our input «hello world» which are 11 characters, will be saved inside this array from position 0. Our «hello world» case works well, but now imagine what if our input has more than 20 characters, then we won’t have enough space to save all of these characters:
Exit fullscreen mode
In this case, since we don’t have enough space to save user’s input, the program crashed.
Sometimes this may cause bug that harder to find, suppose we have code like this:
Exit fullscreen mode
Before we compile and run the code, guess what the output would be if in the gets step, we type «hello world».
Your guess might be the program will crash since «hello world» in total are 11 characters (including the white-space in the middel) but our allocated arr only have 10 slots. Well, you are not wrong with the modern compiler, by that I mean, if we compile the code and run it:
Exit fullscreen mode
But in the old compiler, you might won’t have this crashed. The modern gcc will compile with a default «stack-protector» turned on. Let’s try to turn it off then run again:
Exit fullscreen mode
Ah, this time the program doesn’t crash. But our variable ‘c’ which is declared before arr has been «magically» changed to the character ‘d’, which is the last letter in «world».
To understand this, we need to know how the program allocate spaces in stack. Most of the operating system will allocate stack from high address to low address. The stack in our program will be something like this:
When we input «hello world», program will try to save this string from arr[0] , like this:
So technically we still have 11 slots, but the last one is not for our array, it is the space for our variable c . When the program saved the whole string, it put the last letter ‘d’ in our char c slot — that’s why after input our varaible c has been modified to d ! This kind bug is quite dangerous and not easy to find!
The solution
The alternative function we should use is fgets :
Источник
How to solve C++ Error C4996 ‘getch’: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name: _getch
Carlos Delgado
Learn why this error appears in your C++ code and how to solve it.
For newbies in C++ using modern tools may become a big headache, specially when the VS version of your school is pretty old and you have the latest version of VS at home. One of the most known exercises for students, is the famous hello world in this language. Playing with a console application, the exercise is simple, print «hello world» and keep the console open to see the printed message. According to the programming style of your teacher you may receive an example snippet using cout:
Or using printf to print the text in the console:
Both scripts are totally valid and they use the getch method to keep the console open. They should work normally in the compilers of the school where VS is always outdated, however, if you use a modern compiler to compile any of the previous examples (using latest version of Visual Studio), you will face the exception. The problem is that the getch method is a non-standard function, and MS compilers have traditionally offered those under two names, but Microsoft decided to define the name without underscore deprecated, because those names are reserved for the programmer.
Solution
The most simple workaround for this issue is to use the _getch method with an underscore as prefix:
Источник
Атрибут deprecated в С++14
Нормальным развитием любого кода является устаревание отдельных его частей (функций, классов и т.д.) и планомерное удаление их из проекта для снижения сложности и повышения безопасности кода. Просто убрать что-то обычно является плохой идеей — это может резко сломать какой-нибудь компонент, использующий удаляемую сущность. Хорошей практикой является пометка устаревшего кода каким-либо способом, который даст возможность использующим его программистам узнать о том, что он запланирован к удалению. (Прим. переводчика — Microsoft для этого изобрела свой велосипед, а ещё люди иногда пользовались #pragma message).
Новый стандарт С++14 вводит атрибут deprecated для пометки устаревших сущностей, планируемых к удалению в дальнейшем. Компилятор может выводить предупреждения при любой попытке их использования. Синтаксис принципиально новый для С++: атрибуты предполагается писать в виде списка через запятую, внутри двойных квадратных скобок. Вот так выглядит функция, помеченная как deprecated:
Код выше, будучи скомпилированным последним билдом clang, выдаёт следующее сообщение на этапе компиляции:
Также возможно вывести собственное сообщение:
Атрибут deprecated также применим к классам, typedef-ам, переменным, не статическим членам данных, перечислениям и шаблонам. Вот пара примеров:
Для тех, кто отмечает бросающуюся в глаза неконсистентность размещения атрибутов (к примеру их использование после ключевых слов class и enum) скажу, что это для того, чтобы можно было отличить пометку самого типа от пометки переменной этого типа. Например, следующее объявление помечает переменную, но не сам класс:
Атрибут deprecated может быть указан только в одном определении некоторой сущности, все следующие объявления без данного атрибута не снимают атрибут deprecated. Лучше всего помещать атрибут в заголовочный файл — этим вы подскажете всем пользователям, что сущность устарела и будет удалена.
Атрибут поддерживается начиная с Clang 3.4 и GCC 4.9.
Примечания переводчика: атрибуты, появившиеся ещё в стандарте С++11 подавали большие надежды — новая возможность добавлять метаданные к функциям, классам и другим вещам потенциально открывали большие возможности для мета-программирования, рефлексии, тестирования — в конце-концов C# и Python уже доказали реальную пользу подобных инструментов языка и лично мне хотелось верить, что и С++ получит в будущем что-то подобное. К сожалению, первые введённые атрибуты (noreturn, carries_dependency, alignas) давали как-то маловато пользы, что портило положительное впечатление от фичи. deprecated — первый, на мой взгляд, реально простой, понятный и нужный каждому атрибут. Хочется верить, что это шаг на пути к дальнейшим фичам языка на основе атрибутов.
Источник
Modern C++ best practices for exceptions and error handling
In modern C++, in most scenarios, the preferred way to report and handle both logic errors and runtime errors is to use exceptions. It’s especially true when the stack might contain several function calls between the function that detects the error, and the function that has the context to handle the error. Exceptions provide a formal, well-defined way for code that detects errors to pass the information up the call stack.
Use exceptions for exceptional code
Program errors are often divided into two categories: Logic errors that are caused by programming mistakes, for example, an «index out of range» error. And, runtime errors that are beyond the control of programmer, for example, a «network service unavailable» error. In C-style programming and in COM, error reporting is managed either by returning a value that represents an error code or a status code for a particular function, or by setting a global variable that the caller may optionally retrieve after every function call to see whether errors were reported. For example, COM programming uses the HRESULT return value to communicate errors to the caller. And the Win32 API has the GetLastError function to retrieve the last error that was reported by the call stack. In both of these cases, it’s up to the caller to recognize the code and respond to it appropriately. If the caller doesn’t explicitly handle the error code, the program might crash without warning. Or, it might continue to execute using bad data and produce incorrect results.
Exceptions are preferred in modern C++ for the following reasons:
An exception forces calling code to recognize an error condition and handle it. Unhandled exceptions stop program execution.
An exception jumps to the point in the call stack that can handle the error. Intermediate functions can let the exception propagate. They don’t have to coordinate with other layers.
The exception stack-unwinding mechanism destroys all objects in scope after an exception is thrown, according to well-defined rules.
An exception enables a clean separation between the code that detects the error and the code that handles the error.
The following simplified example shows the necessary syntax for throwing and catching exceptions in C++.
Exceptions in C++ resemble ones in languages such as C# and Java. In the try block, if an exception is thrown it will be caught by the first associated catch block whose type matches that of the exception. In other words, execution jumps from the throw statement to the catch statement. If no usable catch block is found, std::terminate is invoked and the program exits. In C++, any type may be thrown; however, we recommend that you throw a type that derives directly or indirectly from std::exception . In the previous example, the exception type, invalid_argument , is defined in the standard library in the header file. C++ doesn’t provide or require a finally block to make sure all resources are released if an exception is thrown. The resource acquisition is initialization (RAII) idiom, which uses smart pointers, provides the required functionality for resource cleanup. For more information, see How to: Design for exception safety. For information about the C++ stack-unwinding mechanism, see Exceptions and stack unwinding.
Basic guidelines
Robust error handling is challenging in any programming language. Although exceptions provide several features that support good error handling, they can’t do all the work for you. To realize the benefits of the exception mechanism, keep exceptions in mind as you design your code.
Use asserts to check for errors that should never occur. Use exceptions to check for errors that might occur, for example, errors in input validation on parameters of public functions. For more information, see the Exceptions versus assertions section.
Use exceptions when the code that handles the error is separated from the code that detects the error by one or more intervening function calls. Consider whether to use error codes instead in performance-critical loops, when code that handles the error is tightly coupled to the code that detects it.
For every function that might throw or propagate an exception, provide one of the three exception guarantees: the strong guarantee, the basic guarantee, or the nothrow (noexcept) guarantee. For more information, see How to: Design for exception safety.
Throw exceptions by value, catch them by reference. Don’t catch what you can’t handle.
Don’t use exception specifications, which are deprecated in C++11. For more information, see the Exception specifications and noexcept section.
Use standard library exception types when they apply. Derive custom exception types from the exception Class hierarchy.
Don’t allow exceptions to escape from destructors or memory-deallocation functions.
Exceptions and performance
The exception mechanism has a minimal performance cost if no exception is thrown. If an exception is thrown, the cost of the stack traversal and unwinding is roughly comparable to the cost of a function call. Additional data structures are required to track the call stack after a try block is entered, and additional instructions are required to unwind the stack if an exception is thrown. However, in most scenarios, the cost in performance and memory footprint isn’t significant. The adverse effect of exceptions on performance is likely to be significant only on memory-constrained systems. Or, in performance-critical loops, where an error is likely to occur regularly and there’s tight coupling between the code to handle it and the code that reports it. In any case, it’s impossible to know the actual cost of exceptions without profiling and measuring. Even in those rare cases when the cost is significant, you can weigh it against the increased correctness, easier maintainability, and other advantages that are provided by a well-designed exception policy.
Exceptions versus assertions
Exceptions and asserts are two distinct mechanisms for detecting run-time errors in a program. Use assert statements to test for conditions during development that should never be true if all your code is correct. There’s no point in handling such an error by using an exception, because the error indicates that something in the code has to be fixed. It doesn’t represent a condition that the program has to recover from at run time. An assert stops execution at the statement so that you can inspect the program state in the debugger. An exception continues execution from the first appropriate catch handler. Use exceptions to check error conditions that might occur at run time even if your code is correct, for example, «file not found» or «out of memory.» Exceptions can handle these conditions, even if the recovery just outputs a message to a log and ends the program. Always check arguments to public functions by using exceptions. Even if your function is error-free, you might not have complete control over arguments that a user might pass to it.
C++ exceptions versus Windows SEH exceptions
Both C and C++ programs can use the structured exception handling (SEH) mechanism in the Windows operating system. The concepts in SEH resemble the ones in C++ exceptions, except that SEH uses the __try , __except , and __finally constructs instead of try and catch . In the Microsoft C++ compiler (MSVC), C++ exceptions are implemented for SEH. However, when you write C++ code, use the C++ exception syntax.
Exception specifications and noexcept
Exception specifications were introduced in C++ as a way to specify the exceptions that a function might throw. However, exception specifications proved problematic in practice, and are deprecated in the C++11 draft standard. We recommend that you don’t use throw exception specifications except for throw() , which indicates that the function allows no exceptions to escape. If you must use exception specifications of the deprecated form throw( type-name ) , MSVC support is limited. For more information, see Exception Specifications (throw). The noexcept specifier is introduced in C++11 as the preferred alternative to throw() .
Источник
MSDN описывает функцию gets как устаревшую:
These functions are obsolete. Beginning in Visual Studio 2015, they are not available in the CRT. The secure versions of these functions, gets_s and _getws_s, are still available
Компилятор gcc также выдает предупреждение о том, что функция устаревшая:
warning: ‘char* gets(char*)’ is deprecated [-Wdeprecated-declarations]
В Linux man рассказано об опасностях использования этой функции:
Never use gets(). Because it is impossible to tell without knowing the
data in advance how many characters gets() will read, and because
gets() will continue to store characters past the end of the buffer, it
is extremely dangerous to use. It has been used to break computer
security. Use fgets() instead.
Таким образом, использование функции gets не рекомендуется нигде. Проблема функции в том, что она считывает данные в буфер до тех пор, пока не встретится символ конца строки, не учитывая размер буфера. Т.е., например, если мы выделим память под 25
символов, а пользователь введет строку из 40
символов, то следующие за строкой 15
байт будут перезаписаны. Такая ошибка может привести к чему угодно, причем программа может продолжать работать, но делать это неправильно (ошибку очень сложно выявить).
Описанные проблемы показаны следующим кодом:
#include <stdio .h> struct Item { char name[8]; int cost; }; int main(void) { Item item; item.cost = 100; printf ("cost is %dn", item.cost); printf ("Enter an item name: "); gets(item.name); printf ("cost is %dn", item.cost); printf ("name is %sn", item.name); }
Поля структуры располагаются в памяти последовательно, поэтому если пользователь введет название предмета более чем из семи символов (восьмым будет символ окончания строки), то стоимость предмета изменится:
Проблемы использования gets
Если теперь мы поменяли бы еще раз стоимость предмета — изменилось бы и содержимое строки.
Microsoft предлагает решать проблему с помощью gets_s — эта функция принимает на вход не только буфер, но и размер буфера. В случае ошибки (буфер равен NULL
или размер буфера недостаточен для сохранения введенной строки) функция возвращает NULL
. Кроме того, gets_s
может вызывать специальный обработчик ошибок.
Стандартная библиотека предлагает другое решение — функцию fgets
, которая также принимает на вход размер буфера:
char *fgets(char *s, int size, FILE *stream);
Функция позволяет считывать данные с файла, а если требуется ввод с клавиатуры — в качестве третьего аргумента нужно передавать stdin
. Если пользователь введет более size
символов, будет записано лишь первые size
байт, а остальные символы останутся в файле нетронутыми.
Заменив в приведенном выше коде ввод данных на
fgets(item.name, 8, stdin);
Мы получим другой вывод программы (теперь вводится только часть имени предмета, но не портятся данные и программа остается корректной.
Использование gets вместо gets
- Печать
Страницы: [1] 2 3 4 Все Вниз
Тема: CodeBlocks не может найти gets Что сделать, чтобы можно было его использовать? (Прочитано 6023 раз)
0 Пользователей и 1 Гость просматривают эту тему.
WhiteLedy
предупреждение: «gets» is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations] выдаёт CodeBlocks (Ubuntu 13.04)
при этом # include <stdio.h>
# include <stdlib.h>
# include <string.h>
(Я на Си чистом только)
Почему он не может его найти?
—
Как сделать так, чтобы компилятор позволил использовать gets?
« Последнее редактирование: 05 Мая 2013, 15:24:31 от WhiteLedy »
*/ ///______I BANGBANG
) . /_(_)
/__/
quiet_readonly
Так он же его находит. Предупреждение означает, что функция не рекомендуется к использованию и разработчики платформы допускают, что в некотором будущем программы, использующие функцию с атрибутом deprecated, перестанут компилироваться и работать.
WhiteLedy
Да? Но раз это просто предупреждение программа должна скомпилится и заработать, а она вылетает с этой ошибкой… ()_()
Они же предупреждают, а не запрещают Как сделать так, чтобы компилятор перестал ругаться на gets?
« Последнее редактирование: 05 Мая 2013, 09:43:39 от WhiteLedy »
*/ ///______I BANGBANG
) . /_(_)
/__/
hippi90
Как сделать так, чтобы компилятор перестал ругаться на gets?
Не использовать gets.
WhiteLedy
>Не использовать gets.
К сожалению, это невозможно — т.к. это программа моё дз и gets там должен быть. Кстати, запилить свою gets тоже невозможно из-за близкого дедлайна, я ниасилю.
Нельзя ли как-то CodeBlocks нормально настроить?
*/ ///______I BANGBANG
) . /_(_)
/__/
quiet_readonly
Да? Но раз это просто предупреждение программа должна скомпилится и заработать, а она вылетает с этой ошибкой… ()_()
Они же предупреждают, а не запрещают Как сделать так, чтобы компилятор перестал ругаться на gets?
Если программа вылетает с ошибкой, то в ней, очевидно, ошибка: запустите в отладчике. Или поищите ошибку компиляции, а не предупреждение.
WhiteLedy
Если программа вылетает с ошибкой, то в ней, очевидно, ошибка: запустите в отладчике. Или поищите ошибку компиляции, а не предупреждение.
Это предупреждение и есть ошибка Тут больше ничего нет тк прога работала на Ubuntu 12.10 я переустановила в 13.04 и она начала вылетать вот с этим. Компилятор перерабатывает программу с начала в начале и есть эта «ошибка» и как раз этот кусок кода я проверяла-перепроверяла бесчисленное число раз Что-то не так с CodeBlocks Но что? Может ему надо написать что-то особенное?
/home/whiteledy/Документы/Безымянная папка/сессииинфы/Sklad3.c|111|предупреждение: «gets» is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations]|
||=== Build finished: 1 errors, 0 warnings (0 minutes, 0 seconds) ===|
« Последнее редактирование: 05 Мая 2013, 14:52:39 от WhiteLedy »
*/ ///______I BANGBANG
) . /_(_)
/__/
kupamaan
покажите программу.
Ато в слепую как-то
SergeyIT
WhiteLedy,
Написано же «предупреждение: «gets» is depr….. «.
Показывайте ошибку
Извините, я все еще учусь
WhiteLedy
вот прога
Пользователь решил продолжить мысль 05 Мая 2013, 16:06:04:
там куча функций недописано. гетс в самом начале- в реализации меню (там 600 строк гдето ) И всё ней в порядке просто codeBlocks не работает почему-то
На 111 строке ошибка, как он думает
*/ ///______I BANGBANG
) . /_(_)
/__/
SergeyIT
Извините, я все еще учусь
WhiteLedy
вот именно! Что -то с моим компилятором
Ты в чём компилил? И на какой версии Ubuntu?
*/ ///______I BANGBANG
) . /_(_)
/__/
SergeyIT
Извините, я все еще учусь
WhiteLedy
имеешь ввиду что надо из терминала? Сейчас попробую
Пользователь решил продолжить мысль 05 Мая 2013, 16:50:32:
бугагашеньки whiteledy@whiteledy-Lenovo-G580:~$ gcc Sklad3.c
Sklad3.c: В функции «menu_select»:
Sklad3.c:111:9: предупреждение: «gets» is deprecated (declared at /usr/include/stdio.h:638) [-Wdeprecated-declarations]
мой комп сдвинулся
Пользователь решил продолжить мысль 05 Мая 2013, 16:51:27:
У тебя точно Ubuntu 13.04?
« Последнее редактирование: 05 Мая 2013, 16:53:34 от WhiteLedy »
*/ ///______I BANGBANG
) . /_(_)
/__/
- Печать
Страницы: [1] 2 3 4 Все Вверх
Croessmah,
Я не пойму почему, одна и та же программа работает с cin корректно, а с fgets некорректно.
А именно, если мы вводим правильный пароль то программа должна нас пускать.
Но от того что мы вводим по разному данные в массив, программа работает по разному.
Вот код программы с cin:
C++ | ||
|
И она при вводе пароля 12345 выводит корректное сообщение:
Введите пароль: 12345
Пароль действителен.
Вход разрешен.
А при вводе любого другого числа выводит корректное сообщение:
Введите пароль: 34434
Пароль недействителен.
Вход запрещен.
НО !!! С fgets все работает не правильно:
C++ | ||
|
При вводе корректного пароля 12345 с использованием функции fgets:
Введите пароль: 12345
Пароль недействителен.
Вход запрещен.
А при вводе любого другого случайного числа все равно выводит такое же сообщение:
Введите пароль: 324
Пароль недействителен.
Вход запрещен.
По сути мы поменяли в коде только метод ввода данных в массив, почему же тогда сразу программа работает неправильно из-за этого ? Может есть нюанс работы с fgets или у нас компилятор неправильный ?
Пробовали кстати эти 2 программы скомпилировать и запустить в:
— Ubuntu + nano + «g++»
— Windows 10 + CodeBlocks + GNU GCC Complier
Одинаково некорректно работает fgets
Скажите пожалуйста, в чем дело ?
Table of content
- Introduction of strings
- Memory Representation
- Reading Strings
- Using scanf() function
- Using gets() function
- Using fgets() function
- Using getline() function
- Using getdelim() function
- Using getchar() function
- Printing Strings
- Using printf() function
- Using puts() function
- Using putchar() function
- Using sprintf() function
- String Operations
Introduction:
- An array of characters is commonly called as strings.
- They are used by programmers to manipulate texts or sentences.
- In real time, we use a lot of applications which processes words and sentences to find pattern in user data, replace it , delete it , modify it etc.
- They are widely used in spell checkers, spam filters, intrusion detection system, search engines, plagiarism detection, bioinformatics, digital forensics and information retrieval systems etc.
Strings in C:
- A string or array of characters is terminated by a null character ‘’.
- After the last character in a string it is followed by a null character to denote the end of string.
- Syntax to declare string: data_type array/string_name [] = {“text”};
- Example:
char string_first[]="Hello World";
The above example declares a string called ‘string_first’, with ‘Hello World’ as the text inside it. You do not need to specify the size of array when declaration is made in the above fashion. The null character gets implicitly added into the string.
This is the default method of string declaration in C and is called as declaration of string constant as value is pre-assigned to the string.
The above statement can be also declared as follows:
char string_first[12] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' ,''};
Note that here we have to include space and null character as separate characters as they are too part of string. This is the classic array declaration method that we have used above for strings.
You can even declare a string as:
char name[size];
However, always remember that the you will be able to store size-1 characters as the last character will be null.
Memory Representation:
char string[] = {"Hello"};
Related:
Sort characters in a string in alphabetical order
Program in C to delete the vowels in a string
To extract one or all substrings of a string in C
Concatenating Strings in C
Copying strings in C
Swap two Strings
Comparing Strings
Removing Blank Spaces from a string
If you are new to learn C programming, you may find there are many functions like «getc», «putc», «getchar», «putchar», «fgets», «fputs» : every «put» function comes with a corresponding «get» function. But there is a «puts» function while no «gets» function. Why?
Well, the short anwer is, the «gets» function was there before in C89 standard, then it got deprecated in C99 and removed in C11. But let’s see why it got removed.
First let’s check the function’s declaration:
char* gets(char* str)
Enter fullscreen mode
Exit fullscreen mode
Basically we pass a pre-allocated «str» buffer to this function, gets
will get user’s input and save it into this buffer. So we can write some code like this:
char buffer[20] = {0};
gets(buffer);
printf("Your input is: %sn", buffer);
Enter fullscreen mode
Exit fullscreen mode
If we use gcc
to compile the code, we can see the error something like this:
main.c:(.text+0x164): warning: the ‘gets’ function is dangerous and should not be used.
We can see using this function is dangerous, but if we ignore this warning and go ahead run our program: if we type «hello world» as our input, the program can correctly put this string out.
So why is it dangerous? In our program we allocated a char array which has 20 slots, and our input «hello world» which are 11 characters, will be saved inside this array from position 0. Our «hello world» case works well, but now imagine what if our input has more than 20 characters, then we won’t have enough space to save all of these characters:
$ ./main
hello world hello world hello world
Your input is: hello world hello world hello world
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)
Enter fullscreen mode
Exit fullscreen mode
In this case, since we don’t have enough space to save user’s input, the program crashed.
Sometimes this may cause bug that harder to find, suppose we have code like this:
#include <stdio.h>
int main() {
char c = 'c';
char arr[10] = {0};
printf("before gets: variable c is %cn", c);
gets(arr);
printf("after gets: variable c is %cn", c);
return 0;
}
Enter fullscreen mode
Exit fullscreen mode
Before we compile and run the code, guess what the output would be if in the gets
step, we type «hello world».
Your guess might be the program will crash since «hello world» in total are 11 characters (including the white-space in the middel) but our allocated arr
only have 10 slots. Well, you are not wrong with the modern compiler, by that I mean, if we compile the code and run it:
$ gcc main.c -o main
main.c:(.text+0x51): warning: the 'gets' function is dangerous and should not be used.
$ ./main
before gets: variable c is c
hello world
after gets: variable c is c
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)
Enter fullscreen mode
Exit fullscreen mode
But in the old compiler, you might won’t have this crashed. The modern gcc
will compile with a default «stack-protector» turned on. Let’s try to turn it off then run again:
$ gcc main.c -fno-stack-protector -o main
main.c:(.text+0x42): warning: the 'gets' function is dangerous and should not be used.
$ ./main
before gets: variable c is c
hello world
after gets: variable c is d
Enter fullscreen mode
Exit fullscreen mode
Ah, this time the program doesn’t crash. But our variable ‘c’ which is declared before arr
has been «magically» changed to the character ‘d’, which is the last letter in «world».
To understand this, we need to know how the program allocate spaces in stack. Most of the operating system will allocate stack from high address to low address. The stack in our program will be something like this:
When we input «hello world», program will try to save this string from arr[0]
, like this:
So technically we still have 11 slots, but the last one is not for our array, it is the space for our variable c
. When the program saved the whole string, it put the last letter ‘d’ in our char c
slot — that’s why after input our varaible c
has been modified to d
! This kind bug is quite dangerous and not easy to find!
The solution
The alternative function we should use is fgets
:
char* fgets(char *string, int length, FILE * stream);
Enter fullscreen mode
Exit fullscreen mode
So we can change our code to:
#include <stdio.h>
int main() {
char c = 'c';
char arr[10] = {0};
printf("before gets: variable c is %cn", c);
fgets(arr, 10, stdin);
printf("after gets: variable c is %cn", c);
return 0;
}
Enter fullscreen mode
Exit fullscreen mode
Use gcc
to compile and run it:
$ gcc main.c -o main
$ ./main
before gets: variable c is c
hello world
after gets: variable c is c
Enter fullscreen mode
Exit fullscreen mode
Two things changed here: 1) when compile, there is no warning info printed out 2) although our «hello world» contains 11 letters, our program didn’t crash anymore and our variable c
hadn’t been overwritten.
Reference
- stack smashing detected
- gets is deprecated in c99
- fgets function