Академический Документы
Профессиональный Документы
Культура Документы
Tlgm: @it_boooks
Николай П рохоренок
____И.МОЕ
Санкт-Петербург
« БХВ-Петербург»
2020
Tlgm: @it_boooks
УДК 004.438 С
ББК 32.973.26-018.1
П84
Прохоренок Н. А.
П84 Язык С. Самое необходимое. - СПб.: БХВ-Петербург, 2020. - 480 с.:
ил. -(Самое необходимое)
ISBN 978-5-9775-4116-9
Описан базовый синтаксис современного языка С: типы данных, операторы,
условия, циклы, работа с числами, строками, массивами и указателями, создание
пользовательских функций, модулей, статических и динамических библиотек. Рас
смотрены основные функции стандартной библиотеки языка С, а также функции,
применяемые только в операционной системе Windows. Для написания, компиля
ции и запуска программ используется редактор Eclipse, а для создания исполняе
мого файла - компилятор gcc.exe версии 8.2, входящий в состав популярной биб
лиотеки MinGW-W64. Книга содержит большое количество практических приме
ров, помогающих начать программировать на языке С самостоятельно. Весь
материал тщательно подобран, хорошо структурирован и компактно изложен, что
позволяет использовать книгу как удобный справочник. Электронный архив
с примерами находится на сайте издательства.
Для програм.wистов
УДК 004.438 С
ББК 32.973.26-018.1
Оглавление
Введение ............................................................................................................................ 9
Глава 1. Установка программ под Windows ............................................................ 11
1.1. Создание струкгуры каталогов ..............................................................................................11
l'.2. Добавление пути в переменную РА ТН .................................................................................12
1.3. Работа с командной строкой ..................................................................................................14
1.4. Установка MinGW и MSYS ...................................................................................................14
1.5. Установка MinGW-W64 .........................................................................................................20
1.6. Установка MSYS2 и MinGW-W64 ........................................................................................24
1.7. Установка и настройка редактора Eclipse ............................................................................29
1.8. Создание проектов в редакторе Eclipse ................................................................................34
4 Оглавление
Оглавление 5
б Оглавление
Оглавление 7
Введение
10 Введение
ГЛАВА 1
12 Глава 1
Каталоги book и срр лучше разместить в корне какого-либо диска. В моем случае
это будет диск С:, следовательно, пути к содержимому каталогов - C:\book и С:\срр.
Можно создать каталоги в любом другом месте, но в пути не должно быть русских
букв и пробелов - только латинские буквы, цифры, тире и подчеркивание. Ос
тальных символов лучше избегать, если не хотите проблем с компиляцией и запус
ком программ.
В каталоге C:\book мы станем размещать наши тестовые программы и тренироваться
при изучении работы с файлами и каталогами. Некоторые функции при неправиль
ном действии без проблем могут удалить все дерево каталогов, поэтому для экспе
риментов мы воспользуемся отдельным каталогом, а не каталогом С:\срр,
в котором у нас будет находиться все сокровенное, - обидно, если по случайности
мы удалим все установленные программы и проекты.
Внутри каталога С:\срр у нас созданы три вложенных каталога:
□ eclipse - в этот каталог мы установим редактор Eclipse;
□ projects- в этом каталоге мы станем сохранять проекты из редактора Eclipse;
□ lib- путь к этому каталогу мы добавим в системную переменную РАТН и будем
размещать в ней различные библиотеки, которые потребуются для наших про
грамм.
Свойства системы
Бь1с'!р0дейсmие
Визуальные эср<рек,ь1, использование процессора. oneparnвнofl и
вир1уальной памяrn
'!}араметры ....•
ПроФили пользователей
Параметры рабочего стола, оnюсяшиеся ко =тrt в систем)'
Загрузка и восстановление
Загрузка и восстановление системы. отлаnочная информация
�-�-- ---..........,
-•···
�------'
Параме'!ро!.•.
Переменные среды
Переменная Значен�1е: А
Сисrенные переменные
А
j Переменная Значение
os '#indows_NT
14 Глава 1
.,
ок е
Отм на .-1
Добавлять пути в переменную РАТН мы будем несколько раз, поэтому способ изме
нения значения этой системной переменной нужно знать наизусть.
ВНИМАНИЕ/
Случайно не удалите существующее значение переменной РАТН, иначе другие прило
жения перестанут запускаться.
Тhis is fтее software; see the product documentation or source code, for copying and
redistnЪution conditions. Тhere is NO WARRANТY; not even an implied WARRANiY OF
MERCНANTAВILПY, nor of FIТNESS FOR ANY PARТICULAR PURPOSE.
Тhis tool wiU guide you through the first time setup of the MinGW lnstallation Manager
software (mingw-get) on your computer; additionaDy, it wШ offer you the opportunity to
install some other common components of the MinGW software distribution.
After first time setup has been completed, you should invoke the MinGW lnstallation
Manager directly, (either the CLI mingw-get.exe variant, or its GUI counterpart,
according to your preference), when you wish to add or to remove components, or to
upgrade your MinGW software instaHation.
16 Глава 1
� Change
If you elect to change th1s, you are advised to avo,d any cho1ce of directory vhich
includes white space ,,,,ithin the absolute representation of its path паmе.
Details
1пngh1-ger; -1"�:'J-IhFO setup: un.packi ng -r.-i ng"·-get-setup-0. 6. з-т, ng,,·32-pre-201 л,
70905-1-x,r,l. tar. xz
'Тli ПQ\'1'-get: :'J-�:t: II\FO setup: updat'ing ins.ta11atio database
mi ng�,·-get: "'�1: II\FO setup: r e:gi ste.r 1,р ng\, -get-0. 6. 3-'Т"" rgд•32-pr е-201 -0905 -1
-b1n. tar. xz
mingw-get: ::,с--:: It-.FO se up: i-eg· ster :r-: ng-.,·-get-o. 6. З-rrir,g·дЗ2-prё-201-0905-1
-gu1. tar. Х2
,,--rgo,•-ge.t: :: -:: � IhFO setup: reg, ster 'У\. ng\, -ge.t-0. 6. 3-т-, r9,-.З::!-cre-:01,o:юs -1
-11c.tar.xz
•ri"•ng-.-.-get: �::-:: Ihi::o setup: rsta ai::- оп database updat:ed
SubSystem: ming•x32
Package Name: mlng•н32-gcc-g++
Componeп.t Class: Ыn
. talled Versiori: nane
Ins
Repository Version: gcc-c++-8.2.0-З-mingwЗ2-Ьin.tar.xz
Package URL: http://osdn.net/dl!m1n9w/gcc-c++-в.2.o-з-min9w32-Ьin.tar,xz
Licence URL: http:/ /osdп.net/dl/ming·•н/gcc-8.2.0-3-mingwЗ2-lic,tar.'"'
Scurce URL: http://osdn.net/dl!mingw/gcc-8.2.0-З-mingw32-s:-c.tar.""
Okay to proceed?
The package changes itemised below will
Ье implemented when you choose "Apply"
18 Глава 1
AII changes were applied successfully; you may now close this dialogue.
1 ;ns-ralling coreut:ils-5.97-3-msys-1.o.1з-Ыn.tar.1zma
1
1 "��:�] {;�� �1iь�/;�;��:;���i�ь���\1ff� i:�;��zma
1
!i nstal1: msysCORE-1.0.19-1-msys-1.О.19-Ыn.-саг.xz
insta11ing msysCORE-1.0.19-1-msys-1.О.19-Ыn.tar.xz
'insta11: termcap-o.20050421_1-2-msys-1.О.13-Ы n.tar.lzma
1 installin.9 t:er,ncap-o.2ooso421_1-2-msys-1.o.1з-bin.tar. lzma
;inst a11: libregex-1. 20090805-2-msys-1. 0.13-dll -1.tar.lzma
i ·nstalling Hbregex-1.2009osos-2-msys-1.o.13-d11-1.tar.1zma
'install: li bter11cap-0.20050421_1-2-msys-1. 0.13-dl 1-0.tar.lzm a
! installing libtermc ap-o. 20050421_1-2-msys-1. 0.13-dl1-о.tar.lzma
;inst al1: 1 i bbz2-1.о.6-1-msys-1. 0.17-dl1-1.tar.lzma
installing libbz2-1.0.6-1-msys-1.o.1,-dll-1.t ar.lzma
1, install: msysCORE-1.0.19-1-msys-1.о.19-ex"t.tаг.xz
; instal1 ing msysCORE-1.0.19-1-msys-1.0.19-e.xt.tar. xz
. instal1: bzi р2-1.о.6-1-msys-1.о.17-Ьin.tar.lzma
; nstal1ing bzi р2-1.о.6-1-msys-1.о.1.7-Ыn. tar.lzma
i nstal1: bash-3.1. 23-1-msys-1.о. 18-Ыn.t ar.xz
. installing bash-з.1.в-1-msys-1.o.1s-Ыn.tar.xz
'instal1: "1nsys-base-2013072ЗOO-msys-Ыn.meta
1 installing msys-base-2013072300-msys-Ыn.meta
--- ____ j
C:\Users\Unicross>cd С:\
C:\>set Path=C:\MinGW\bin;C:\MinGW\msys\l.0\bin;%Path%
C:\>gcc --version
gcc (MinGW.org GCC-8.2.0-3) 8.2.0
Copyright (С) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for МERCНANTAВILITY or FITNESS FOR А
PARTICULAR PURPOSE.
C:\>g++ --version
g++ (MinGW.org GCC-8.2.0-3) 8.2.О
Copyright (С) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for МERCНANTAВILITY or FITNESS FOR А
PARTICULAR PURPOSE.
Первая команда (cd с:\) делает текущим корневой каталог диска С:. Вторая коман
да (set Path=C:\MinGW\bin;C:\MinGW\msys\l. 0\Ьin;%Path%) изменяет значение сис
темной переменной РАТН для текущего сеанса. Пути к каталогам C:\MinGW\bln и
C:\МinGW\msys\1.0\Ьin мы добавили в самое начало переменной РАТН. Третья
команда (gcc --version) выводит версию программы gcc.exe. Эту программу мы
будем использовать для компиляции программ, написанных на языке С. Четвертая
команда (g++ --version) выводит версию программы g++.exe. Эту программу можно
использовать для компиляции программ, написанных на языке С++. Фрагменты
Tlgm: @it_boooks
@echo off
title Заголовок окна
cd С:\
echo Текущий каталог: %СО%
@echo.
set Path=C:\MinGW\bin;C:\MinGW\msys\1.0\bin;%Path%
rem Вывод версии gcc.exe
echo gcc --version
@echo.
gcc --version
rem Вывод версии g++.exe
echo g++ --version
@echo.
g++ --version
pause
20 Глава 1
lnstalling MinGW-Wб4
MinGW-W64
�ancel
lnstalling MinGW-W64 - о
Settings
Specify setup settin.gs.
Version (�;\·О---.
. ;.:::::::::::-·;;..~_;_;;_:-:.. �
Aгchitecture •х86_64
i
Threads v,i:'_�2.
___
·-� .. __ __,_
_
.... . . .......
·-,
·-·:
�
v!
· 1
:====== -- .:.1
Exception , se.h
Build revision I о
J;;ancel
22 Глава 1
lnstalling x86_64-8.1.0-\·,in32-selнt_vб-rev0
Installation folder
Select а dest,nation folder where хSб-64-S.l.0-winЗ2-seh-rt_vб-rev0 will
Ь� installed.
Destination folder
.§:rowse ...
lnstalling x86_64-8.1.0-1vin32-seh-rt_vб-revO
Installing Files
Copying x36_64-3.1.0-win32-seh-rt_v6-rev0 files. to yaur computer.
�linG\'I-W64
lnstalling x86_64-8.1.0-win32-seh-rt_vб-rev0 - □
x86_64-S.1.0-win32-seh-rt_vб-revO has
been successfully installedl
�1inGW·"Nб4
C:\>set Path=C:\MinGW64\mingw64\bin;%Path%
C:\>gcc --version
gcc (x86_64-win32-seh-rev0, Built Ьу MinGW-W64 project) 8.1.0
Copyright (С) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for МERCНANTAВILITY or FIТNESS FOR А
PARTICULAR PURPOSE.
C:\>g++ --version
g++ {x86_64-win32-seh-rev0, Built Ьу MinGW-W64 project) 8.1.О
Copyright {С) 2018 Free Software Foundation, Inc.
This is free _software; see the source for copying conditions.
There is NO warranty; not even for МERCНANTAВILITY or FIТNESS FOR А
PARTICULAR PURPOSE.
Tlgm: @it_boooks
24 Глава 1
C:\>set Path=C:\MinGW32\mingw32\bin;tPatht
C:\>gcc --version
gcc (i686-win32-dwarf-rev0, Built Ьу MinGW-W64 project) 8.1.0
Copyright (С) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCJ--!ANTAВILITY or FITNESS FOR А
PARTICULAR PURPOSE.
C:\>g++ --version
g++ (i686-win32-dwarf-rev0, Built Ьу MinGW-W64 project) 8.1.0
Copyright (С) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCНANTAВILITY or FITNESS FOR А
PARTICULAR PURPOSE.
Каталог установки
Укаж;пt каталог дл;; 'f:TO!"'iOБI0-1 MSYS2 6-Ьt.
0§_,ор..•
i Accessib1llty
Acc.essories
Administrati•1� Тools
AviSynth
CodeBlccks
easyHDR BASIC 2
Gluon ���J
26 Глава 1
100%
Устi!IНОВК.! 34еерwена!
•[1-i•[J
Заnю. м,,r,ti,nance Tool.
1
Установка 3аверше11а!
���--���������--·- - _> � 1
J.алее >
rJJ - □-
Теперь можно установить библиотеку MinGW-W64. Для этого в окне вводим сле
дующую команду:
pacman -S mingw-w64-x86_64-toolchain
Для установки всех компонентов нажимаем клавишу <Enter>, а затем на запрос
подтверждения установки вводим букву У и нажимаем клавишу <Enter>. Библиоте
ка MinGW-W64 будет установлена в каталог C:\msys64\mingw64. Давайте проверим
установку, выполнив в командной строке следующие команды:
C:\Users\Unicross>cd С:\
C:\>set Path=C:\msys64\mingw64\bin;%Path%
C:\>gcc --version
gcc (Revl, Built Ьу MSYS2 project) 8.2.1 20181214
Copyright (С) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for МERCНANTAВILITY or FITNESS FOR А
PARTICULAR PURPOSE.
C:\>g++ --version
g++ (Revl, Built Ьу MSYS2 project) 8.2.1 20181214
Copyright (С) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for МERCНANTAВILITY or FITNESS FOR А
PARTICULAR PURPOSE.
Tlgm: @it_boooks
28 Глава 1
- □-
30 Глава 1
Значение 1.8 указывает на то, что нужна Java 8. Устанавливаем на компьютер тре
буемую версию Java и задаем путь к ней с помощью опции -vm. Самый простой
способ - создать ярлык для eclipse.exe, отобразить окно Свойства и в поле Объект
указать такое значение (замените фрагмент <Путь к JRE> реальным значением):
C:\cpp\eclipse\eclipse.exe -vm "<Путь к JRE>\bin\server\jvm.dll"
Можно использовать и более новую версию, например Java l О, но не выше, т. к.
в версии l l некоторые пакеты были удалены. Пример указания пути к Java l О:
C:\cpp\eclipse\eclipse.exe
-vm "C:\Program Files\Java\jre-10\Ьin\server\jvm.dll"
ПРИМЕЧАНИЕ
Все способы указания значения подробно описаны на странице
http://wiki.eclipse.org/Eclipse.ini.
[ ).aunch Cancel
. _ /
Preferences □
А Gt:ner,I
t,. Appe,rance
Compare/Patch
Сontent Тypes О Bd"resh using natiw hoolcs or polling
f'> Editors � Refresh оп accej;s
Globalization О Always tlose unrelated projects without prompt
Keys
Link Handlers
:Wortcspace s� inte:rval (in ПUnutes): !S
р Network Connections
Notifications Window title
PerspкtivlfS � Show worlcspкe n.,m�
Projкt Naturб
О Show per,peqive name
Search
� Security
О Show full workspace path: C:\cpp\projects
;, RPM
> Run/Debug
t, Team Restore Qdaults j L__8Р&,_
j Apply and Close] Cancd
1
=:
� С/С++ Generat J..oc.ation: C:\cpp\projects\Т еsШс ��
-
linux Тools Path
ust mocf,tied: 16 яноаря 2919 г, 09:39:45
Project Natures
Iext file encoding
Project Referencб
О Jnherited from cont..iner (Cp12S1)
Run/Debug Settings
;> Т ask Repository @Qther. [зш.о vj
TaskTags
О ,Store the encoding of derived rfioшces separately
• Validotion
V/ikiТext New text file line delimiter
@ lnhffited from cont,iner(Windows)
О Other: V'/1ndc�.-s
32 Глава 1
(рис. 1.25) слева в списке выбираем пункт Resource, а затем справа в группе Text
file encoding указываем нужную кодировку.
По умолчанию редактор вместо пробелов вставляет символы табуляции. Нас это не
устраивает. Давайте изменим настройку форматирования кода. Для этого в меню
Window выбираем пункт Preferences. В открывшемся окне переходим в раздел
С/С++ 1 Code Style I Formatter (рис. 1.26). Нажимаем кнопку New. В открывшемся
окне (рис. 1.27) в поле Profile name вводим название стиля, например MyStyle, а из
списка выбираем пункт K&R [built-in]. Нажимаем кнопку ОК. Откроется окно
(рис. 1.28), в котором можно изменить настройки нашего стиля. На вкладке
Indentation из списка ТаЬ policy выбираем пункт Spaces only, а в поля lndentation
size и ТаЬ size вводим число з. Сохраняем все изменения.
Formatter
General
� C/C+
Aь_ti\'€ profiie:
Appearance
Arduino MyStyle fdit, ..
дutotools
lшport...
Build
CM•ke
CodeAn•lyш
• Code Style
_Code Тen, pl•tes
, #include <rr,ath. 11>
Formatte:r
Name Style
class Point {
Organize lncludes p uЫic:
Core Bu1ld Т oolchains Point(douЫe х, douЫe у)
Debug х(х), у(у) {
Docker Container }
douЫe distance(const Point& other) const;
Editcr
File Туре, douЫe х;
!ndexer douЫe у;
l•ngu•g• Mappings };
l\•leson
dollЬle Pcint: : distance ( const Point& other) const {
New С/С-• Proj,ct W,zard
douЫe dx = х - other. х;
Profiling douЫe dy = у - cther, у;
Property Page:s Settings 1·etu1·n sq·t(dx ' dx + dy • dy);
Qt
Task Tag,
Тemplate Default Value.s
Changelog
Docker
Help.
lnst•II/Update Restore _Qefaults 8ppl y
frofile n•rm:
ок 1. ___ Concel
Profile 'MyStyle'
frofilen•m<= �Мy�S�tyle� -
------------- -
------------------�
(j)
34 Глава 1
Preterences - □
t ty!::.!_ilteг text Colors and Fonts
" General
Colors and .Eonts (font, siz� type, ? = any character, ... = any string) :
• Appeisrance
Colors and Fonts type fi!ter t�xt
ф New Projed □
Selectawiard
Create • new С project
'l,!inrds:
1 typefilte-, t--,----------
ex .
;, В General
�fс:,С/Сн
Arduino Project
1�
�
С Prqjectj
!]'] С/Сн Project
@з С++ Project
� Makefile Project with Existing Code
t,ic:,RPM
> В Tracing
, fc:, Examples
f:inish Cancel
ф С Project □
г·
с Projкt --◊
Create С project of sele<ted type
� Show project types and toolchains only if th<!y are supported оп the platform
_..cE_in_i__
sh _,11,_, __еа_пс_,_,__,
_.!::!_бt_>_�I 1...l
36 Глава 1
С Project - □
Вasic Settings
Basic properties of а project
Author unicross
Source
С Project - □
Seled Configurations
Select platforms and configurations you wish to deploy on
____,
-,
,' Advanced sottings... l
Cancel
�.:J
Logging
Environment v.!riables to set
Sd:tin95
Origin
[seю... j
Toof Chatn Editor 1 VariaЫe Va-lue
> С/С-+-+ General ;! " · 8\JILD,SVSТEM )
L��::_J
CWD • __(;:_\cpP\proj,cts\Тt!Sl>Zc\Del>uq
linux Т ools Pгth MINGW_НOME C:\MinGW32\mingw32 USER: CONFIG
Project Natures МSУS_НОМЕ C:\MinGWЗ2\mingw32\msys\ 1 .О USER: CONFIG
Project References 0-ele:te
РАТН S(MINGW_HOME}\Ьin;S(MSYS_HOME}\Ыn;S(MSYS_.,, BUILD .SYSTEM
Run/Debug Sвtings BUILD SYSТEM
;> Т ask Repository
PWD C:\cpp\projects\Т est32c\0.,bug
�
T•sk Tags
t- V!lidation String list МоМ: Cc.njunФon
@Append varia� to native environment
Wiki1<Xt +Мoo]f:t
О Replace natWe №vironment with speciftIO one
38 Глава 1
Canc�
1"
[, Resource
Builders -------------, ------� ,,,.
• С/С++ Build Conf19urot1on: i [ AJIconfogurltions ] " , ; М....gе Confogurot,ons...,
'
Build VariaЬl6
Environment
Lo9ging � тool s.tt,ngs · в- C�nta,ner Stettings 1 ,;,,. Build Steps P�\!Гв�,ldдrtii�Т �ы Вinщ p....,IS I о н�I
Settings
О Check syntax only (-fsyntax-only) 1�-
Ш
Тool Chain Editor • � GCC AssemЫer
t> CJC++ General
Linux Тools Path
@Gen,ral
• � GCC С Compiler
О Pedantic (-pedantic)
О PШntic wamings as errors (-pIOantk-errors)
r
Project №tures � Dialect О lnhibit all warnings (-w)
Project References � Preprocessor
�Allwarnings (-Wall)
Run/Debug Settin9s @lncludб
� Тosk Repository @ Optimiution О Extra warnings (-Wextra)
Task Ta9s @ Debugg�ng [] Warnings as errors (-Werror)
1, Validation �-�'!'.!'!�.9'.1 � lmplicit conversion wamings (-Wconvt:rsion)
\'/ikiText @ Miscellan�us
• � MinGW С Linker
@General
@llbrari6
� Miscellaneous
40 Глава 1
Run As - □
Oescription
Runs а locol С/С++ application C
1 @·[\Blillit;-• O·g�•Q.·
J Build 'Debug' for project 'Т est32c' �
�---�г--�
�__о_к_�l
1.......
Cancel . .J 4 3
l
- □
□-,
ф
! � P;;;j::t Explo,;; � � _., . ' � . -�- EJ % 1 �.. ��-
j •'3Т�2с
• r,,� Binaries
• ii;Ji! lncludes
� @ C:/MinGW32/mingw32/iб86-w64·mingwЗ2/include
>@ C,/MinGW32/mingw32/liЬ/gccfi686-w64-mingw32/8,1,0/include
1
:, @ C:/MinGW32/mingw32/liЬ/gccfo686-w64-mingw32/8,1,0/include-fixed j
�@ src '
•в Debug
� (а, src
� � Тest32e,exe • [x86/le]
•1'3 Testб4c
�f/.Binaries
• � lncludes
:, @ �/msys64/mingw64/include
,, @ C:/msysб4/mingwб4/lib/g<e/x86_64-w64-mingw32/8.2.1/include
>@ C:/msysб4/mingwб4/liЬ/gcc/x86_64-w64-mingw32/8.2.1/includtcfixed
� @ C,/msysб4/mingwб4/x86_64-w64-mingwЗ2/include
;, � src
• (о �bug 11
�(osrc
�,r,,.__т�est64c--.ex_e_-_�t �m-d64/_1e_1_I _____________), 1
42 Глава 1
lnstalt - □
AvailaЫe Software
Check the items that you wish to install.
,Select AII
Name Version Q.eselectдll
; 4 � GQO Programming Languages
�'at С/С++ V"ISШI С++ Suppor1 9.6.0.201811241055 1
___J.
1 item selected
Details
Support for building project using the Visual С++ compiler.
� Show only the l•test versions of availaЫe software � J:iide items that are already installed
� yroup item, Ьу category What is already 1nstalled?
О Show only software applicaЫe to target environment
� rontact all update sites during install to find required software
С Project - □
С Project
Projю name must Ье sp�ified
•
Empty Project MinGWGCC
Hello World ANSI С Project
� G:;, Shared Library
v (с Static Library
� (с Makefile projю
� Show project tурб and toolchains only if th� are supported оп the platform
-________;i
! <Васk Cancel
L_ __
ГЛАВА 2
Первые шаги
Первые шаги 45
#include <stdio.h>
int main(void) [
printf("Hello, world!");
return О;
46 Глава 2
Первые шаги 47
Теперь все готово для компиляции. Выполняем следующую команду (текст коман
ды указывается на одной строке без символа переноса):
C:\book>gcc -Wall -Wconversion -03 -finput-charset=cpl251
-fexec-charset=cpl251 -о helloworld.exe helloworld.c
Первое слово (gcc} вызывает компилятор gcc.exe. Флаг -Wall указывает выводить
все предупреждающие сообщения, возникающие во время компиляции программы,
флаг -Wconversion задает вывод предупреждений при возможной потере данных,
а флаг -03 определяет уровень оптимизации. С помощью флага -finput-charset
указывается кодировка файла с программой, а с помощью флага -fexec-cl)arset -
кодировка С-строк. Название создаваемого в результате компиляции файла
(helloworld.exe) задается после флага -о. Далее указывается название исходного тек
стового файла с программой на языке С (helloworld.c}.
Если в процессе компиляции возникнут какие-либо ошибки, то они будут выведе
ны. Эти ошибки следует исправить, а затем запустить компиляцию повторно. Если
компиляция прошла успешно, то никаких сообщений не появится, а отобразится
приглашение для ввода следующей команды:
C:\book>gcc -Wall -Wconversion -03 -finput-charset=cpl251
-fexec-charset=cp1251 -о helloworld.exe helloworld.c
С:\book>
После успешной компиляции в каталоге С:\Ьооk будет создан файл helloworld.exe,
который можно запустить из командной строки следующим образом:
C:\Ьook>helloworld
Hello, world!
Вторая строка в этом коде- это результат выполнения программы.
48 Глава 2
С Project - □
С Project
Create С project of selected type
�о ,,,;, п C:\cpp\projects\He\ioNorld
•
Empty Project 1 MinGWGCC
Hello World ANSI С Project
� Shared Library
о в Static Library
� "3, Makefile project
� Showproject types and toolchains only if they are supported on the platform
�_f._in_is__
h _,j l
Далее создадим каталог для файлов с исходным кодом. Для этого в окне Project
Explorer щелкаем правой кнопкой мыши на названии проекта и из контекстного
меню выбираем пункт New I Source Folder (рис. 2.2). В открывшемся окне
(рис. 2.3) в поле Folder name вводим src и нажимаем кнопку Finisb.
Первые шаги 49
Source folder
ф Exclusion patterns of 1 source folder(s) updated to solve nesting.
(J)
Source File
Сгеэtе а n� sourc� file. с:
:.=...J
Iemplate: : <Non
_ •_>______,-,,_· ____ __ ___ .., '. Config ur e...
·г.. ···-········. · ••"···
(J)
50 Глава 2
Set Encoding
0 Qther. Ср1251
1 Cancel
дррlу �1 __ о_к_�
Header File
Create а new header file.
f.inish j Г Cancel
В результате файл будет создан в каталоге src и открыт на отдельной вкладке. При
чем внутри файла будет вставлен код, приведенный в листинге 2.2.
Первые шаги 51
52 Глава 2
ловочных файлов, как будто название указано внутри угловых скобок. Таким спо
собом обычно включаются заголовочные файлы проекта.
Можно указать просто название заголовочного файла:
#include "HelloWorld.h"
абсолютный путь к нему:
#include "C:\\cpp\\projects\\HelloWorld\\src\\HelloWorld.h"
или относительный путь к нему:
#include "./HelloWorld.h"
Если указано только название заголовочного файла и этот файл не входит с состав
проекта (или название указано внутри угловых скобок), нужно дополнительно ука
зать место поиска заголовочных файлов. Давайте попробуем подключить файл jni.h
(находится в каталоге <Путь к JDK>\include):
#include "C:\\Prograrn Files\\Java\\jdk-10\\include\\jni.h"
Даже если мы укажем абсолютный путь к этому файлу, все равно получим сообще
ние об ошибке. Дело в том, что внутри файла jni.h подключается файл jni_md.h, кото
рый расположен в каталоге <Путь к JDK>\include\win32. Компилятору
Первые шаги 53
Directory:
! C:\Prog,om FilesVova\jdk-1(1\includ�
.____о_к -�1 !
C.nce 1 ·1 Worltspaco... i i file syst�m... j
54 Глава 2
- □
EJ
CDT Global Build Console
19�51:39 .,,.,,.,,., Increrr;ental Build of configuraticn Debug for pr-oject Helloblorld "'"'**
••• •• •• ••• • •• • •• • О<•••••• •Н•••• •••••• • ••"''''' •••••
� BuildAII Ctrl+B
�, Hello ____________ ►1
iв
Build Conf�gura_ t i _on_ . s___ _►_ ,�_S_e t_A_ct_iv_•____ 1 Debug
===========г---------,
2��� - _ -� - Jl
___
,г.;·1
0
h> Build Project Manage. ,
_,.,!,
Build Working Set J
1
Build Ьу Working Set
Clean, ..
, wo SetActive Ьу Working Set
...:::_, Build Automatically Mariage Wc,rk1ng Se1s, ..
Build Тargets
С/С++ lndex
Properties
Первые шаги 55
При создании пустого проекта в настройках режима Release не указан режим опти
мизации и не отключен вывод отладочной информации. Давайте это исправим. Оr
крываем свойства проекта (в меню Project выбираем пункт Properties) и из списка
слева выбираем пункт С/С++ Build I Settings. На отобразившейся вкладке из спи
ска Configuration выбираем пункт Release, а затем на вкладке Tool Settings выби
раем пункт GCC С Compiler I Optimization. Из раскрывающегося списка
Optimization Level (рис. 2.12) выбираем пункт Optimize most (-03). Затем выбира
ем пункт GCC С Compiler I Debugging и из раскрывающегося списка Debug Level
выбираем пункт None.
Далее укажем кодировку файла и кодировку С-строк. Дriя этого в свойствах проек
та из списка слева выбираем пункт С/С++ Build I Settings. На отобразившейся
вкладке из списка Configuration выбираем пункт АН configurations, а затем на
вкладке Tool Settings из списка выбираем пункт GCC С Compiler I Miscellaneous
(см. рис. 1.35). В поле Other flags через пробел к имеющемуся значению добавляем
следующие инструкции:
-finput-charset=cpl251 -fexec-charset=cp1251
Содержимое поля Other flags после изменения должно быть таким:
-с -fmessage-length=O -finput-charset=cp1251 -fexec-charset=cp1251
Затем на вкладке Tool Settings из списка выбираем пункт GCC С Compiler 1
Warnings. Устанавливаем флажок -Wconversion. Проверяем также установку
флажка -Wall. Сохраняем настройки проекта, нажимая кнопку Apply and Close.
Settings
56 Глава 2
После изменения настроек при компиляции в режиме Release в окне Console ото
бразятся следующие инструкции:
20:06:27 **** Incremental Build of configuration Release for project
HelloWorld ****
Info: Internal Builder is used for build
gcc "-IC:\\Program Files\\Java\\jdk-10\\include"
"-IC:\\Program Files\\Java\\jdk-10\\include\\win32" -03 -Wall
-Wconversion -с -fmessage-length=0 -finput-charset=cp1251
-fexec-charset=cp1251 -о "src\\HelloWorld.o" " .. \\src\\HelloWorld.c"
gcc -о HelloWorld.exe "src\\HelloWorld.o"
C:\book>C:\cpp\projects\HelloWorld\Release\HeJloWorld.exe
Hello, world!
Запустить программу в редакторе Eclipse можно несколькими способами:
□ в меню Run выбираем пункт Run;
□ в меню Run выбираем пункт Run As, а затем профиль запуска;
□ нажимаем комбинацию клавиш <Ctrl>+<F11>;
□ на панели инструментов нажимаем кнопку с зеленым кругом и белым треуголь-
ником внутри него (см. рис. 2.11).
Если программа была изменена, то автоматически запустится процесс компиляции,
а затем результат отобразится в окне Console. Согласитесь, очень просто и удобно,
когда компиляция и запуск выполняются нажатием всего одной кнопки.
Для изменения настроек конфигурации запуска в меню Run выбираем пункт Run
Configurations. В итоге отобразится окно (рис. 2.13), в котором можно указать спе
циальные настройки для уже созданных конфигураций или создать новую конфи
гурацию запуска.
Tlgm: @it_boooks
Первые шаги 57
Run Configurations
·- ··-·
IC $ll � Xi � 1Р. .t:jame: J HelloWond.exe -·-- --· !
'
•-н«м••
j type filter text ifl Mai�', (>с), Arguments). Environment) [!J �ommon{
@') Build Dcx:ker lmage
frojю:
А!
;I
,,, � С{С++ Application
,� НelloWodd.-1
HelloWond ____]i i,� .. 1
(!J Тest32c.exe
1il
С/С++ Application:
t!:)Test64c.exe
i Debug/HelloWorld.exe
[rJ С/С++ Container Launcher
[:[] С/С++ RemoteApplication
1 Y:ariюles••. 1 1S-ch Pr oject... i\ Browse... 1�
(1
Cij С/С•+ Unit
Build [,t required) lmor• launching
О Docker Compos•
!!J Launch Group Build Configuration: 1Dюug __\,/j
....: ,S;
► Launch Group (Deprecated)
Launch over Serial 0 ЕnаЫе auto build
@ Use workspace sdtings
О DjQЬle auto build
�onfigur� \Vorl:s11ace Settings...
1
f1 Run Docker lmage
"i
R�yert Appl
Filt� matched 13 of 14 items
1· !!,un
58 Глава 2
Первые шаги 59
дактор пометил строку красным кругом с белым крестиком внутри. Если навести
указать мыши на эrу метку, то отобразится текст ошибки.
При объявлении можно сразу присвоить начальное значение переменной. Присваи- ..
ванне значения переменной при объявлении называется инициш,uзацией перемен
ной. Для того чтобы произвести инициализацию переменной, после ее названия
указывается оператор =, а затем значение. Пример:
int х = 10;
Если глобальной переменной не присвоено значение при объявлении, то она будет
иметь значение о. Если локальной переменной не присвоено значение, то перемен
ная будет содержать произвольное значение. Как говорят в таком случае: перемен
ная содержит "мусор".
Обратите внимание на то, что перед оператором = и после него вставлены пробелы.
Количество пробелов может быть произвольным, или пробелов может не быть во
все. Кроме того, допускается вместо пробелов использовать символ перевода стро
ки или символ табуляции. Например, эти инструкции вполне допустимы:
int х =21;
int у= 85;
int z
56;
Тем не менее следует придерживаться единообразия в коде и обрамлять операторы
одним пробелом. Следует учитывать, что это _не строгие правила, а лишь рекомен
дации по оформлению кода.
Как видно из предыдущего примера, инструкция может быть расположена на не
скольких строках. Концом инструкции является точка с запятой, а не конец с�роки.
Например, на одной строке может быть несколько инструкций:
int х = 21; int у= 85; int z = 56;
Из этого правила есть исключения. Например, после директив препроцессора точка
. с запятой не указывается. В этом случае концом инструкции является конец строки.
Директиву препроцессора можно узнать по символу # перед названием директивы.
Типичным примером директивы препроцессора является директива #include, кото
рая используется для включения заголовочных файлов:
#include <stdio.h>
После объявления глобальных переменных могут располагаться объявления функ
ций. Такие объявления называются прототипами. Схема прототипа функции вы
глядит следующим образом:
<Тип возвращаемого значения> <Название функции>(
[<Тип> [<Параметр 1>]
[, ... , <Тип> [<Параметр N>]]]);
Например, прототип функции, которая складывает два целых числа и возвращает
их сум му, выглядит так:
int sum(int х, int у);
Tlgm: @it_boooks
60 Глава 2
Вся реализация функции должна быть расположена внутри фигурных скобок. От
крывающая скобка может находиться на одной строке с определением функции,
как в предыдущем примере, или в начале следующей строки. Пример:
void print(int х)
{
printf("%d", х);
Первые шаги 61
Значение void внутри круглых скобок означает, что функция не принимает пара
метры. Этим прототипом мы пользовались в листинге 2.1.
Второй прототип применяется для получения значений, указанных при запуске
программы из командной строки (см. листинг 2.1 О). Количество значений доступно
через параметр argc, а сами значения через параметр argv. Параметр penv в третьем
прототипе позволяет получить значения переменных окружения (см. листинг 17.3).
Перед названием функции указывается тип возвращаемого значения. Ключевое
слово int означает, что функция возвращает целое число. Возвращаемое значение
указывается после ключевого слова return в самом конце функции rnain() . Число о
означает нормальное завершение программы. Если указано другое число, то это
свидетельствует о некорректном завершении программы. Согласно стандарту,
внутри функции rnain() ключевое слово return можно не указывать. В этом случае
компилятор должен самостоятельно вставить инструкцию, возвращающую значе
ние о. Возвращаемое значение передается операционной системе и может исполь
зоваться для определения корректности завершения программы.
Вместо безликого значения о можно воспользоваться макроопределением
EXIT_succEss, а для индикации некорректного завершения программы - макрооп
ределением EXIT_FAILURE. Предварительно необходимо включить заголовочный
файл stdlib.h. Определения макросов:
Tlgm: @it_boooks
62 Глава 2
#include <stdlib.h>
#define EXIT SUCCESS О
#define EXIT FAILURE 1
Пример:
return EXIT SUCCESS;
В листинге 2.3 приведен пример программы, структуру которой мы рассмотрели
в этом разделе. Не расстраивайтесь, если что-то показалось непонятным. Все это
мы будем изучать более подробно в следующих главах книги. На этом этапе доста
точно лишь представлять структуру программы и самое главное - уметь ее ском
пилировать и запустить на выполнение.
// Определения функций
int surn(int х, int у) {
return х + у;
void print(int х)
printf("%d", х);
Первые шаги 63
#ifndef HELLOWORLD Н
#define HELLOWORLD Н
#endif /* HELLOWORLD Н */
64. Глава 2
// Это комментарий
printf("Hello, world!\n"); // Это комментарий
// printf("Hello, world!"); // Инструкция вьmолнена не будет
printf("// Это НЕ комментарий!!!");
НА ЗАМЕТКУ
Старые компиляторы подцерживали только многострочные комментарии.
Редактор Eclipse позволяет быстро добавить символы комментариев. Для того что
бы вставить однострочный комментарий в начале строки, нужно сделать текущей
строку с инструкцией и нажать комбинацию клавиш <Ctrl>+</>. Если предвари
тельно выделить сразу несколько строк, то перед всеми выделенными строками
будет вставлен однострочный комментарий. Если все выделенные строки были за
комментированы ранее, то комбинация клавиш <Ctrl>+</> удалит все одностроч
ные комментарии. Для вставки многострочного комментария необходимо выделить
строки и нажать комбинацию клавиш <Shift>+<Ctrl>+</>. Для удаления много
строчного комментария предназначена комбинация клавиш <Shift>+<Ctrl>+<\>.
У начинающих программистов может возникнуть вопрос: зачем может потребо
ваться комментировать инструкции? Проблема заключается в том, что часто в ло
гике работы программы возникают проблемы. Именно по вине программиста. На
пример, программа выдает результат, который является неверным. Для того чтобы
найти ошибку в алгоритме работы программы, приходится отключать часть кода с
помощью комментариев, вставлять инструкции вывода промежуточных результа
тов и анализировать их. Как говорится: разделяй и властвуй. Таким "дедовским"
способом мы обычно ищем ошибки в коде. А "дедовским" мы назвали способ по
тому, что сейчас все редакторы предоставляют методы отладки, которые позволяют
выполнять код построчно и сразу видеть промежуточные результаты. Раньше тако-
го не было. Хотя способ и устарел, но все равно им часто пользуются.
Tlgm: @it_boooks
первые шаги 65
бб Глава 2
Первые шаги 67
□ % - символ процента ( % ):
printf("10%%"); // 10%
Параметр <Ширина> задает минимальную ширину поля. Если строка меньше шири
ны поля, то она дополняется пробелами. Если строка не помещается в указанную
ширину, то значение игнорируется и строка выводится полностью:
printf("'%3s"', "string"); // 'str;i.ng'
printf("'%10s"', "string"); // ' string'
Задать минимальную ширину можно не только для строк, но и для других типов:
printf(" '%10d' ", 25); // ' 25'
printf("' %10f' ", 12.5); // ' 12.500000'
Tlgm: @it_boooks
68 Глава 2
Первые шаги 69
□ + - задает обязательный вывод знака как для отрицательных, так и для положи
тельных чисел. Пример:
printf('"%+d' '%+d"', -3, 3); // '-3' '+3'
□ 1(буква "эль") - для вывода значения переменной, имеющей тип long int.
Пример:
long int х= 21474836471;
printf("%ld", х); // 2147483647
Модификатор 1 можно использовать совместно с типами с и s, для вывода двух
байтового символа и строки, состоящей из двухбайтовых символов, соответст
венно. Пример:
wchar_t str[J = 1"string";
printf("%ls", str); // string
□ I64 - для вывода значения переменной, имеющей тип long long int. Использу
ется в Windows. Пример:
long long х = 922337203685477580711;
printf("%I64d\n", х); // 9223372036854775807
unsigned long long у= 18446744073709551615U11;
printf("%I64u\n", у); // 18446744073709551615
□ 11 - для вывода значения переменной, имеющей тип long long. При использо
вании функции printf( J в Windows при компиляции в MinGW выводятся пре
дупреждения. В Windows можно либо указать I64 вместо 11, либо использовать
функцию_mingw_printf( J вместо printf( J. Пример:
long long х = 922337203685477580711;
_mingw_printf("%lld", х); // 9223372036854775807
Tlgm: @it_boooks
70 Глава 2
□ L - для вывода значения переменной, имеющей тип long douЬle. Для того что
бы значение в MinGW в Windows выводилось правильно, нужно использовать
функцию _mingw _printf() вместо printf (). Пример:
long douЬle х = 8e+245L;
_mingw_printf("%Le\n", х); // 8.000000е+245
Если выводить несколько значений подряд с помощью функции printf(), то они
все отобразятся на одной строке:
printf("stringl");
printf("string2");
// stringlstring2
Для того чтобы значения выводились на отдельных строках, нужно воспользовать
ся комбинацией символов \n, обозначающей перевод строки:
printf("stringl\n");
printf("string2");
/*
stringl
string2 */
С помощью стандартного вывода можно создать индикатор выполнения процесса
в окне консоли. Для того чтобы реализовать такой индикатор, нужно вспомнить,
что символ перевода строки в Windows состоит из двух символов- \r (перевод
каретки) и \n (перевод строки). Таким образом, используя только символ перевода
каретки \r, можно перемещаться в начало строки и перезаписывать ранее выведен
ную информацию. Пример индикатора процесса показан в листинге 2.5.
#include <stdio.h>
#include <windows.h>
int main(void) {
printf(" ... 0%%");
for (int i = 5; i < 101; i += 5) {
Sleep(l000); // Имитация процесса
printf("\r ... %d%%", i);
fflush(stdout);
printf("\n");
return О;
C:\book>helloworld.exe
Tlgm: @it_boooks
Первые шаги 71
Эта программа немного сложнее, чем простое приветствие из листинга 2.1. Здесь
присуrствует имитация процесса с помощью функции Sleep() (при этом программа
"засыпает" на указанное количество миллисекунд). Прототип функции:
linclude <windows.h>
VOID Sleep(DWORD dwMilliseconds);
Кроме того, в программе использован цикл for, который позволяет изменить поря
док обработки инструкций. Обычно программа выполняется сверху вниз и слева
направо. Инструкция за инструкцией. Цикл for меняет эту последовательность вы
полнения. Инструкции, расположенные внуrри блока, выполняются несколько раз.
Количество повторений зависит от выражений внутри круглых скобок. Этих выра
жений три, и разделены они точками с запятой. Первое выражение объявляет цело
численную переменную i и присваивает ей значение 5. Второе выражение является
условием продолжения повторений. Пока значение переменной i меньше значения
101, инструкции внутри блока будуr повторяться. Это услов»е проверяется на каж
дой итерации цикла. Третье выражение на каждой итерации цикла прибавляет зна
чение 5 к текущему значению переменной i.
Так как данные перед выводом мoryr помещаться в буфер, вполне возможно по
требуется сбросить буфер явным образом. Сделать это можно с помощью функции
fflush(). Прототип функции:
#include <stdio.h>
int fflush(FILE *stream)
72 Глава 2
ные останутся в буфере. Ключевое слово void внутри круглых скобок означает, что
функция не принимает никаких параметров. Пример получения символа:
int ch;
printf("ch = "); // Вывод подсказки
fflush(stdout); // Сброс буфера
ch = getchar(); // Получение символа
�
printf("%d\n", ch); // Вывод кода
printf("%c\n", (char) ch); // Вывод символа
else {
рuts("Ошибка при вводе числа");
Первые шаги 73
else {
puts("Owибкa при вводе строки");
#include <stdio.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
int х = О, у= О;
printf("Bвeдитe первое число: ");
fflush(stdout); // Сброс буфера вывода
if (scanf("%d", &х) != 1) {
puts("Bы ввели не число");
return 1; // Выходим из функции main()
Tlgm: @it_boooks
74 Глава 2
первые шаги 75
#include <stdio.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
int х = О, у= О, flag = О, count = 1;
·do {
printf("Введите первое число: ");
Tlgm: @it_boooks
76 Глава 2
fflush(stdout);
fflush(stdin);
if (scanf("%d", &х) != 1) {
puts("Bы ввели не число");
// Сбрасываем флаг ошибки и очищаем буфер ввода
if (feof(stdin) 11 ferror(stdin)) clearerr(stdin);
int ch = О;
while ((ch = getchar()) != '\n' && ch != EOF);
++count;
if (count > 3) {
puts("Bы сделали три ошибки");
return 1;
else flag = 1;
while ( ! flag);
flag = О;
count = 1;
do
printf("Bвeдитe второе число: ");
fflush(stdout);
fflush(stdin);
if (scanf("%d", &у) != 1) {
puts("Bы ввели не число");
// Сбрасываем флаг ошибки и очищаем буфер ввода
if (feof(stdin) 11 ferror(stdin)) clearerr(stdin);
int ch = О;
while ((ch = getchar()) != '\n' && ch != EOF);
++count;
if (count > 3) {
puts("Bы сделали три ошибки");
return 1;
else flag = 1;
while ( ! flag);
printf("Cyммa равна: %d\n", х + у);
return О;
Первые шаги 77
78 Глава 2
#include <stdio.h>
#include <string.h>
#include <locale.h>
#include <stdlib.h>
int rnain(void) {
systern("chcp 1251") ; // Смена кодировки консоли
setlocale(LC_ALL, "Russian_Russia.1251");
char buf[256] = "", *р = NULL;
printf("Введите строку: ");
fflush(stdout);
fflush(stdin);
р = fgets(buf, 256, stdin);
if (р) {
// Удаляем символ перевода строки
size t len = strlen(buf);
if (len != О && buf[len - 1] == '\n')
buf[len - 1] = '\0';
// Выводим результат
printf("Bы ввели: %s\n", buf);
Первые шаги 79
80 Глава 2
#include <stdio.h>
#include <conio.h>
#include <locale.h>
int rnain(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
char passwd[l7] "";
int flag = О, i О, ch = О;
printf("Bвeдитe пароль: ");
fflush(stdout);
do (
ch = _getch();
if (i > 15 1 1 ch == '\r' 1 1 ch '\n') (
flag = 1;
passwd[i] = '\0';
passwd[i] = (char)ch;
printf("%s", "*") ;
Tlgm: @it_boooks
первые шаги 81
fflush(stdout);
++i;
while ( ! flag);
ОБРАТИТЕ ВНИМАНИЕ
Код из листинга 2.9 будет работать только в консоли. Запуск в редакторе Eclipse при
ведет к бесконечной работе программы.
82 Глава 2
вой символ в конец массива. Он будет обозначать конец строки с введенным паро
лем. После этих инструкций управление передается в конец цикла и производится
проверка условия ! flag. Так как условие вернет Ложь ( !Истина), происходит выход
из цикла. После выхода из цикла производим вывод введенного пароля на консоль.
Если ввод пароля не закончен, то производим проверку вхождения символа в до
пустимый диапазон значений (цифры от о до 9 и латинские буквы от а до z в любом
регистре). Выполнить проверку допустимости символа необходимо, т. к. функция
_getch () возвращает также коды некоторых служебных клавиш, например <Ноте>,
<End> и др. Если условие не соблюдается, то выводим сообщение об ошибке и воз
вращаем значение о, тем самым завершая выполнение программы.
Проверяемое условие содержит более сложное выражение, нежели в предыдущем
условии. Выражение разбито на несколько мелких выражений с помощью круглых
скобок. Внутри первых круглых скобок проверяется соответствие символа диапа
зону чисел от о до 9. Цифра о соответствует коду 48, а цифра 9- коду 57. Коды ос
тальных цифр находятся в диапазоне между этими кодами. Дnя того чтобы не пе
речислять в выражении коды всех цифр, используют проверку двух условий, разде
ленных оператором && (логическое и). Выражение ch > 47 && ch < 58 следует
читать так: если переменная ch содержит символ, имеющий код больше 47 и мень
ше 58, то вернуть значение истина, в противном случае - значение ложь. Если ус
ловие внутри первых скобок является истинным, то все выражение является истин
ным и дальнейшая проверка не производится. Если условие внутри первых круглых
скобок является ложным, то проверяется условие внутри вторых круглых скобок.
Условие внутри третьих круглых скобок проверяется, только если условие внутри
вторых круглых скобок является ложным. Вместо кодов символов можно указать
сами символы внутри апострофов. В этом случае условие будет выглядеть так:
else if (ch >= 'О' && ch <= '9') // Цифры от О до 9
11 (ch >= 'А' && ch <= 'Z') // Буквы от А ДО Z
11 (ch >= 'а' && ch <= 'z')) // Буквы от а до z
первые шаги 83
int main(int argc, char *argv[)) {
// Инструкции
return О;
tinclude <stdio.h>
return О;
C:\Ьook>set Path=C:\msys64\mingw64\bin;%Path%
84 Глава 2
#include <stdio.h>
#include <conio.h>
#include <locale.h>
int main(void) [
setlocale(LC_ALL, "Russian_Russia.1251");
printf("Hello, world!\n");
printf("Дnя закрьrrия окна нажмите любую клавишу ... ");
fflush(stdout); // Сброс буфера вывода
fflush(stdin); // Очистка буфера ввода
_getch();
return О;
Tlgm: @it_boooks
Первые шаги 85
int main(void) {
printf("Hello, world!\n");
fflush(stdout); // Сброс буфера вывода
system("pause");
return О;
#include <stdio.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
printf("Hello, world!\n");
printf("Для закрытия окна нажмите <Enter> ");
fflush(stdout); // Сброс буфера вывода
Tlgm: @it_boooks
86 Глава 2
Первые шаги 87
fазмер
rи--
!!,!риq,т □Жирный)
j-if Consolas
iTочечные wриФты
;
Размер знаков:
Ширина 6,иксели): 7
Высота t,,кcero,): 12
ок 1 1 о-
88 Глава 2
#include <stdio.h>
#include <locale.h>
int rnain(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
printf("Пpивeт, мир!\n");
return О;
C:\book>helloworld.exe
Привет, мир!
C:\book>chcp 1251
Текущая кодовая страница: 1251
C:\book>helloworld.exe
Привет, мир!
Однако программа может быть запущена на компьютере, в котором кодировка кон
соли не позволяет отобразить русские буквы. Единственный способ полностью ре
шить проблему с кодировками - выводить сообщения на английском языке. Коды
латинских букв во всех однобайтовых кодировках одинаковые, поэтому при ис
пользовании английского языка проблем не будет. Однако тут существует другая
сложность. Пользователь может не знать английского языка. Как видите, не все так
просто с кодировками при использовании консольных приложений.
Tlgm: @it_boooks
Гlервыешаги 89
tinclude <stdio.h>
linclude <stdlib.h>
linclude <locale.h>
int rnain(void) 1
setlocale(LC_ALL, "Russian_Russia.1251");
int х = О;
printf ( "Введите число: ");
fflush (stdout);
if (scanf("%d", &х) != 1) 1
puts("Вы ввели не число");
Tlgm: @it_boooks
90 Глава 2
ГЛАВА 3
92 Глава З
НА ЗАМЕТКУ
При использовании старых компиляторов все локальные переменные должны быть
объявлены в самом начале функции.
_ -�-
asm__ _j douЫe 1 int struct _Alignof (С11)
auto else long switch _Atomic (С11)
---·------
94 Глава З
□ void - означает отсутствие типа. Используется в основном для того, чтобы ука
зать, что функция не возвращает никакого значения или не принимает парамет
ров, а также для передачи в функцию данных произвольного типа. Пример
объявления функции, которая не возвращает значения и принимает указатель на
данные произвольного типа:
void free(void *memory);
Перед элементарным типом данных могут быть указаны следующие модификаторы
или их комбинация.
□ signесt-указывает, что символьный или целочисленный типы могут содержать
отрицательные значения.
Тип signed char может хранить значения от -128 до 127. Пример:
signed char min = -128, max = 127, ch = 'w';
// Выводим символ
printf("%c\n", ch); // w
// Выводим код
printf("%d\n", ch); // 119
printf("%d\n", min); // -128
printf("%d\n", max); // 127
Tlgm: @it_boooks
96 Глава 3
Диапазон значений у типа unsigned short int (или просто unsigned short) -
от о до 65 535. Пример:
unsigned short int min = О;
unsigned short max = 65535;
printf("%hu\n", min); // о
printf("%hu\n", max); // 65535
// Выводим размер в байтах
printf("%d\n", (int) sizeof(min)); // 2
// #include <limits.h>
printf("%d\n", USHRT_МAX); // 65535
Диапазон значений у типа unsigned long int (или просто unsigned long)- от с
до 4 294 967 295. Пример:
unsigned long int min = 0U1;
unsigned long max = 4294967295U1;
printf("%lu\n", min); // о
printf("%lu\n", max); // 4294967295
// Выводим размер в байтах
printf("%d\n", (int) sizeof(min)); // 4
// #include <limits.h>
printf("%lu\n", U1ONG_МАХ); // 4294967295
Ключевое слово long перед целым типом может быть указано дважды. Диапа
зон значений у типов long long int (или просто long long) и signed long long
int (или просто signed long long) - от -9 223 372 036 854 775 808 до
9 223 372 036 854 775 807. Пример:
Tlgm: @it_boooks
98 Глава З
100 Глава З
3. 7. Оператор typedef
Оператор typedef позволяет создать псевдоним для существующего типа данных.
В дальнейшем псевдоним можно указывать при объявлении переменной. Оператор
имеет следующий формат:
typedef <Существующий тип> <Псевдоним>;
В качестве примера создадим псевдоним для типа long int:
typedef long int lint;
lint х = 5L, у= l0L;
Tlgm: @it_boooks
После создания псевдонима его имя можно использовать при создании другого
псевдонима:
�ypedef long int lint;
�ypedef lint newint;
�ewint х = 5L, у= l0L;
Псевдонимы предназначены для создания машинно-независимых программ. При
переносе программы на другой компьютер достаточно будет изменить одну строку.
Подобный подход часто используется в стандартной библиотеке. Например, прото-
1ИП функции strlen {),,позволяющей получить длину строки, выглядит так:
size_t strlen(const char *str);
3.8. Константы
Константы - это участки памяти, значения в которых не должны изменяться
во время работы программы. В более широком смысле под константой понимают
.т1юбое значение, которое нельзя изменить, например 10, 12. 5, •w•, "string".
При объявлении константы перед типом данных указывается ключевое слово const:
const int МУ CONST = 10;
printf("%d\n", МY_CONST); // 10
Обратите внимание на то, что в названии константы принято использовать буквы
только в верхнем регистре. Если название константы состоит из нескольких слов,
то между словами указывается символ подчеркивания. Эrо позволяет отличить
внутри программы констанrу от обычной переменной. После объявления констан
ты ее можно использовать в выражениях:
const int МУ CONST = 10;
int у;
у= МY_CONST + 20;
Присвоить значение константе можно только при объявлении. Любая попытка
изменения значения в программе приведет к ошибке при компиляции:
error: assignment of read-only variaЫe 'МY_CONST'
Создать констанrу можно также с помощью директивы Jldefine. Значение, указан
ное в этой директиве, подставляется в выражение до компиляции. Название, ука
занное в директиве #define, принято называть макроопределением или макросом.
Директива имеет следующий формат:
#define <Название макроса> <Значение>
Tlgm: @it_boooks
102 Глава 3
int main(void) {
int у;
у= МY_CONST + 20;
printf("%d\n", МY_CONST); // -5
printf("%d\n", у); // 15
return О;
linclude <stdio.h>
int main(void) (
printf("%d\n", LINE );
printf("%s\n", FILE );
printf("%s\n", DATE );
printf("%s\n", ТIМЕ );
return О;
Tlgm: @it_boooks
104 Глава:
int main(void) {
extern int х; // Определение в другом месте
printf("%d\n", х); // 10
return О;
#include <stdio.h>
void func(void);
int main(void) {
func(); // 1
func(); // 2
func(); // 3
return О;
void func(void)
static int � О; // Статическая переменная
++х; // Увеличиваем значение на 1
printf("%d\n", х);
106 Глава 3
#include <stdio.л>
int main{void) {
func{);
// Переменная х из функции func{) здесь не видна
// ВЫвод значения глобальной переменной х
printf{"%d\n", х); 1110
// Блок
int z = 30;
printf{"%d\n", z); // 30
}
// Переменная z здесь уже не видна!!!
for {int i = О; i < 10; ++i)
printf("%d\n", i);
}
// Переменная i здесь уже не видна!!!
return О;
void func{void) {
// Переменная z здесь не видна
int х = 5; // Локальная переменная
// ВЫвод значения локальной переменной х,
// а не одноименной глобаJ\Ьной
printf{"%d\n", х); // 5
Очень важно учитывать, что переменная, объявленная внуrри блока, видна только
в пределах блока (внуrри фигурных скобок). Например, присвоим значение пере
менной в зависимости от некоторого условия:
//Неправильно!!!
if {х == 10) { // Какое-то условие
int у;
у 5;
else
int у;
у= 25;
}
// Переменная у здесь не определена!!!
Tlgm: @it_boooks
В этом примере переменная у видна только внуrри блока. После условного опера
rора if переменной не существует. Для того чтобы переменная была видна внуrри
блока и после выхода из него, необходимо поместить объявление переменной
перед блоком:
// Правильно
int у;
if (х = 10) { // Какое-то условие
у= 5;
else {
у= 25;
printf("%d\n", у);
Если обращение к переменной внуrри функции или блока производится до объяв
ления одноименной локальной переменной, то до объявления будет использоваться
глобальная переменная, а после объявления-локальная переменная (листинг 3.4).
Если глобальной переменной с таким названием не существует, то будет ошибка
при компиляции.
tinclude <stdio.h>
int main(void)
// Выводится значение глобальной переменной
printf("%d\n", х); // 10
int х = 5; // Локальная переменная
// Выводится значение локальной переменной
printf("%d\n", х); // 5
return О;
3.11. Массивы
Массив - это нумерованный набор переменных одного типа. Переменная в масси
ве называетс)1 элементом, а ее позиция в массиве задается индексом. Объем памяти
(в байтах), занимаемый массивом, определяется так:
<Объем памяти>= sizeof (<Тип>) * <Количество элементов>
Объявление массива выглядит следующим образом:
<Тип> <Переменная>[<Количество элементов>];
Пример объявления массива из трех элементов, имеющих тип long int:
long arr[З];
Tlgm: @it_boooks
108 Глава 3
printf("\n");
Tlgm: @it_boooks
110 Глава 3
Получить или задать значение элемента можно, указав два индекса (не забывайте,
что нумерация начинается с нуля):
int arr[2] [4] = {
(1, 2, 3, 4),
(5, 6, 7, 8)
);
printf("%d\n", arr[0J[0J); // 1
printf("%d\n", arr[l] [О]);// 5
printf("%d\n", arr[l] [3]);// 8
3.12. Строки
Строка является массивом символов, последний элемент которого содержит нуле
вой символ (' \О'). Обратите внимание на то, что нулевой символ (нулевой байт)
не имеет никакого отношения к символу 'о'. Коды этих символов разные. Такие
строки часто называют С-строками.
Объявляется С-строка так же, как и массив элементов типа char:
char str[7];
При инициализации можно перечислить символы внутри фигурных скобок:
char str[7] = { 's', 't', 'r', 'i', 'n', 'g' , '\0' ) ;
3.13. Указатели
Указатель - это переменная, которая предназначена для хранения адреса. В язы
ке С указатели часто используются в следующих случаях:
□ для управления динамической памятью;
□ чтобы иметь возможность изменить значение переменной внуrри функции;
□ для эффективной работы с массивами и др.
Объявление _указателя имеет следующий формат:
<Тип> *<Переменная>;
Пример объявления указателя на тип int:
int *р = NULL;
printf("%p\n", р); // Вывод адреса
// Значение в проекте Test32c: 00000000
// Значение в проекте Test64c: 0000000000000000
printf("%d\n", (int) sizeof(p)); // Вывод размера
// Значение в проекте Test32c: 4
// Значение в проекте Test64c: 8
Очень часто символ * указывается после типа, а не перед именем переменной:
int* р;
С точки зрения компилятора эти два объявления ничем не различаются. Однако
следует учитывать, что при объявлении нескольких переменных в одной инструк
ции символ * относится к переменной, перед которой он указан, а не к типу дан
ных. Например, следующая инструкция объявляет указатель и переменную, а не
два указателя:
int* р, х; // Переменная х указателем не является!!!
Tlgm: @it_boooks
112 Глава 3
#include <stdio.h>
int х = 10;
int *р = NULL; // Нулевой указатель
int main(void)
// Присваивание указателю адреса переменной х
р = &х;
// Вывод адреса
printf("%p\n", р); // Например: 0000000000403010
// Вывод значения
printf("%d\n", *р); // 10
return О;
В этом примере при объявлении указателя ему присваивается значение NULL. Указа
тель, которому присвоено значение NULL или о, называется нулевым указателем.
Определение макроса NULL выглядит так:
#include <stdio.h>
#define NULL ( (void *) О)
Tlgm: @it_boooks
114 Глава.3
#include <stdio.h>
int main(void) {
int *р = NULL, arr[ARR_SIZE] = {10, 20, 30};
// Устанавливаем указатель на первый элемент массива
р = arr; // Оператор & не указывается!!!
for (int i = О; i < ARR_SIZE; ++i)
printf("%d\n", *р);
++р; // Перемещаем указатель на следующий элемент
Для перебора элементов массива используется цикл for. В первом параметре цикла
задается начальное значение (int i = о), во втором - условие (i < ARR_srzE), а в
третьем - приращение на единицу (++ i) на каждой итерации цикла. Инструкции
внутри цикла будут выполняться, пока условие является истинным (значение пере
менной i меньше количества элементов массива).
Внутри цикла выводится значение элемента, на который ссьmается указатель, а за
тем значение указателя увеличивается на единицу (++р). Обратите внимание на то,
что изменяется адрес, а не значение элемента массива. При увеличении значения
указателя используются правила адресной арифметики, а не правила обычной
арифметики. Увеличение значения указателя на единицу означает, что значение
будет увеличено на размер типа. Например, если тип int занимает 4 байта, то при
увеличении значения на единицу указатель вместо адреса 0x0012FFЗ0 будет содер
жать адрес 0x0012FFЗ4. Значение увеличилось на 4, а не на 1. В нашем примере вме
сто двух инструкций внутри цикла можно использовать одну:
printf("%d\n", *р++);
Выражение р++ возвращает текущий адрес, а затем увеличивает его на единицу.
Символ * позволяет получить доступ к значению элемента по указанному адресу.
Последовательность выполнения соответствует следующей расстановке скобок:
printf("%d\n", *(р++));
Если скобки расставить так:
printf("%d\n", (*р)++);
то вначале будет получен доступ к элементу массива и выведено его текущее зна
чение, а затем будет произведено увеличение значения элемента массива. Переме
щение указателя на следующий элемент не выполняется.
Tlgm: @it_boooks
116 Глава 3
�
linclude <stdio.h>
int main(void)
int у= 10;
funcl (у); // Передаем копию значения
printf("%d\n", у); // 10 (значение не изменилось!!!)
func2(&y); // Передаем адрес, а не значение
printf ("%d\n", у); // 20 (значение изменилось!!!)
return О;
i
void funcl(int х) (
х = х * 2; // Значение нигде не сохраняется
118 Глава 3
tinclude <stdio.h>
linclude <stdlib.h>
linclude <locale.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
const unsigned int ARR_SIZE = 10;
int *р = malloc(ARR_SIZE * sizeof(int));
if ( ! р) { // Проверяем на корректность
puts("He удалось ,выделить память");
exit(1); // выходим при ОUП!lбке
}
// Нумеруем элементы массива от 1 до 10
for (int i = О; i < ARR_SIZE; ++i) { // Пользуемся памятью
p[i] = i + 1;
// Выводим значения
for (int i = О; i < ARR_SIZE; ++i) { // Пользуемся памятью
printf("%d\n", p[i]);
120 Глава 3
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const unsigned int ROWS = 2; // Количество строк
const unsigned int COLUМNS = 4; // Количество столбцов
int i = О, j = О;
// Создаем массив указателей
int **р = calloc(ROWS, sizeof(int*));
if ( ! р) exit( 1); // Выходим при ошибке
// Добавляем строки
for (i = О; i < ROWS; ++i) [
p[i] = calloc(COLUМNS, sizeof(int));
if (!p[i]) exit(l); // Выходим при ошибке
}
// Нумеруем элементы массива
int n = 1;
for (i = О; i < ROWS; ++i) {
for (j = 0; j < COLUМNS; ++j)
p[i] [j] = n++;
// *(*(р + i) + j) = n++;
}
// Выводим значения
for (i = О; i < ROWS; ++i) {
for (j = О; j < COLUМNS; ++j)
printf("%3d", p[i][j]);
// printf("%3d", *(*(р + i) + j));
printf("\n");
}
// Освобождаем память
for (int i = О; i < ROWS; ++i) {
free(p['i]);
Tlgm: @it_boooks
free(p);
р = NULL; // Обнуляем указатель
return О;
tinclude <stdio.h>
tinclude <stdlib.h>
�Ilt main(void) {
const unsigned int ROWS = 2; // Количество строк
const unsigned int COLUМNS = 4; // Количество столбцов
unsigned int i = О, j = О;
int *р = calloc(ROWS * COLUМNS, sizeof(int));
if ( !р) exit(1); // Выходим при ошибке
// Нумеруем элементы массива
int n = l;
for (i = О; i < ROWS; ++i) {
for (j О; j < COLUМNS; ++j)
*(р + i * COLUМNS + j) = n++;
}
// Выводим значения
for (i = О; i < ROWS; ++i) {
for (j = О; j < COLUМNS; ++j)
printf("%3d", *(р + i * COLUМNS + j));
printf("\n");
122 Глава 3
Так как в этом случае все элементы двумерного массива расположены в смежных
ячейках, мы можем получить доступ к элементам с помощью указателя и адресной
арифметики. Например, пронумеруем все элементы:
int *р2 = р; // Сохраняем адрес первого элемента
int n = 1;
unsigned int count = ROWS * COLUМNS;
for (i = О; i < count; ++i) {
*р2 = n++;
++р2;
#include <stdio.h>
#include <stdlib.h>
int main(void) {
unsigned int arr_size = 5;
int *р = malloc(arr_size * sizeof(int));
if ( !р) exit(1); // Выходим при ошибке
// Нумеруем элементы массива
for (int i = О; i < arr size; ++i)
p[i] = i + 1;
Tlgm: @it_boooks
3.15. Структуры
Сmруктура - это совокупность переменных (называемых членами, элементами
8ЛИ полями), объединенных под одним именем. Объявление струюуры выглядит
аrедующим образом:
struct [<Название структуры>] {
<Тип данных> <Название поля 1>;
124 Глава 3
tinclude <stdio.h>
int main(void) {
rect.top_left.x = О;
rect.top_left.y = О;
rect.bottom_right.x = 100;
rect.bottom_right.y = 100;
printf("%d\n", rect.top_left.x); // о
printf("%d\n", rect.top_left.y); // о
printf("%d\n", rect.Ьottom_right.x); // 100
printf("%d\n", rect.bottom_right.y); // 100
printf("%d\n", rect2.top_left.x); // 10
printf("%d\n", rect2.top_left.y); // 20
printf("%d\n", rect2.bottom_right.x); // 30
printf("%d\n", rect2.bottom_right.y); // 40
printf("%d\n", (int) sizeof rect); // 16
return О;
126 Глава 3
#include <stdio.h>
int main(void)
struct Point *р &pointl; // Объявление указателя
р->х = 10;
р->у = 20;
printf("%d\n", р->х); // 10
printf("%d\n", р->у); // 20
printf("%d\n", (*р) .х); // 10
printf("%d\n", (*р) .у); // 20
return О;
3.17. Объединения
Обьединение - это область памяти, используемая для хранения данных разных
'IIIПOB. В один момент времени в этой области могут храниться данные только од-
8Х'О типа. Размер объединения будет соответствовать размеру более сложного типа
8ННЫХ. Например, если внутри объединения определены переменные, имеющие
'111ПЬ1 int, float и douЬle, то размер объединения будет соответствовать размеру
uma douЬle. Объявление объединения имеет следующий формат:
union [<Название объединения>] (
<Тип данных> <Название члена 1>;
128 Глава 3
union myUnion
int х;
float у;
douЫe z;
unionl;
int main(void}
unionl.x = 10;
printf("%d\n", unionl.x); // 10
unionl.z = 2.5;
printf("%.2f\n", unionl.z); 11 2.50
// Объявление переменной
Шlion myUnion union2;
Шlion2.x = 5;
printf("%d\n", union2.x); // 5
// Объявление указателя на объединение
union myUnion *р = &union2;
printf("%d\n", р->х); // 5
р->х = 20;
printf("%d\n", р->х); 11 20
printf("%d\n", union2.x); 11 20
// Определение размера объединения
printf("%d\n", (int) sizeof(union myUnion)); // 8
printf("%d\n", (int) sizeof(unionl)); // 8
return О; .
Tlgm: @it_boooks
3.18. Перечисления
Перечисление - это совокупность целочисленных констант, описывающих все
.:t0пустимые значения переменной. Если переменной присвоить значение, не совпа
�щее с перечисленными константами, то компилятор выведет сообщение об
ошибке. Объявление перечисления имеет следующий формат:
enum [<Название перечисления>] {
<Список констант через запятую>
, [<Объявления переменных через запятую>];
130 Глава З
enurn Color {
RED, BLUE, GREEN 5, BLACK
) ;
int main(void)
enurn Color color; // Объявление переменной
color = BLACK;
printf("%d\n", color); // 6
if (color == BLACK) // Проверка значения
printf("color == BLACK\n"); // Выведет: color == BLACK
else {
printf("color != BLACK\n");
return О;
Для того чтобы результат выполнения этого выражения не вызывал сомнений ком
пилятора, необходимо выполнить операцию явного приведения типов. Формат опе
рации:
(<Тип результата>}<Выражение или переменная>
Результатом первого выражения будет число 127, которое входит в диапазон значе
ний типа char, а вот результат второго выражения- число 137 - в диапазон не
входит, и происходит усечение. В итоге мы имеем число -119. Согласитесь, мы по
лучили совсем не то, что хотели. Поэтому приведением типов нужно пользоваться
осторожно, хотя в некоторых случаях такое усечение очень даже полезно. Напри
мер, вещественное число можно преобразовать в целое. В этом случае дробная
часть просто отбрасывается:
float х= 1.2f;
douЫe у= 2.5;
printf("%.2f\n", х}; .// 1.20
printf("%d\n", (int}x}; // 1
printf("%.2f\n", у}; // 2.50
printf("%d\n", (int}y}; // 2
Tlgm: @it_boooks
132 Глава 3
ГЛАВА 4
Операторы и циклы
□ - - унарный минус:
int х = 10;
printf("%d\n", -х); // -10
□ * - умножение:
printf("%d\n", 25 * 2); // 50
134 Глава 4
#include <stdio.h>
#include <locale.h>
Tlgm: @it_boooks
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
int х = О, у= О;
х = 5;
у= х++; //у= 5, х = 6
рuts("Постфиксная форма (у= х++;) :");
printf("y = %d\n", у);
printf("x = %d\n", х);
х = 5;
у= ++х; //у= 6, х = 6
рuts("Префиксная форма (у= ++х;) :");
printf("y = %d\n", у);
printf("x = %d\n", х);
return О;
136 Глава 4
□ 1 - двоичное ипи:
unsigned char х = 100; !/ 01100100
unsigned char у= 75; // 01001011
unsigned char z = х 1 у; // 01101111
#include <stdio.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
Tlgm: @it_boooks
else
str[k] = 'О';
str[B] '\0';
return str;
Tlgm: @it_boooks
138 Глава 4
:nt х = О, у= о, z = О;
][ = (у= 10, z = 20, у + z);
p::::intf("%d\n", х); // 30
printf("%d\n", у); // 10
printf("%d\n", z); // 20
В этом примере после объявления и инициализации переменных переменной у
присваивается значение 10, затем переменной z присваивается значение 20, далее
вычисляется выражение у + z и его результат присваивается переменной х.
140 Глава 4
// #include <math.h>
printf("%d\n", (_Bool)INFINITY); // 1 (Истина)
printf("%d\n", (_Bool)NAN); // 1 (Истина)
В этом примере мы воспользовались приведением к типу _Bool. Внутри логическо
го выражения приведение типов выполнять не нужно, т. к. оно производится авто
матически.
Следует учитывать, что оператор проверки на равенство содержит два символа =.
Указание одного символа = является логической ошибкой, т. к. этот оператор ис
пользуется для присваивания значения переменной, а не для проверки условия. Ис
пользовать оператор присваивания внутри логического выражения допускается.
поэтому компилятор не выведет сообщение об ошибке, однако программа может
выполняться некорректно. Подобную ошибку часто допускают начинающие про
граммисты. Например, в следующем примере вместо проверки равенства числу 1:
производится операция присваивания:
int х = 10;
if (Х = 11) {
puts("x == 11");
Приоритет Операторы
1 (высший) () [] . -> ++ -- (постфиксная форма)
2 ++ -- (префиксная форма)~ ! * (разыменование) & (взятие адреса)
- (унарный минус) (приведение типа) sizeof
з */%
4 + - (сложение и вычитание)
5 << >>
Tlgm: @it_boooks
142 Глава 4
Приоритет Операторы
6 < > < = >=
7 == !=
8 & (побитовое И)
9 л
10 1
11 &&
12 11
13 ?:
14 =*=/=%=+=-=>>=<<=& = л = 1=
15 (низший) , (запятая)
4. 7. Оператор ветвления if
Оператор ветвления if позволяет в зависимости от значения логического выраже
ния выполнить отдельный блок программы или, наоборот, не выполнять его. Опе
ратор имеет следующий формат:
if (<Логическое выражение>) {
<Блок, вьmолняемый если условие истинно>
[else {
<Блок, вьmолняемый если условие ложно>
}]
#include <stdio.h>
#include <locale .h>
int main(void) {
setlocale(LC_ALL, "Russian Russia.1251");
int х = О;
Tlgm: @it_boooks
else {
if (х % 2 == О)
printf("%d - четное число", х);
else
printf("%d - нечетное число", х);
printf("\n");
return О;
Как видно из примера, один оператор ветвления можно вложить в другой. Первый
оператор if проверяет отсутствие ошибки при вводе числа. Если ошибки при вводе
числа нет, то выполняется блок else. В этом блоке находится второй оператор
ветвления, который проверяет число на четность. В зависимости от условия
х % 2 == о выводится соответствующее сообщение. Если число делится на 2 без
остатка, то оператор % вернет значение о, в противном случае - число 1. Обратите
внимание на то, что оператор ветвления не содержит фигурных скобок:
if (Х % 2 == 0)
printf("%d - четное число", х);
else
printf("%d - нечетное число", х);
printf("\n");
В этом случае считается, что внутри блока содержится только одна инструкция,
поэтому последняя инструкция к блоку else не относится. Она будет выполнена
в любом случае, вне зависимости от условия. Для того чтобы это сделать наглядным,
перед инструкциями, расположенными внутри блока, добавлено одинаковое коли
чество пробелов. Если записать это следующим образом, то ничего не изменится:
if (х % 2 == О)
printf("%d - четное число", х);
else
printf("%d - нечетное число", х);
printf("\n");
Однако в дальнейшем разбираться в таком коде будет неудобно. Поэтому перед
инструкциями внутри блока всегда следует размещать одинаковый отступ. В каче
стве отступа можно использовать пробелы или символ табуляции. При использова
нии пробелов размер отступа равняется трем или четырем пробелам для блока пер-
Tlgm: @it_boooks
144 Глава 4
else {
// Инструкции
1
// Стиль 2
if (<Логическое выражение>)
// Инструкции
else {
// Инструкции
// Стиль 3
if (<Логическое выражение>)
// Инструкции
else
// Инструкции
Tlgm: @it_boooks
else {
if (<Условие 2>)
/ / Инструкции
else {
// Инструкции
Для того чтобы проверить несколько условий, эту схему можно изменить так:
if (<Условие 1>)
// <Блок 1>
else {
// <Блок else>
Если <Условие 1> истинно, то выполняется <Блок 1>, а и все остальные условия
пропускаются. Если <Условие 1> ложно, то проверяется <Условие 2>. Если <Условие
2> истинно, то выполняется <Блок 2>, а все остальные условия пропускаются. Если
<Условие 2> ложно, то точно так же проверяются остальные условия. Если все ус
ловия ложны, то выполняется <Блок else>. В качестве примера определим, какое
число от о до 2 ввел пользователь (листинг 4.4).
Tlgm: @it_boooks
146 Глава 4
#include <stdio.h>
#include <locale.h>
int rnain(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
int х = О;
printf("Bвeдитe число от О до 2: ");
fflush (stdout);
int status = scanf("%d", &х);
if (status != 1) {
puts("Bы ввели не число");
return О;
else 1
if (х == О)
puts("Bы ввели число О");
else if (х == 1)
puts("Bы ввели число 1");
else if (х == 2)
puts("Bы ввели число 2");
else {
puts("Bы ввели другое число");
printf("x = %d\n", х);
return О;
4.8. Оператор ? :
Для проверки условия вместо оператора if можно использовать оператор ? : , кото
рый имеет следующий формат:
<Переменная> = <Логическое выражение>? <Выражение если Истина>
<Выражение если Ложь>;
int func2(int х) {
printf("%d%s\n", х, " - нечетное число");
return О;
148 Гпава4
внимание на то, что значения в блоках case не могут иметь одинаковые константы..
Пример использования оператора switch приведен в листинге 4.5.
#include <stdio.h>
#include <locale.h>
int rnain(void) {
setlocale(LC_ALL, "Russian Russia.1251");
int os = О;
printf("Kaкoй операционной системой вы nользуетесь?\n\
1 - Windows XP\n\
2 - Windows 8\n\
3 - Windows 10\n\
4 - Другая\n\n\
Введите число, соответствующее ответу: ");
fflush(stdout);
int ·status = scanf("%d", &os);
if (status != 1) {
puts("Bы ввели не число");
return О;
else {
switch (os) {
case 1:
puts("Bы выбрали - Windows ХР");
break;
case 2:
puts("Вы выбрали - Windows 8");
break;
case 3:
puts("Bы выбрали - Windows 10");
break;
case 4:
puts("Bы выбрали - Другая");
break;
default:
puts("Mы не смогли определить систему");
return О;
Как видно из примера, в конце каждого блока case указан оператор break. Этот
оператор позволяет досрочно выйти из оператора выбора switch. Если не указать
Tlgm: @it_boooks
linclude <stdio.h>
int main(void) {
enurn Color color GREEN;
switch (color) {
case RED:
puts("RED");
break;
case BLUE:
puts("BLUE");
break;
case GREEN:
puts("GREEN");
break;
case BLACK:
puts("BLACK");
return О;
150 Глава4
printf("%d\n", 1);
printf("%d\n", 2);
// . . .
printf("%d\n", 100);
С помощью циклов то же действие можно выполнить одной строкой кода:
for (int i = 1; i <= 100; ++i) printf("%d\n", i);
Цикл for используется для выполнения инструкций определенное число раз. Цик..1
имеет следующий формат:
for (<Начальное значение>; <Условие>; <Приращение>) {
<Инструкции>
НА ЗАМЕТКУ
При использовании старых компиляторов все локальные переменные должны быть
объявлены в самом начале функции.
Цикл выполняется до тех пор, пока <Условие> не вернет ложь. Если это не произой
дет, то цикл будет бесконечным. Логическое выражение, указанное в параметре
<Условие>, вычисляется на каждой итерации. Поэтому если внутри логического вы
ражения производятся какие-либо вычисления и значение не изменяется внутри
цикла, то вычисление следует вынести в параметр <Начальное значение>. В этом
случае вычисление указывается после присваивания значения переменной
счетчику через запятую. Пример:
for (int i = 1, j = 10 + 30; i <= j; ++i) {
printf("%d\n", i);
else {
break; // Выходим из цикла
Tlgm: @it_boooks
152 Глава4
ВНИМАНИЕ/
Если <Приращение> не указано, то цикл будет бесконечным.
-4. Проверяется условие: если оно истинно, происходит переход к пункту 2, а если
нет - выполнение цикла завершается.
Выведем все числа от 1 до 100, используя цикл do...while:
.:.nt i = 1; // <Начальное значение>
do {
printf("%d\n", i); // <Инструкции>
++i; // <Приращение>
while (i <= 100); // <Условие>
ВНИМАНИЕ!
Если <Приращение> не указано, то цикл будет бесконечным.
154 Глава4
#include <stdio.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.i251");
int х = О, surn = О, status = О;
рuts("Введите число О для получения результата");
for ( ; ; ) {
printf("Введите число: ");
fflush(stdout);
fflush(stdin);
status = scanf("%d", &х);
if (status != 1) {
puts("Bы ввели не число");
// Сбрасываем флаг ОПП1бки и очищаем буфер
if (feof(stdin) 11 ferror(stdin)) clearerr(stdin);
int ch = О;
while ( (ch = getchar()) != '\n' && ch != EOF);
continue;
if ( ! х) break;
surn += х;
BLOCK_END: ;
Tlgm: @it_boooks
ГЛАВА 5
Числа
В языке С практически все элементарные типы данных (_вооl, char, int, float ■
douЫe) являются числовыми. Тип _вооl может содержать только значения l и :.
которые соответствуют значениям истина и ложь. Тип char содержит код символа.
а не сам символ. Поэтому значения этих типов можно использовать в одном выра
жении вместе со значениями, имеющими типы int, Поаt и douЫe. Пример:
Bool а = 1;
char ch = 'w' ; // 119
printf("%d\n", а + ch + 10); // 130
Для хранения целых чисел предназначен тип int. Диапазон значений зависит от
компилятора. Минимальный диапазон- от -32 768 до 32 767. На практике- ди;r
пазон от-2 147 483 648 до 2 147 483 647(занимает4байта).
С помощью ключевых слов short, long и long long можно указать точный размер
типа int. При использовании этих ключевых слов тип int подразумевается по
умолчанию, поэтому его можно не указывать. Тип short занимает 2 байта (диап;r
зон от -32 768 до 32 767), тип long - 4 байта (диапазон от -2 147 483 Е�=
до 2 147 483 647), а тип long long занимает 8 байт (диапазон значений от
-9 223 372 036 854 775 808 ДО 9 223 372 036 854 775 807).
По умолчанию целочисленные типы являются знаковыми (signed). Знак числа хра
нится в старшем бите: о соответствует положительному числу, а 1 - отрицатель
ному. С помощью ключевого слова unsigned можно указать, что число являете•
только положительным. Тип unsigned char может содержать числа в диапазоне от :
ДО 255, ТИП unsigned short - ОТ О ДО 65 535, ТИП unsigned long- ОТ О ,10
4 294 967 295, а тип unsigned long long- от О до 18 446 744 073 709 551 615.
При преобразовании значения из типа signed в тип unsigned следует учитывать, что
знаковый бит (если число отрицательное, то бит содержит значение 1) может стап.
причиной очень больших чисел, т. к. старший бит у типа unsigned не содержит при
знака знака:
int х = -1;
printf ("%u\n", (unsigned int)х); // 4294967295
Tlgm: @it_boooks
157
158 Глава 5
Числа 159
160 Глава 5
#include <stdio.h>
#define -USE-МАТН-DEFINES
#include <rnath.h>
int main(void) {
printf("%.5f\n", М PI); // 3.14159
printf("%.5f\n", М Е); // 2.71828
return О;
#include <stdlib.h>
int abs(int х);
long labs(long х);
lung long llabs(long long х);
int64 abs64( int64 х);
#include <rnath.h>
int abs(int х) ;
long labs(long х);
float fabsf(float х);
douЫe fabs(douЫe х);
long douЫe fabsl(long douЫe х);
Tlgm: @it_boooks
161
Пример:
printf("%d\n", abs(-1)); // 1
162 Глава 5
Объявления структур:
typedef struct div t
int quot;
int rern;
div t;
typedef struct ldiv t
long quot;
long rern;
ldiv t;
typedef struct
long long quot;
long long rern;
l'ldiv t;
Пример использования функции:
div_t d;
d = div(lЗ, 2);
Tlgm: @it_boooks
Числа 163
printf("%d\n", d.quot); · // 6 == 13 /2
printf("%d\n", d.rern); //1==13 %2
□ imaxdiv( J -возвращает структуру из двух полей: quot (результат целочислен
ного деления х / у), rern ( остаток от деления х % у). Прототип функции:
#include <inttypes.h>
imaxdiv_t imaxdiv(intmax_t х, intmax_t у);
typedef long long· intmax_t;
Пример:
printf("%d\n", _max(l0, 3)); //10
□ fmax( J - максимальное значение. Прототипы функции:
#include <math.h>
float fmaxf(float а, float Ь);
douЬle fmax(douЬle а, douЬle Ь);
long douЬle fmaxl(long douЬle а, long douЬle Ь);
Пример:
printf("%.0f\n", fmax(l0, 3)); //10
Пример:
printf("%d\n", _min(l0, 3)); // 3
□ fmin( J - минимальное значение. Прототипы функции:
#include <math.h>
float fminf(float а, float Ь);
dot./Ьle fmin(douЫe а, douЬle Ь);
long douЬle fminl(long douЬle а, long douЬle Ь);
Tlgm: @it_boooks
164 Глава5
Пример:
printf("%.0f\n", fmin(l0, 3)); // 3
Числа 165
166 Глввв5
167
168 Глава 5
Если в строке нет числа, то возвращается число о. Если число выходит за диапа
зон значений для типа, то значение будет соответствовать минимальноМ)
(LONG-MIN или о) или максимальному (LONG-МАХ или ULONG-МАХ) значению дл1
типа, а переменной errno присваивается значение ERANGE. Пример:
printf("%ld\n", strtol("str", NULL, О)); // О
printf("%ld\n", strtol("2147483649", NULL, О));
// 2147483647 (соответствует LONG_МAX)
printf("%d\n", errno); // 34
// #include <rnath.h> или #include <errno.h>
if (errno == ERANGE) {
puts("Bыxoд за диапазон значений");
169
170 Глава5
Если в строке нет числа, то возвращается число о. Если число выходит за диапа
зон значений для типа, то значение будет соответствовать минимальному или
максимальному значению для типа, а переменной errno присваивается значение
ERANGE. Пример:
printf("%I64d\n", _strtoi64("str", NULL, О)); // О
printf("%I64d\n", _strtoi64("9223372036854775809", NULL, О));
// 9223372036854775807
printf("%d\n", errno); // 34
// #include <math.h> иm1 #include <errno .h>
if (errno == ERANGE) {
puts("Bыxoд за диапазон значений");
171
// #include <stdint.h>
printf("%I64d\n", INТМAX_MIN); // -9223372036854775808
printf("%I64d\n", INТМАХ_МАХ); // 9223372036854775807
printf("%I64u\n", UINТМАХ_МАХ); // 18446744073709551615
printf("%I64d\n", strtoirnax("str", NULL, О)); // О
printf("%I64d\n", strtoirnax("9223372036854775809", NULL, О));
// 9223372036854775807 (соответствует INТМАХ_МАХ)
printf("%d\n", errno); // 34
// #include <rnath.h> или #include <errno.h>
if (errno == ERANGE) (
рuts("Выход за диапазон значений");
172 Глава5
Числа 173
char *р = NULL;
_mingw_printf("%.2Lf\n", strtold(" \t\n 25", NULL)); // 25.00
_rningw_printf("%.2Lf\n", strtold("2.5", NULL)); // 2.50
_mingw_printf("%.2Lf\n", strtold("5.lstr", NULL)); // 5.10
_rningw_printf("%Le\n", strtold("14.5e5s10", &р)); // 1.450000е+006
printf("%s", р); // s10
Если в строке нет числа, то возвращается о. Если число выходит за диапазон
значений для типа, то возвращается значение -HUGE_VALL или НUGE_VALL, а пере
менной errno присваивается значение ERANGE.
Все рассмотренные в этом разделе функции позволяют преобразовать всю С-строку
• число. Если необходимо преобразовать отдельный символ (представляющий чис
.ю) в соответствующее целое число от о до 9, то можно воспользоваться следую
щим кодом:
char ch = '8';
int n = ch - 'О'; // Эквивалентно int n = 56 - 48;
printf("%d\n", n); // 8
В переменной типа char хранится код символа, а не сам символ. Символы от 'о' до
'9' в кодировке ASCII имеют коды от 48 до 57 с.оответственно. Следовательно, что
бы получить целое число от о до 9, достаточно вычесть из текущего символа код
символа 'о'. В качестве еще одного примера сохраним все цифры, встречающиеся
в С-строке, в целочисленном массиве (листинг 5.2).
linclude <stdio.h>
int main(void) {
char str[) = "аЬс0123456789";
int arr(ARR_SIZE) = {О}, index � О;
for (char *р = str; •р; ++р) { // Перебираем строку
if (*р >= '0' && •р <= '9')
arr[index] = *р - '0';
++index;
if (index >= ARR_SIZE) break;
return О;
174 Глава5
#include <stdio.h>
int sscanf(const char *str, const char *forrnat, ...);
int _snscanf(const char *str, size_t rnaxCount, const char *forrnat, ...);
Функция sscanf() не производит никакой проверки длины строки, что может при
вести к ошибке. Обязательно указывайте ширину при использовании спецификато
ра %s (например, %255s). Кроме того, следует учитывать, что функция не проверяет
возможность выхода значения за диапазон значений для типа, что может привести
к неправильному результату. Функция никак об этом вас не предупредит, поэтому
лучше использовать функции, рассмотренные в начале этого раздела.
175
176 Глава 5
177
178 Глвва5
Пример:
srand( (unsigned int)time(NULL) );
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
int main(void) {
char str[B0] = 11 11.,
srand( (unsigned int)time(NULL) );
passw_generator(str, В);
printf("%s\n", str); // Выведет примерно JМhFAE29
passw_generator(str, В);
printf("%s\n", str); // 9tpgmUts
passw_generator(str, 10);
printf("%s\n", str); // MYGeIXXx2g
return О;
Числа 179
*pstr = '\0';
// #include <math.h>
printf("%f\n", INFINITY); 11 1.#INFOO
printf("%f\n", -INFINITY); 11 -1.#INFOO
printf("%f\n", NAN); 11 1.#QNANO
printf("%f\n", NAN + 1); 11 1.#QNANO
printf("%f\n", О * INFINITY); 11 1.#QNANO
printf("%f\n", INFINITY/ INFINITY); 11 1.#QNANO
printf("%f\n", -INFINITY + INFINITY); 11 1.#QNANO
Для проверки соответствия этим значениям нельзя использовать логические опера
торы. Например, значение NAN не равно даже самому себе:
printf("%d\n", NAN == NAN); 11 о
Для того чтобы проверить значения, нужно воспользоваться следующими функ
циями:
□ _finite(J и _finitef(J - возвращают ненулевое число, если значение не равно
плюс или минус бесконечность или значению NAN, и о - в противном случае.
Прототипы функций:
#include <math.h>
int _finitef(float х); // Только в версии хб4 (MinGW-W64)
int finite(douЫe х);
Примеры:
printf("%d\n", finite(l0.5)); // 1
printf("%d\n", finite(NAN)); 11 о
printf("%d\n", finite(INFINITY)); 11 о
Tlgm: @it_boooks
180 Глава 5
□ _isnan <), _isnanf <) и макрос isnan() - возвращают ненулевое число, если зна
чение равно NAN, и о - в противном случае. Прототипы функций:
#include <rnath.h>
int _isnanf(float х); // Только в версии хб4 (MinGW-W64)
int _isnan(douЫe х);
Примеры:
printf("%d\n", isnan(l0.5)); // о
printf("%d\n", isnan(INFINITY)); // о
printf("%d\n", isnan(NAN)); /! 1
printf("%d\n", isnan(l0.5)); // о
printf("%d\n", isnan(INFINITY)); // о
printf("%d\n", isnan(NAN)); // 1
□ isinf <) - возвращает ненулевое число, если значение равно плюс или минус
бесконечность, и о - в противном случае. Прототип функции:
#include <rnath.h>
#define isinf(x) (fpclassify(x) == FP_INFINITE)
Примеры:
printf("%d\n", isinf(l0.5)); // о
printf("%d\n", isinf(NAN)); // о
printf("%d\n", isinf(INFINITY)); // 1
printf("%d\n", isinf(-INFINITY)); !/ 1
Tlgm: @it_boooks
ГЛАВА 6
Массивы
182 Главаl
llессивы 183
184 Главаl
Пример:
int arr[3] = {10, 20, 30};
printf("%d\n", (int) sizeof(arr)); // 12
printf("%d\n", (int) sizeof(int) * 3); // 12
llассивы 185
arr(2] = х - arr[2];
printf("%ld ", х); // 32
printf("%ld ", arr[2]); // 2
puts("---------------�---");
// ВЫБодим значения в обратном порядке
for (int i = ARR_SIZE - 1; i >= О; --i)
printf("%d\n", arr[i]);
186 Глава 5
Последняя инструкция может показаться странной. Однако если учесть, что выра
жение 1[arr] воспринимается компилятором как* (1 + arr), то все встанет на свои
места.
При указании индекса внутри квадратных скобок каждый раз производится вычис
ление адреса соответствующего элемента массива. Для того чтобы сделать процесс
доступа к элементу массива более эффективным, объявляют указатель и присваи
вают ему адрес первого элемента. Далее для доступа к элементу просто перемеща
ют указатель на соответствующий элемент. Пример объявления указателя и при
сваивания ему адреса первого элемента массива:
int *р = NULL;
int arr[З] = 11, 2, З};
р = arr;
printf("%d\n", *р); // 1
printf("%d\n", *(р + 1)); // 2
printf("%d\n", *(р + 2)); // 3
Обратите внимание на то, что перед названием массива не указывается оператор &,
т. к. название переменной содержит адрес первого элемента. Если использовать
оператор &, то необходимо дополнительно указать индекс внутри квадратных
скобок:
р = &arr(0]; // Эквивалентно: р = arr;
Таким же образом можно присвоить указателю адрес произвольного элемента мас
сива, например третьего:
Tlgm: @it_boooks
"8ссивы 187
int *р = NULL;
int arr[3] = {1, 2, 3};
р = &arr[2]; // Указатель на третий элемент массива
printf("%d\n", *р); // 3
,Аля того чтобы получить значение элемента, на который ссылается указатель, не-
8бходимо произвести операцию разыменования. Для этого перед названием указа
'RЛЯ добавляется оператор *. Пример:
printf("%d\n", *р);
Указатели часто используются для перебора элементов массива. В качестве приме
" создадим массив из трех элементов, а затем выведем значения с помощью цикла
for:
tdefine ARR SIZE 3
int *р = NULL, arr[ARR_SIZE] = {1, 2, 3};
// Устанавливаем указатель на первый элемент массива
р = arr; // Оператор & не указывается!!!
for (int i = О; i < ARR_SIZE; ++i)
printf("%d\n", *р);
++р; // Перемещаем указатель на следукхций элемент
188 Главаf
то вначале будет получен доступ к элементу массива и выведено его текущее зна
чение, а затем произведено увеличение знач�ния элемента массива. Перемещение
указателя на следующий элемент не выполняется.
Указатель можно использовать в качестве переменной-счетчика в цикле for. В этом
случае начальным значением будет адрес первого элемента массива, а условием
продолжения - адрес меньше адреса первого элемента плюс количество элемен
тов. Приращение осуществляется аналогично обычному, только вместо классиче
ской арифметики применяется адресная арифметика. Пример:
#define ARR SIZE 3
int arr[ARR_SIZE] = {1, 2, 3};
for (int *р = arr, *р2 = arr + ARR SIZE; р < р2; ++р) {
printf("%d\n", *р);
Вывести все значения массива с помощью цикла while и указателя можно так:
#define ARR SIZE 3
int *р = NULL, i = ARR_SIZE, arr[ARR_SIZE] = {1, 2, 3};
р = arr;
while (i-- > О) {
printf("%d\n", *р++);
р = arr;
В указателе можно сохранить адрес составного литерш�а, который удобно переда
вать в качестве параметра в функцию. Составной литерал состоит из круглых ско
бок, внутри которых указываются тип данных и количество элементов внутри
квадратных скобок. После круглых скобок указываются фигурные скобки, внутри
которых через запятую перечисляются значения:
const long *р = (long [3]) { 1, 2, 3};
printf("%ld " , *р); // 1
printf("%ld * (р + 1)); // 2
printf("%ld " , *(р + 2)); // 3
Tlgm: @it_boooks
llассивы 189
190 Глава 5
printf("\n");
Получить или задать значение элемента можно, указав два индекса (не забывайте,
что нумерация начинается с нуля):
int arr[2][4] = {
{1, 2, 3, 4},
(5, 6, 7, 8)
);
Tlgm: @it_boooks
llассивы 191
printf("%d\n", arr[O][О]); // 1
printf("%d\n", arr[l][O]); // 5
printf("%d\n", arr[l] [З]); // 8
Для того чтобы вывести все значения массива, необходимо использовать вложен
ные циклы. В качестве примера пронумеруем все элементы массива, а затем выве
дем все значения:
const int ROWS = 4; // Количество строк
const int COLUМNS = 5; // Количество столбцов
int i = О, j = О, n = 1, arr[ROWS][COLUМNS];
// Нумеруем все элементы массива
for (i = О; i < ROWS; ++i) {
for (j = О; j < COLUМNS; ++j)
arr(i](j] = n;
++n;
1
// Выводим значения
for (i = О; i < ROWS; ++i) {
for (j = О; j < COLUМNS; ++j)
printf("%3d", arr[i] [j]);
printf("\n");
Результат:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
192 Глава 1
Для того чтобы такая инициализация указателя стала корректной, нужно объявип.
указатель следующим образом:
int (*р)[COLUМNS] = arr;
printf("%d\n", р[О][О]); // 1
printf("%d\n", р[З][4]); // 20
Можно также объявить указатель на двумерный массив, в этом случае при инициа
лизации перед названием двумерного массива нужно указать оператор &:
int (*р) [ROWS][COLUМNS] = &arr;
printf("%d\n", (*р)[О][О]); // 1
printf("%d\n", (*р)[3][4]); // 20
или так:
*(*(р + i) + j) = n++;
или так:
printf("%3d", *(*(р + i) + j));
free(р);
р = NULL; // Обнуляем указатель
Tlgm: @it_boooks
Массивы 193
6.9. Поиск
минимального и максимального значений
Дrlя того чтобы в массиве найти минимальное значение, следует присвоить пере
менной min значение первого элемента массива, а затем сравнить с остальными
1Лементами. Если значение текущего элемента меньше значения переменной min,
то присваиваем значение текущего элемента переменной пiin.
Дrlя того чтобы найти максимальное значение, следует присвоить переменной max
111ачение первого элемента массива, а затем сравнить с остальными элементами.
Если значение текущего элемента больше значения переменной �х, то присваива
ем значение текущего элемента переменной max.
Пример поиска минимального и максимального значения приведен в листинге 6.1.
linclude <stdio.h>
L�t main(void) {
int arr[ARR_SIZE] = {2, 5, 6, 1, 3);
printf("min = %d\n", min(arr, ARR_SIZE));
printf("max = %d\n", max(arr, ARR_SIZE));
int _min = О, max = О;
min_max(arr, ARR_SIZE, &_min, &_max);
printf("min %d\n", _min);
printf("max = %d\n", _max);
min = О;
min_rnax(arr, ARR_SIZE, &_min, NULL);
printf("rnin = %d\n", _min);
max = О;
min_max(arr, ARR_SIZE, NULL, &_max);
printf("max = %d\n", _max);
return О;
194 Глава 11
return rnin;
return rnax;
В этом примере мы создали три функции: rnin(), rnax() и rnin_rnax(). Функции в пер
вом параметре принимают адрес первого элемента массива, а во втором - длиН)·
массива. Внутри функции rnin() выполняется поиск минимального значения, а
внутри функции rnax() - максимального значения. Найденные значения функции
возвращают с помощью оператора return. Как только внутри функции встречаетсt
оператор return или поток доходит до конца блока функции, управление передает
ся в место вызова функции. При этом становится доступно значение, указанное
в операторе return. Если функция ничего не возвращает, то при определении перез
именем функции указывается ключевое слово void. В этом случае оператор retuг..
использовать не нужно. Хотя его можно и указать, например, чтобы досрочно вый
ти из функции:
if ( !pArr 11 length < 1) return;
Если pArr является нулевым указателем или длина массива меньше 1, то просто вы
ходим из функции. При этом после оператора return значение не указывается.
Функция rnin_rnax() выполняет поиск и минимального значения, и максимального
значения массива одновременно. Однако из функции с помощью оператора returr.
мы можем вернуть только одно значение, поэтому вместо возврата значени.1
в третьем и четвертом параметрах функция принимает адреса переменных, в кото
рые будут записаны найденные значения. Для того чтобы передать в функцию
адрес переменной, перед ее именем указывается оператор&:
int _rnin = О, _rnax = О;
rnin_rnax(arr, ARR_SIZE, &_rnin, &_rnax);
Внутри функции rnin_rnax() мы проверяем наличие адреса переменной, поэтому мо
жем передать значение NULL вместо адреса, если какое-либо значение нам не нужно.
В этом примере мы хотим получить только минимальное значение:
Tlgm: @it_boooks
lllllccuвы 195
_ain = О;
ain_max(arr, ARR_SIZE, &_min, NULL);
tinclude <stdio.h>
int main(void) {
int arr[ARR_SIZE] {10, 5, 6, 1, 3};
int j = О, tmp = О, k = ARR SIZE - 2;
_Bool is_swap = О;
for (int i = О; i <= k; ++i) {
is_swap = О;
for (j = k; j >= i; --j) {
if (arr[j] > arr(j + l])
tmp = arr[j + l];
arr[j + l] = arr(j];
Tlgm: @it_boooks
196 Глава 6
arr[j] = tmp;
is_swap = 1;
return О;
#include <stdio.h>
int main(void) {
int arr[ARR_SIZE] (5, 6, 1, 10, 3);
int j = О, tmp = О;
_Bool is_swap = О;
for (int i = ARR SIZE - 1; i >= 1; --i) {
is_swap = О;
for (j = О; j < i; ++j)
if (arr[j] < arr[j + 1])
tmp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = tmp;
is_swap = 1;
if (!is_swap) break;
)
// Выводим отсортированный массив
for (int i = О; i < ARR_SIZE; ++i)
printf("%d\n", arr[i]);
return О;
llассивы 197
tinclude <stdlib.h>
void qsort(void *base, size_t numOfElements, size t sizeOfElements,
tnt (*PtFuncCompare)(const void *, const void *));
linclude <stdio.h>
linclude <stdlib.h>
:nt main(void)
int arr[ARR_SIZE] = {10, 5, б, 1, З};
qsort(arr, ARR_SIZE, sizeof(int), compare);
for (int i = О; i < ARR_SIZE; ++i) {
printf("%d\n", arr[i]);
return .О;
198 Главаl
#include <stdio.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
int arr[ARR_SIZE] = {10, 5, 6, 1, 6}, index -1;
int search_key = 6;
for (int i = О; i < ARR_SIZE; ++i)
if (arr[i] search_key)
index = i;
break;
if (index != -1) {
printf("index = %d\n", index);
llассивы 199
весь массив. Если массив состоит из 100 ООО элементов, то нужно будет сделать
100 ООО сравнений.
Поиск по отсортированному массиву производится гораздо быстрее, т. к. не нужно
каждый раз просматривать все значения массива. Наиболее часто применяется би
нарный поиск (листинг 6.6), при котором массив делится пополам и производится
сравнение значения элемента, расположенного в середине массива, с искомым зна
чением. Если искомое значение меньше значения элемента, то пополам делится
первая половина массива, а если больше, то пополам делится вторая половина. Да
хе таким же образом производится деление оставшейся части массива. Поиск за
анчивается, когда будет найдено первое совпадение или начальный индекс станет
больше конечного индекса. Таким образом, на каждой итерации отбрасывается
половина оставшихся элементов. Поиск значения, которое расположено в конце
массива, состоящего из 100 ООО элементов, будет произведен всего за 17 шагов.
Однако если поиск производится один раз, то затраты на сортировку сведут на нет
все преимущества бинарного поиска. В этом случае прямой перебор элементов
может стать эффективнее.
linclude <stdio.h>
linclude <stdlib.h>
linclude <locale.h>
int main(void)
setlocale(LC_ALL, "Russian_Russia.1251");
int arr[ARR_SIZE] = {10, 5, 6, 1, 3};
// Сортируем по возрастанию
qsort(arr, ARR_SIZE, sizeof(int), compare);
// Производим поиск
int index = search(б, arr, ARR_SIZE);
if (index != -1)
printf("index = %d\n", index);
200 Главаl
return -1;
Функция должна возвращать отрицательное значение, если argl меньше arg2, по
ложительное значение, если argl больше arg2, или о, если значения равны. Внутри
функции необходимо выполнить приведение указателя void * к определенному ти
пу. Если значение не найдено, то возвращается нулевой указатель, в противном
случае указатель ссылается на найденный элемент. Пример использования функции
bsearch() приведен в листинге 6. 7.
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
Массивы 201
int main(void)
setlocale(LC_ALL, "Russian_Russia.1251");
int arr[ARR_SIZE] = (10, 5, 6, 1, 3}, search_key = 6;
// Сортируем по возрастанию
qsort(arr, ARR_SIZE, sizeof(int), compare);
// Производим поиск
int *р (int *)bsearch(&search_key, arr, ARR_SIZE,
sizeof(int), compare);
if (р)
int index = {int) (р - arr);
printf("index = %d\n", index);
202 Главаl
203
linclude <string.h>
errno_t rnemmove_s(void *dst, size_t dstSize, const void *src,
size_t maxCount);
Функция rneппnove_s() копирует первые maxcount байтов из массива src в массив
dst. В параметре dstSize указывается размер массива dst в байтах. Если копиро
вание прошло успешно. функция возвращает значение о. Пример:
ldefine ARR SIZE 5
int arr[ARR_SIZE] = {1, 2, 3, 4, 5), *р = О;
р = arr + 2;
memmove_s(p, sizeof arr, arr, 3 * sizeof(int));
for (int i = О; i < ARR_SIZE; ++i) {
printf("%d ", arr[i]);
1 // 1 2 1 2 3
printf("\n");
204 Главаl
int main(void) {
int arr[ARR_SIZE] = {1, 2, 3, 4, 5};
print_array(arr, ARR_SIZE); // 1 2 3 4 5
reverse(arr, ARR_SIZE);
print_array(arr, ARR_SIZE); // 5 4 3 2 1
reverse(arr, ARR_SIZE);
print_array(arr, ARR_SIZE); // 1 2 3 4 5
return О;
Массивы 205
printf("\n");
ГЛАВА 7
Символы и С-строки
В языке С доступны два типа строк: С-строка и L-строка. С-строка является мас
сивом однобайтовых символов (тип char), последний элемент которого содержит
нулевой символ ('\О'). Обратите внимание на то, что нулевой символ (нулевой
байт) не имеет никакого отношения к символу 'о'. Коды этих символов разные.
L-строка является массивом широких символов (тип wchar_t), последний элемеtп
которого содержит нулевой символ.
[] \t - горизонтальная табуляция;
[] \v - вертикальная табуляция;
[] \а - звуковой сигнал;
[] \Ь - возврат на один символ;
[] \f - перевод формата;
[] \' - апостроф;
□ \" - кавычка;
□ \? - знак вопроса;
□ \ \ - обратная косая черта;
□ \N - восьмеричное значение N;
□ \xN - шестнадцатеричное значение N.
Пример указания восьмеричного и шестнадцатеричного значений:
char chl, ch2;
chl = '\167'; // Восьмеричное значение
ch2 = '\х77'; // Шестнадцатеричное значение
printf("%c\n", chl); // w
printf("%c\n", ch2); // w
По умолчанию тип char является знаковым и позволяет хранить диапазон значений
or -128 до 127. Если перед типом указано ключевое слово unsigned, то диапазон
будет от о до 255. Когда переменной присваивается символ внутри апострофов,
он автоматически преобразхется в соответствующий целочисленный код. Пример
вывода размера и диапазона значений:
// Выводим размер в байтах
printf("%d\n", (int) sizeof(char)); // 1
// #include <limits.h>
printf("%d\n", CНAR_HIN); // -128
printf("%d\n", СНАR_МАХ); // 127
printf("%d\n", UСНАR_МАХ); // 255
printf("%d\n", CНAR_BIT); // 8
Тип char занимает в памяти 1 байт (8 бит). Если тип является знаковым, то старший
бит содержит признак знака: о соответствует положительному числу, а 1 - отрица
тельному. Если тип является беззнаковым, то признак знака не используется. Это
следует учитывать при преобразовании знакового типа в беззнаковый, т. к. старший
бит станет причиной больших значений:
char chl = -1;
unsigned char ch2 = (unsigned char)chl;
printf("%u\n", ch2); // 255
Символы, имеющие код меньше 120 (занимают 7 бит), соответствуют кодиров
ке ASCII. Коды этих символов одинаковы практически во всех однобайтовых коди
ровках. В состав кодировки ASCIJ входят цифры, буквы латинского алфавита,
Tlgm: @it_boooks
208 ГлаваJI
Символ dec oct hex Символ dec oct hex Символ dec oct he•
\0 о о о ; 59 73 зь л
94 136 5е
\а 7 7 7 < 60 74 Зс 95 137 5f
\Ь 8 10 8 = 61 75 3d 96 140 60
\t 9 11 9 > 62 76 3е а 97 141 61
\n 10 12 а ? 63 77 3f ь 98
·-
142 62
\v 11 13 ь @ 64 100 40 с 99 143 63
\f 12 14 с А 65 101 41 d 100 144 64
\r 13 15 d в 66 102 42 е 101
----- --- --
145 65
пробел 32 40 20 с 67 103 43 f 102 146 66
! 33 41 21 D 68 104 44 g 103 147 67
" 34 42 22 Е 69 105 45 h 104 150 68
# 35 43 23 F 70 106 46 i 105 151 69
$ 36 44 24 G 71 107 47 j 106 152 6а
% 37 45 25 н 72 110 48 k 107 153 6Ь
& 38 46 26 I 73 111 49 1 108 154 6с
' 39 47 27 J 74 112 4а m 109 155
---f-
'ср866
_____
ср1251
,__ --т--
l- oct
---·--
Символ
unsigned signed oct hex unsigned signed hex
-+ � _
� ----1i ---с9-
�-
210 Глава 1
212 Глава1
else {
puts("He удалось настроить локаль");
Tlgm: @it_boooks
Для того чтобы использовать другую кодировку, нужно указать ее название после
точки, например ".1251" или "Russian _Russia.1251":
char *pLocale = setlocale(LC_ALL, "Russian_Russia.1251");
if (pLocale) {
printf("%s\n", pLocale); // Russian Russia.1251
else {
puts("He удалось настроить локаль");
Вместо названия можно указать пустую строку. В этом случае будет использовать
ся локаль, настроенная в системе:
char *pLocale = setlocale(LC_ALL, "");
if (pLocale) {
printf("%s\n", pLocale); // Russian_Russia.1251
После настройки локали многие функции будут работать не так, �ак раньше. На
пример, от настроек локали зависит вывод функции printf():
printf("%.2f\n", 2.5); // 2.50
setlocale(LC_NUMERIC, "dutch");
printf("%.2f\n", 2.5); // 2,50
Некоторые функции позволяют указать локаль в качестве параметра, например,
функция _printf _1 ():
int _printf_l(const char *format, locale t locale, ...);
Tlgm: @it_boooks
214 Глава 1
#include <stdio.h>
#include <locale.h>
int rnain(void) (
char *pLocale = setlocale(LC ALL, NULL);
if (pLocale) {
printf("%s\n", pLocale); // С
216 Главаl
printf("%s\n", str);
// АБВГдЕ�ИЙКЛМНОПРСТУФХЦЧ!ШЦЬЬIЬЭЮЯ
Tlgm: @it_boooks
printf("%s\n", str);
// абвгдеёжзийклмнопрстуфхцЧIШЦ'ЬЫЬэюя
□ _strupr() - заменяет все буквы в С-строке str соответствующими прописными
буквами. Функция возвращает указатель на строку str. Прототип функции:
#include <string.h>
char *_strupr(char *str);
Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
char str [] = "абвгдеёжэийклмнопрстуфхцчllШfЬl:,IЬэюя";
_strupr(str);
printf("%s\n", str);
// АБВГДЕtжзИЙЮIМНОПРСТУФХЦЧ]lПЦЬЫЬЗОЯ
Вместо функции _strupr() лучше использовать функцию _strupr_s () . Прототип
функции:
#include <string.h>
errno_t _strupr_s(char *str, size_t strSize);
218 Глава!
#include <string.h>
char *_strlwr(char *str);
Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
char str [] = "АБВГдЕЁЖЗИЙКЛМНОПРСТУФХЦЧIIШ.{ЬЬlЬЭЮЯ";
_strlwr(str);
printf("%s\n", str);
// абвгдеёжзийклмнопрстуфхцчnnцъыьэюя
Вместо функции _strlwr() лучше использовать функцию _strlwr_s(). ПротоТ8
функции:
#include <string.h>
errno_t _strlwr_s(char *str, size_t strSize);
В параметре strSize указывается размер строки. Функция возвращает зна11&
ние о, если операция успешно выполнена, или код ошибки. Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
char str[40] = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧIШЦЪЫЬЭЮЯ";
_strlwr_s(str, 40);
printf("%s\n", str);
// абвгдеёжзийклмнопрстуфхцч11ПЦЪЫЬэюя
220 Глава 7
printf("%d\n", isalnurn('8')); // 4
printf("%d\n", isalnurn('\t')); // о
printf("9,d\n", isalnurn((unsigned char)'б')); // 258
□ islower() и _islower_l() - возвращают ненулевое значение, если символ явля
ется буквой в нижнем регистре, в противном случае - о. Прототипы функций:
#incl11de <ctype.h>
int islower(int ch);
int islower l(int ch, locale t locale);
Для русских букв необходимо настроить локаль или указать ее в параметре
locale. Пример:
setlocale(LC__ALL, "Russian Russia.1251");
printf("%d\n", islower('w')); // 2
printf("%d\n", islower('8')); // о
printf("%d\n", islower('\t')); // о
printf("%d\n", islower((unsigned char)'б')); // 2
printf("%d\n", islower((unsigned char)'Б')); // о
□ isupper( Jи _isupper_1( J - возвращают ненулевое значение, если символ явля
ется буквой в верхнем регистре, в противном случае - о. Прототипы функций:
#include <ctype.h>
int isupper(int ch);
int isupper_l(int ch, locale t locale);
Для русских букв необходимо настроить локаль или указать ее в параметре
locale. Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
printf("%d\n", isupper('W')); // 1
printf("%d\n", isupper('8')); // о
printf("%d\n", isupper('\t')); // о
printf("%d\n", isupper((unsigned char)'б')); // о
printf("%d\n", isupper((unsigned char)'Б')); // 1
222 Глава1
#include <ctype.h>
int isЬlank(int ch);
Для русских букв необходимо настроить локаль. Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
printf("%d\n", isЫank('8')); // О
printf("%d\n", isЬlank('\t')); // 1
printf("%d\n", isЫank(' ')); //1
Обратите внимание на то, что перед русскими буквами указывается операция при,
ведения к типу unsigned char. Если это не сделать, то производится попытка прео&
разования к типу unsigned int. Поскольку коды русских букв по умолчанию име!ОI
отрицательные значения, знаковый бит станет причиной большого значения, кото
рое выходит за рамки диапазона значений для типа char. Так, для буквы "б" зна�
ние будет равно 4294967265. Пример:
printf("%u\n", (unsigned intNunsigned char)'б'); //225
printf("%u\n", (unsigned int)'б'); //4294 967265
224 Главаl
Строку можно присвоить указателю, но в этом случае строка будет доступна тот.
ко для чте11ия. Попытка изменить какой-либо символ внутри строки приведе1
к ошибке при выполнении:
const char *р = "string";
printf("%s\n", р); // string
или так:
char *str[] = { "Stringl", "String2", "String3"};
printf("�s\n", str [О]); // Stringl
printf("�s\n", str [ 1]); // String2
printf("i\s\n", str[2]); // String3
226 Глава 7
char *р = str;
printf("%d\n", (int) sizeof(p));
// Значение в проекте Test64c: 8
Обратите внимание: длина строки и размер символьного массива - это разные
вещи. Длина строки - это количество символов с начала массива до первого ну.,е
вого символа. Размер сшwвольного массива - это общее количество символов, дос
тупное под строку. Мы можем получить размер символьного массива с помощЫС'
оператора sizeof, указав имя переменной, но если присвоить ее адрес указателю. тt'
мы получим размер указателя, а не символьного массива.
puts("-------------------");
// Выводим символы в обратном порядке
for (int i = len - 1; i >= О; --i) {
printf("%c\n", str[i]);
228 Глава 7
Если количество символов в строке source меньше числа count, то строка dest
· будет дополнена нулевыми символами, а если больше или равно, то копируются
только count символов, при этом нулевой символ автоматически не вставляется.
Пример копирования шести символов:
char str[7];
strncpy(str, "String", 6);
str[б] = '\О'; // Нулевой символ автоматически не вставляется
printf("%s\n", str); // String
Вместо функции strncpy() лучше использовать функцию strncpy_s(). Прототип
функции:
*include <string.h>
errno_t strncpy_s(char *dest, size_t sizeinBytes,
const char *source, size_t maxCount);
Функция strncpy_s() копирует первые maxCount символов из строки source
в строку dest и вставляет нулевой символ. В параметре sizeinВytes указывается
максимальное количество элементов массива dest. Пример:
tdefine SIZE 7
char str[SIZE);
strncpy_s(str, SIZE, "String", 5);
prlntf("%s\n", str); // Strin
С] strcat() - копирует символы нз С-строки source в конец С-строки dest и
вставляет нулевой символ. В качестве значения функция возвращает указатель
на строку dest. Если строка dest имеет недостаточный размер, то произойдет
переполнение буфера. Прототип функции:
*include <string.h>
char *strcat(char *dest, const char *source);
Пример использования функции:
char str[20] = "ст";
strcat(str, "ро");
strcat(str, "ка");
printf("%s\n", str); // строка
Обратите внимание на то, что перед копированием в строке dest обязательно
должен быть нулевой символ. Локальные переменные автоматически не ини
циализируются, поэтому этот код приведет к ошибке:
char str[20]; // Локальная переменная
strcat(str, "строка."); // Ошибка, нет нулевого сиывола!
Для того чтобы избавиться от ошибки, нужно произвести инициализацию стро
ки нулями следующим образом:
char str[20J = {О}; // инициализация нулями
strcat(str, "строка"); // Все нормально
printf("%s\n", str); // строка
Tlgm: @it_boooks
230 Глава 7
232 Глава 7
Практически все функции без суффикса _s, которые мы рассмотрели в этом разде
ле, в Visual С выводят предупреждающее сообщение warning С4996, т. к. по умо.1-
чанию функции не производят никакой проверки корректности данных. В это-.
случае возможно переполнение буфера. Забота о корректности данных полностью
лежит на плечах программиста. Если вы уверены в своих действиях, то можно по
давить вывод предупреждающих сообщений. Сделать это можно двумя способами:
□ определить макрос с названием _CRT_SECURE_No_WARNINGS в самом начале про
граммы, перед подключением заголовочных файлов. Пример:
#define -CRT-SECURE-NO-WARNINGS
#include <stdio.h>
#include <string.h>
if (р) (
printf("Индeкc: %d\n", (int)(р - str));
1 // Индекс: 4
□ strrchr <) - ищет в С-строке str последнее вхождение символа ch. В качестве
значения функция возвращает указатель на символ или нулевой указатель, если
символ не найден. Прототип функции:
#include <string.h>
char *strrchr(const char *str, int ch);
Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
char str [] = "строка строка", *р = NULL;
р = strrchr(str, 'к');
if (р) (
printf("Индекс: %d\n", (int)(р - str));
1 // Индекс: 11
□ mernchr( J - ищет в строке str первое. вхождение символа ch. Максимально про
сматривается rnaxcount символов. В качестве значения функция возвращает ука
затель на первый найденный символ в строке str или нулевой указатель, если
символ не найден. Прототип функции:
#include <string.h>
void *memchr(const void *str, int ch, size_t rnaxCount);
Пример:
char str[J = "строка строка", *р = NULL;
р = rnernchr(str, 'к', strlen(str));
if (р) (
printf("Индeкc: %d\n", (int)(р - str));
} // Индекс: 4
234 Глава 7
Пример:
setlocale(LC_ALL, "Russian Russia .1251");
char str[] = "строка";
_strset(str, '*');
printf("'%s'\n", str); // '******'
□ memset() - заменяет первые count элементов массива dest символом ch. В ка
честве значения функция возвращает указатель на массив dest. Прототип функ
ции:
#include <string.h>
void *memset(void *dest, int ch, size t count);
Tlgm: @it_boooks
236 Глава 1
Пример:
char str[] = "String";
memset(str, '*', 4);
printf("'%s'\n", str); // ' ****ng'
□ _strrev() - меняет порядок следования символов внутри С-строки на противо
положный, как бы переворачивает строку. Функция возвращает указатель на
строку str. Прототип функции:
#include <string.h>
char *_strrev(char *str);
Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
char str[] = "строка";
_strrev(str);
printf("%s\n", str); // акортс
Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
char str [] = "АБВГдЕ�ИЙКЛМНОПРСГУФХЦЧIШЦЬЬIЬЭЮЯ";
_strlwr(str);
printf("%s\n", str);
// абвгдеёжзийклмнопрстуфхцЧIШЦ'WЬэюя
238 Глава"
Пример:
char strl[] = "абв", str2[] = "абг";
printf("%d\n", strncrnp(strl, str2, 2)); // О
printf("%d\n", strncrnp(strl, str2, 3)); // -1
#include <string.h>
int _strncoll(const char *strl, const char *str2, size t maxCount);
int _strncoll_l(const char *strl, const char *str2,
size t maxCount, locale t locale);
Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
char strl[] = "абв", str2[] = "абг";
printf("%d\n", _strncoll(strl, str2, 2)); // О
printf("%d\n", _strncoll(strl, str2, 3)); // -1
240 Глава 7
□ stricmp() и _stricrnp_1 () - сравнивают С-строки без учета регистра символ0&
Функции учитывают настройки локали для категории 1с_СТУРЕ. Для русскю.
букв необходимо настроить локаль. Прототипы функций:
#include <string.h>
int _stricrnp(const char *strl, const char *str2);
int stricrnp_l(const char *strl, const char *str2, locale t locale);
Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
char strl [] = "абв", str2 [] = "АБВ";
printf("%d\n", _stricrnp(strl, str2)); // О
□ _strnicrnp() и _strnicrnp_1() - сравнивают rnaxcount первых символов в С-с�
ках strl и str2 без учета регистра символов. Функции учитывают настройКII
локали для категории 1с_СТУРЕ. Для русских букв необходимо настроить лока.11,,..
Прототипы функций:
#include <string.h>
int strnicrnp(const char *strl, const char *str2, size t rnaxCount);
int _strnicrnp_l(const char *strl, const char *str2,
size t rnaxCount, locale t locale);
Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
char strl[] = "абве", str2[] = "АБВЖ";
printf("%d\n", strnicmp(strl, str2, З)); // О
printf("%d\n", _strnicmp(strl, str2, 4)); // -1
□ _stricoll() и _stricoll_l() - сравнивают С-строки без учета регистра сим�
лов. Функции учитывают настройки локали для категорий 1с_СТУРЕ и 1с_COLIA�
Для русских букв необходимо настроить локаль. Прототипы функций:
#include <string.h>
int _stricoll(const char *strl, const char *str2);
int _stricoll_l(const char *strl, const char *str2, locale t locale);
Пример:
char strl[] = "абв", str2[] = "АБВ";
printf("%d\n", stricoll(strl, str2)); // 32
locale t locale = create_locale(LC_ALL, "Russian_Russia.1251");
printf("%d\n", _stricoll_l(strl, str2, locale)); // О
free_locale(locale);
setlocale(LC_ALL, "Russian_Russia.1251");
printf("%d\n", _stricoll(strl, str2)); // о
□ _strnicoll() и _strnicoll_ 1 () - сравнивают rnaxcount первых символов в С-�
ках strl и str2 без учета регистра символов. Функции учитывают настройки
локали для категорий 1с_СТУРЕ и 1с_COLIATE. Для русских букв необходимо
настроить локаль. Прототипы функций:
Tlgm: @it_boooks
#include <string.h>
int _strnicoll (const char *strl, const char *str2, size_t ma):CC.01.шt);
int _strnicoll_l(const char *strl, const char *str2,
size_t maxCount, locale t locale);
Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
char strl[] = "абвг", str2[] = "АБВД";
printf("%d\n", _strnicoll(strl, str2, З)); // О
printf("%d\n", _strnicoll(strl, str2, 4)); // -1
Для сравнения строк можно также использовать функцию memcmp (). Функция
шemcmp () сравнивает первые size байтов массивов bufl и buf2. В качестве значения
функция возвращает:
□ отрицательное число - если bufl меньше buf2;
□ о - если массивы равны;
□ положительное число - если bufl больше buf2.
Прототип функции:
linclude <string.h>
int memcmp(const void *bufl, const void *buf2, size t size);
Пример:
char strl[] = "аЬс", str2[] = "аЬс";
int х = memcmp(strl, str2, sizeof str2);
printf("%d\n", х); // о
str1[2] = 'Ь';
х = memcmp(strl, str2, sizeof str2);
printf("%d\n", х); // -1
str1[2] = 'd';
х = memcmp(strl, str2, sizeof str2);
printf("%d\n", х); // 1
242 Глава 7
setlocale(LC_ALL, "Russian_Russia.1251");
char strl [] = "абв", str2 [] = "АБВ";
int х = _rnernicrnp(strl, str2, sizeof str2);
printf("%d\n", х); // О
х = rnerncrnp(strl, str2, sizeof str2);
printf("%d\n", х); // 1
locale t locale = _create_locale(LC_ALL, "Russian_Russia.1251");
х = _rnernicrnp_l(strl, str2, sizeof str2, locale);
printf("%d\n", х); !/ о
free locale(locale);
ГЛАВА 8
printf ( "\n");
// 1089 1090 1088 1086 1082 1072 32 115 116 114 105 110 103
Если получили точно такие же числа, то можно начать изучение. Если же чис.,а
отличаются, то следует проверить кодировку файла с программой - она должна
быть windo\vs-1251. Хотя на самом деле кодировка файлов по умолчанию в М inG \\
должна быть UTF-8, но в этом случае вы не сможете работать с обычными строка
ми, т. к. русские буквы в кодировке UTF-8 кодируются двумя байтами, а не одни�.
Для того чтобы избежать проблем с русскими буквами, для файлов мы используе"'
кодировку \Vindows-1251, а не UTF-8.
Если при компиляции полу•1или эту ошибку:
err·or: converting to execution character set: Illegal byte sequence
C:\Ьoox>test.exe
1089 1090 1088 1086 1082 1072 32 115 116 114 105 110 103
Если получили следующую ошибку:
ccl.exe: error: no iconvimplementation, cannot convert frorn ср1251 to
DТF-8
то нужно либо сменить компилятор, т. к. он собран без поддержки библиотеки
iconv, либо использовать для файлов кодировку UTF-8 и не указывать флаги
-finput-charset И -fexec-charset при КОМПИЛЯЦИИ.
Для того чтобы задать флаги в Eclipse, в свойствах проекта из списка слева выбира
I
ем пункт С/С++ Build Settings. На отобразившейся вкладке из списка
Configuration выбираем пункт AII configurations, а затем на вкладке Tool Settings
I
из списка выбираем пункт GCC С Compiler Miscellaneous (см. рис. 1.35). В поле
Otber Oags через пробел к имеющемуся значению добавляем следующие инструк
ции:
-finput-charset=cp1251 -fexec-charset=cp1251
Содержимое поля Other Oags после изменения должно быть таким:
-с -frnessage--ength=0 -finput-charset=cp1251 -fexec-charset=cp1251
Эти флаги должны быть в окне Console при компиляции в Eclipse:
07:22:28 **** Incremental Build of configuration Debug for project
Test64c ****
Info: Internal Builder is used for build
qcc �оо -g3 -Wall -Wconversion -с -frnessage-length=0
-finput-charset=cp1251 -fexec-charset=cp1251 -о "src\\Test64c.o"
" .. \\src\\Test64c.c"
gcc -о Test64c.exe "src\\Test64c.o"
246 Глава В
248 Главаl
ch = _getwcJ:i();
wprintf(L"кoд символа: %u\n", ch);
Для ввода широких символов и других данных предназначена функция wscanf ().
Прототип функции:
linclude <wchar.h> /* ипи #include <stdio .h> */
int wscanf(const wchar_t *fоппаt, ... );
В первом параметре указывается строка специального формата, внутри которой
задаются спецификаторы, аналогичные применяемым в функции printf(), а таюке
некоторые дополнительные спецификаторы. В последующих параметрах переда
lОТСЯ адреса переменных. Функция возвращает количество произведенных при
сваиваний. Пример получения символа:
_wsystem(L"chcp 1251"); // Смена кодировки консоли
_wsetlocale(LC_ALL, L"Russian_Russia.1251");
vchar_t ch;
l!lprintf(L"Bвeдитe символ: ");
fflush(stdout); // Сброс буфера вывода
fflush(stdin); // Очистка буфера ввода
int status = wscanf(L"%c", &ch); // Символ & обязателен! ! !
if (status == 1) (
wprintf(L"Bы ввели: %c\n", ch);
wprintf(L"Koд: %u\n", ch);
else (
wprintf(L"Oшибкa при вводе символа\n");
250 Глава 1
wprintf(L"%s\n", str);
// АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЩJJЩЬЫЬЭЮЯ
□ towlower() - возвращает код символа в нижнем регистре. Если преобразоваюu
регистра не было, то код символа возвращается без изменений. Прототип фуJП:
ции:
#include <wchar.h> /* или #include <ctype.h> */
wint_t towlower(wint_t ch);
Пример:
_wsetlocale(LC_ALL, L"Russian_Russia.1251");
wprintf(L"%c\n", towlower(L'W')); // w
wprintf(L"%c\n", towlower(L'Б')); // б
wchar_t str[] = L"АБВГдЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЬЭЮЯ";
for (int i = О, len = (int)wcslen(str); i < len; ++i)
str[i] = towlower(str[i]);
wprintf(L"%s\n", str);
// абвгдеёжзийклмноnрстуфхцчl!llЦ'ЬЬlьэюя
□ _wcsupr() - заменяет все буквы в L-строке str соответствующими прописны'°'
буквами. Функция возвращает указатель на строку str. Прототип функции:
#include <wchar.h> /* или #include <string.h> */
wchar_t *_wcsupr(wchar_t *str);
Пример:
_wsetlocale(LC_ALL, L"Russian_Russia.1251");
wchar_t str[] = L"абвгдеёжзийклмноnрстуфхцчшщъыьэюя";
_wcsupr(str);
wprintf(L"%s\n", str);
// АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЩJJЩЬЫЬЭЮЯ
252 Глава 1
Пример:
wsetlocale(LC_ALL, L"Russian_Russia.1251");
wprintf(L"%d\n", iswxdigit(L'8')); // 128
wprintf(L"%d\n", iswxdigit(L'a')); // 128
wprintf(L"%d\n", iswxdigit(L'F')); // 128
wprintf(L"%d\n", iswxdigit(L'g')); // О
□ iswalpha() - возвращает ненулевое значение, если символ является буквой.
в противном случае - о. Прототип функции:
#include <wchar.h> /* или #include <ctype.h> */
int iswalpha(wint_t ch);
Пример:
wsetlocale(LC_ALL, L"Russian_Russia.1251");
wprintf(L"%d\n", is·walpha(L'w')); // 258
wprintf(L"%d\n", is•,1alpha(L' 2')); // о
wprintf(L"%d\n", iswalpha(L'б')); // 258
wprintf(L"%d\n", iswalpha(L'Б')); // 257
□ iswspace() - возвращает ненулевое значение, если символ является пробе.11,,..
ным символом (пробелом, табуляцией, переводом строки или возвратом карет
ки), в противном случае- о. Прототип функции:
#include <wchar.h> /* или #include <ctype.h> */
int iswspace(wint_t ch);
Пример:
wsetlocale(LC_ALL, L"Russian_Russia.1251");
wprintf(L"%d\n", iswspace(L'w')); // О
wprintf(L"%d\n", iswspace(L'\n')); // 8
wprintf(L"%d\n", iswspace(L'\t')); // 8
wprintf(L"%d\n", iswspace(L'б')); // О
□ iswalnum() - возвращает ненулевое значение, если символ является буквой Н..18
цифрой, в противном случае - о. Прототип функции:
#include <wchar.h> /* или #include <ctype.h> */
int iswalnum(wint_t ch);
Пример:
wsetlocale(LC_ALL, L"Russian_Russia.1251");
wprintf(L"%d\n", iswalnum(L'w')); // 258
wprintf(L"%d\n", iswalnum(L'8')); // 4
wprintf(L"%d\n", iswalnum(L'\t')); // о
wprintf(L"%d\n", iswalnum(L'б')); // 258
□ iswlower() - возвращает ненулевое значение, если символ является буквоi
в н�жнем регистре, в противном случае- о. Прототип функции:
#include <wchar.h> /* или #include <ctype.h> */
int iswlower(wint_t ch);
Tlgm: @it_boooks
Пример:
_wsetlocale(LC_ALL, L"Russian_Russia.1251");
wprintf(L"%d\n", iswlower(L'w')); // 2
wprintf(L"%d\n", iswlower(L'8')); // о
wprintf(L"%d\n", iswlower(L'\t')); // о
wprintf(L"%d\n", iswlower(L'б')); // 2
wprintf(L"%d\n", iswlower(L'Б')); // о
□ iswupper( J - возвращает ненулевое значение, если символ является буквой
в верхнем регистре, в противном случае - о. Прототип функции:
#include <wchar.h> /* или #include <ctype.h> */
int iswupper(wint_t ch);
Пример:
_wsetlocale(LC_ALL, L"Russian_Russia.1251");
wprintf(L"%d\n", iswupper(L'W')); // 1
wprintf(L"%d\n", iswupper(L'8')); // О
wprintf(L"%d\n", iswupper(L'\t')); // О
wprintf(L"%d\n", iswupper(L'б')); // О
wprintf(L"%d\n", iswupper(L'Б')); // 1
254 Главаl
#include <stdio.h>
#include <locale.h>
#include <wchar.h>
int main(void) {
_wsetlocale(LC_ALL, L"Russian_Russia.1251");
wchar t ch = L'п';
if {iswlower(ch)) {
wprintf(L"Cтpoчнaя\n");
Tlgm: @it_boooks
else {
wprintf(L"Пpoпиcнaя\n");
// р·\зуль тат : Строчная
ch = towupper(ch);
wprintf(L"%c\n", ch); // П
return О;
256 Глава 1
Обратите внимание на вторую строку. В этом пути присутствуют сразу три спе
циальных символа: \t, \n и \f. После преобразования специальных символов путь
будет выглядеть следующим образом:
С:<Табуляция>еmр<Перевод строки>еw<Перевод фopмaтa>ile.txt
Разместить L-строку при инициализации на нескольких строках нельзя. Переход на
1Ювую строку вызовет синтаксическую ошибку:
vchar_t str[] = L"stringl
string2"; // Ошибка! ! !
Для того чтобы разместить L-строку на нескольких строках, следует перед симво
.-ом перевода строки указать символ \:
vc:har_t str[] = L"stringl \
string2 \
stringЗ"; // После символа \ не должно быть никаких символов
11printf(L"%s\n", str); // stringl string2 stringЗ
Кроме того, можно воспользоваться неявной конкатенацией. Если строки распо
.1Ожены подряд внутри одной инструкции, то они объединяются в одну большую
С11)0ку. Пример:
-.:har_t str[] = L"stringl"
L•string2" L'-'stringЗ";
11printf(L"%s\n", str); //stringlstring2string3
258 Глава 1
_putws(L"-------------------"};
// ВЫводим символы в обратном порядке
for (int i = len - 1; i >= О; --i} {
wprintf(L"%c\n", str[i]);
260 Глава 1
Результат выполнения:
Stringl
String2
Для форматированного вывода L-строк и других данных используется функция
wprintf(). Прототип функции:
tinclude <wchar.h> /* или iinclude <stdio.h> */
int wprintf(const wchar_t *format, ...);
В параметре format указывается L-строка специального формата. Внутри этой
строки можно указать обычные символы и спецификаторы формата, начинающиеся
с символа %. Вместо спецификаторов формата подставляются значения, указанные
1 качестве параметров. Количество спецификаторов должно совпадать с количест-
80М переданных параметров. Спецификаторы мы уже рассматривали в разд. 2.8
nрименительно к функции printf(). В качестве значения функция возвращает ко
•нчество выведенных символов. Пример вывода L-строки и числа:
_wsetlocale(LC_ALL, L"Russian_Russia.1251");
wprintf(L"строка\n"); // строка
wprintf(L"Count %d\n", 10); // Count 10
wprintf(L"%s %d\n", L"Count", 10); // Count 10
Для вывода L-строки можно также воспользоваться спецификаторами %1s и %S
1 строке формата функции printf():
setlocale(LC_ALL, "Russian_Russia.1251");
printf("%1s\n", L"строка"); // строка
printf("%S\n", L"строка"); // строка
Если спецификатор %S указать в строке формата функции wprintf(), то можно
будет вывести С-строку:
_wsetlocale(LC_ALL, L"Russian_Russia.1251");
wprintf(L"%S\n", "строка"); // строка
Для ввода L-строки предназначена функция _getws() , однако применять ее в про
rрамме не следует, т. к. функция не производит никакой проверки длины строки,
"ПО может привести к переполнению буфера. Прототип функции _getws () :
linclude <wchar.h> /* или iinclude <stdio .h> */
vchar_t *_getws(wchar_t *buffer);
Лучше получать строку посимвольно с помощью функции getwchar() или восполь
эоваться функцией fgetws(). Прототип функции fgetws() :
linclude <wchar.h> /* или iinclude <stdio.h> */
wchar_t *fgetws(wchar_t *buffer, int maxCount, FILE *stream);
8 качестве параметра stream указывается стандартный поток ввода stdin. Считыва
ние производится до первого символа перевода строки или до конца потока или
пока не будет прочитано maxcount-1 символов. Содержимое строки buffer включает
символ перевода строки. Исключением является последняя строка. Если она не за-
Tlgm: @it_boooks
262 Глава&
#include <stdio.h>
#include <locale.h>
#include <wchar.h>
int main(void) {
_wsystem(L"chcp 1251"); // смена кодировки консоли
_wsetlocale(LC_ALL, L"Russian_Russia.1251");
wchar_t buf[256] = {01, *р = NULL;
wprintf(L"Bвeдитe строку: ");
fflush (stdout);
fflush (stdin);
р = fgetws(buf, 256, stdin);
if (р) {
// Удаляем символ перевода строки
size t len wcslen(buf);
if (len != О && buf[len - 1] == '\n')
buf[len - 1] = L'\0';
// Выводим результат
wprintf(L"Bы ввели: %s\n", buf);
else {
_putws(L"Oшибкa при вводе числа");
else {
_putws(L"Oшибкa при вводе строки");
264 Главаl
НА ЗАМЕТКУ
В Windows для преобразования С-строки в различных кодировках в L-строку и наобо
рот можно воспользоваться функциями из WinAPI MultiByteToWideChar ( •
WideCharToMultiByte(). Описание функций приведено в разд. 8.12.
Tlgm: @it_boooks
ldefine -CRT-SECURE-NO-WARNINGS
linclude <stdio.h>
linclude <locale.h>
linclude <string.h>
linclude <wchar.h>
linclude <stdlib.h>
int main(void) {
size_t count;
char ср866[256] = {О};
wchar_t wstr[256] = {О};
char ср1251[] = "строка"; // Исходная строка (windows-1251)
for (int i = О, len = (int)strlen(cpl251); i < len; ++i) {
printf("%d ", cpl25l[i]);
} // -15 -14 -16 -18 -22 -32
printf("\n");
2бб Глава 1
#include <stdio.h>
#include <locale.h>
#include <string.h>
#include <windows.h>
Tlgm: @it_boooks
int main(void) (
setlocale(LC_ALL, "Russian_Russia.1251");
char ср866[256] = {О};
char ср1251[256] = {О};
char str[J = "строка"; // Исходная строка в кодировке windows-1251
268 Глава 1
-- ------
- --·
-
: librari6 4{J �'JI ® {] <), i
1Kerne:132�1ib
:
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
int count = О;
char ср866[256] = {О};
char utf8[256] = {О};
wchar_t wstr[256] = {О};
wchar_t wstr2[256] = {О};
char str[] = "строка str"; // Исходная строка (windows-1251)
Tlgm: @it_boooks
270 Глава 1
for (int i = О, len = (int)strlen(str); i < len; ++i) {
printf("%d ", str[i]);
) // -15 -14 -16 -18 -22 -32 32 115 116 114
printf("\n");
272 Глава 1
_wsetlocale(LC_ALL, L"Russian_Russia.1251");
wchar_t str[7] = L"строка";
int len = (int)wcsnlen(str, 7);
wprintf(L"%d\n", len); // 6
274 Глава 1
276 Глава 1
if (р) {
wprintf(L"\!:s\п", р); // строка
free (р);
Практически все функции без суффикса _s, которые мы рассмотрели в этом разде
.11е, в Visual С выводят предупреждающее сообщение warning С4996, т. к. по умол
чанию функции не производят никакой проверки корректности данных. В этом
случае возможно переполнение буфера. Забота о корректности данных полностью
вежит на плечах программиста. Если вы уверены в своих действиях, то можно по
.uвить вывод предупреждающих сообщений. Сделать это можно двумя способами:
С] определить макрос с названием _CRT_SECURE_NO_WARNINGS в самом начале про
граммы, перед подключением заголовочных файлов. Пример:
#define CRT SECURE NO WARNINGS
#include <stdio.h>
#include <wchar.h>
ОБРАТИТЕ ВНИМАНИЕ
Поспе директив #define и #pragma точка с запятой не указывается.
278 Глава 1
Пример:
wsetlocale(LC_ALL, L"Russian_Russia.1251");
wchar_t str[] = L"строка строка", *р = NULL;
р = wcsrchr(str, L'к');
if (р) {
wprintf(L"Индекс: %d\n", (int)(р - str));
} // Индекс: 11
□ ищет в L-строке str первое вхождение символа ch. Максимальнс
wmernchr () -
просматривается rnaxcount символов. В качестве значения функция возвращает
указатель на первый найденный символ в строке str или нулевой указате.,�..
если символ не найден. Прототип функции:
#include <wchar.h>
wchar t *wmernchr(const wchar t *str, wchar t ch, size t rnaxCount);
Пример:
wsetlocale(LC_ALL, L"Russian_Russia.1251");
wchar_t str[] = L"строка строка", *р = NULL;
р = wmernchr(str, L'к', wcslen(str));
if (р) {
wprintf(L"Индeкc: %d\n", (int)(р - str));
} // Индекс: 4
Пример:
..:..wsetlocale(LC_ALL, L"Russian Russia.1251");
wchar_t str[] = L"строка строка";
int index = (int)wcscspn(str, L"кр");
wprintf(L"Индeкc: %d\n", index);
// Индекс: 2 (индекс первой буквы "р")
index = (int)wcscspn(str, L"ф");
wprintf(L"Индекс: %d\n", index);
// Индекс: 13 (длина строки, если ничего не наЙдено)
□ wcsspn() - возвращает индекс первого символа в L-строке str, который не сов
падает ни с одним из символов, входящих в L-строку control. Прототип функ
ции:
#include <wchar.h> /* или #include <string.h> */
size_t wcsspn(const wchar t *str, const wchar t *control);
Пример:
_wsetlocale(LC_ALL, L"Russian Russia.1251");
wchar_t str[] = L"строка строка";
int index = (int)wcsspn(str, L"окстр");
wprintf(L"Индекс: %d\n", index);
// Индекс: 5 ("а" не входит в "окстр")
□ wcsstr () - ищет в L-строке str первое вхождение целого фрагмента из
L-строки suЬStr. Вкачестве значения функция возвращает указатель на первое
вхождение фрагмента в строку или нулевой указатель - в противном случае.
Прототип функции:
#include <wchar.h> /* или #include <string.h> */
wchar_t *wcsstr(const wchar t *str, const wchar t *suЬStr);
Пример:
_wsetlocale(LC_ALL, L"Russian Russia.1251");
wchar_t str[] = L"строка строка", *р = NULL;
р = wcsstr(str, L"ока");
if (р) {
wprintf(L"Индeкc: %d\n", (int)(р - str));
} // Индекс: 3
[] _wcsset() - заменяет все символы в L-строке str указанным символом ch.
Функция возвращает указатель на строку str. Прототип функции:
#include <wchar.h> /* или #include <string.h> */
wchar_t *_wcsset(wchar_t *str, wchar t ch);
Пример:
wsetlocale(LC_ALL, L"Russian_Russia.1251");
wchar_t str[] = L"строка";.
_wcsset(str, L'*');
wprintf (L" '%s'\n", str); // '******'
Tlgm: @it_boooks
280 Глава 1
□ wmemset() - заменяет первые count элементов массива dest символом ch. В ка
честве значения функция возвращает указатель на массив dest. Прототип фун��;
ции:
#include <wchar.h>
wchar_t *wmemset(wchar_t *dest, wchar t ch, size t count);
Пример:
wsetlocale(LC_ALL, L"Russian_Russia.1251");
wchar_t str[] = L"String";
wmemset(str, L'*', 4);
wprintf(L"'%s'\n", str); // '****ng'
Tlgm: @it_boooks
282 Глава 1
Пример:
wsetlocale(LC_ALL, L"Russian_Russia.1251");
wchar_t strl[] = L"абв", str2(] = L"абв", strЗ[] L"АБВ";
wprintf(L"%d\n", wcscmp(strl, str2)); // О
wprintf(L"%d\n", wcscmp(strl, strЗ)); // 1
strl[2] = L'б'; // strl[] = L"абб", str2[] L"абв";
wprintf(L"%d\n", wcscmp(strl, str2)); // -1
strl[2] = L'г'; // strl[] = L"абг", str2[] L"абв";
wprintf(L"%d\n", wcscmp(strl, str2)); // 1
284 Глаееl
Дnя сравнения строк можно также использовать функцию wmemcrnp() . Она сравнива
ет первые count символов массивов ьuп и buf2. В качестве значения функция воз-
1ращает:
С1 отрицательное число - если bufl меньше buf2;
С1 о - если массивы равны;
С1 положительное число - если bufl больше buf2.
Прототип функции:
linclude <wchar.h>
int wmemcmp(const wchar t *strl, const wchar t *str2, size t count);
Tlgm: @it_boooks
286 Глава В
Пример:
_wsetlocale(1C_A11, 1"Russian_Russia.1251");
wchar_t strl[] = 1"аЬс", str2[] = 1"аЬс";
wprintf(1"%d\n", wmemcmp(strl, str2, 3)); // о
str1[2] = 1'Ь';
wprintf(1"%d\n", wmemcmp(strl, str2, 3)); // -1
str1(2] = 1'd';
wprintf(1"%d\n", wmemcmp(strl, str2, 3)); // 1
Пример преобразования:
wprintf(L"%ld\n", _wtol(L"25")); // 25
wprintf(L"%ld\n", wtol(L" \n -25")); // -25
wprintf(L"%ld\n", _wtol(L"2.5")); // 2
wprintf(L"%ld\n", _wtol(L"5str")); // 5
wprintf(L"%ld\n", _wtol(L"5s10")); // 5
wprintf(L"%ld\n", _wtol(L"str")); // о
Если значение выходит за диапазон значений типа long, то в Visual С возвраща
ется максимальное или минимальное значение типа long и переменной errno
присваивается значение ERANGE:
_wsetlocale(LC_ALL, L"Russian_Russia.1251");
wprintf(L"%ld\n", _wtol(L"2147483649")); // 2147483647
wprintf(L"%d\n", errno); // 34
// #include <rnath.h> или #include <errno.h>
if (errno == ERANGE)
_putws(L"Выxoд за диапазон значений");
288 Глава 1
Пример преобразования:
wprintf(L"%I64d\n", wtoll(L"9223372036854775807"));
// 9223372036854775807
wprintf(L"%I64d\n", wtoll(L"9223372036854775809"));
// 9223372036854775807
wprintf(L"%d\n", errno); // 34
290 Главаl
_wsetlocale(LC_ALL, L"Russian_Russia.1251"};
wprintf(L"%I64d\n", wcstoll(L"str", NULL, О}}; // О
wprintf(L"%I64d\n", wcstoll(L"9223372036854775809", NULL, О}};
// 9223372036854775807 (соответствует LLONG_МAX}
wprintf(L"%d\n", errno}; // 34
// #include <math.h> или #include <errno.h>
if (errno == ERANGE}
_putws(L"Bыxoд за диапазон значений"};
Если в строке нет числа, то возвращается число о. Если число выходит за диапа
зон значений дr1я типа, то значение будет соответствовать минимальному
(INТМAX_MIN или о) или максимальному (INТМАХ_МА.'{ или UINТМАХ_МАХ) значению
дr1я типа, а переменной errno присваивается значение ERANGE. Пример:
_wsetlocale(LC_ALL, L"Russian_Russia.1251");
// #include <stdint.h>
wprintf(L"%I64d\n", INТМAX_МIN); // -9223372036854775808
wprintf(L"%I64d\n", INТМАХ_МАХ); // 9223372036854775807
wprintf(L"%I64u\n", UINТМАХ_МАХ); // 18446744073709551615
wprintf(L"%I64d\n", wcstoimax (L"str", NULL, О)); // О
wprintf(L"%I64d\n", wcstoimax(L"9223372036854775809", NULL, О));
// 9223372036854775807 (соответствует INТМАХ_МАХ)
wprintf(L"%d\n", errno); // 34
// #include <math.h> или #include <errno.h>
if (errno == ERANGE)
_putws(L"ВЫXoд за диапазон значений");
292 Глава 1
Если в строке нет числа, то возвращается число о. Если число выходит за диа
пазон значений для типа, то возвращается значение -HUGE VA1 или HUGE_Vf:й.... а
переменной errno присваивается значение ERANGE;
□ wcstof () - преобразует L-строку в вещественное число, имеющее тип floa:.
Прототип функции:
#include <stdlib.h> /* или #include <wchar.h> */
float wcstof(const wchar t *str, wchar t **endPtr);
Tlgm: @it_boooks
294 Глава8
При вводе строки функция swscanf() не производит никакой проверки длины с�-..
ки, что может привести к переполнению буфера. Обязательно указывайте шири�
при использовании спецификатора %s (например, %255s). Кроме того, следует учи
тывать, что функция не проверяет возможность выхода значения за диапазон зна
чений для типа, что может привести к неправильному результату. Функция ни�..
об этом вас не предупредит, поэтому лучше использовать функции, рассмотренные
в начале этого раздела.
Пример:
wchar_t str[l00] = {О};
long х = 255;
_ltow_s(x, str, 100, 10);
wprintf(L"%s\n", str); // 255
wprintf(L"%s\n", ltow(x, str, 16)); // ff
296 Глава 1
Пример:
wchar_t str[l00] = {О};
_int64 х = 255;
i64tow_s(x, str, 100, 10);
wprintf(1"%s\n", str); // 255
wprintf(1"%s\n", _i64tow(х, str, 16)); // ff
Пример:
wchar_t str[l00] {О};
unsigned int64 х = 255;
_ui64tow_s(x, str, 100, 10);
wprintf (1"%s\n", str); // 255
wprintf(L"%s\n", _ui64tow(x, str, 16)); // ff
Выполнить форматирование L-строки, а также преобразовать значения элементар
ных типов в L-строку можно с помощью функции swprintf_ s (). Прототип
функции:
#include <stdio.h> /* или #include <wchar.h> */
int swprintf_s(wchar_t *buf, size_t sizeinWords,
const wchar_t *forrnat, ...);
298 Глава 1
ГЛАВА 9
printf("%d\n", ptm->trn_rnday); // 25
printf("%d\n", ptm->trn_rnon); // о (январь)
printf("%d\n", ptm->trn_year); // 119 (1900 + 119 = 2019 г.)
printf("%d\n", ptrn->trn_hour); // 2
printf("%d\n", ptrn->trn_rnin); // 36
printf("%d\n", ptrn->tm_sec); // 10
Tlgm: @it_boooks
302 Глава 9
if ( !ptm) (
printf("Error\n");
exit(l);
printf("%d\n", ptm->tm_mclay); // 25
printf("%d\n", ptm->tm_mon); // о (январь)
printf("%d\n", ptm->tm_year); // 119 (1900 + 119 2019 г.)
printf("%d\n", ptm->tm_hour); // 5
printf("%d\n", ptm->tm_min); // 45
printf("%d\n", ptm->tm_sec); // 49
printf("%d\n", ptm->tm_wday); // 5 (пятница)
printf("%d\n", ptm->tm_yday); // 24
printf("%d\n", ptm->tm_isdst); // о
Вместо функции localtime() можно использовать функцию _localtime64 ().
Число перед открывающей круглой скобкой означает количество битов. Прото
тип функции:
struct tm *_localtime64(const _time64_t *Time);
Вместо функции localtime() лучше использовать "функцию localtime_s()
(_localtime64_s() вместо _localtime64 ()). Прототипы функций:
#include <time.h>
errno_t localtime_s(struct tm *Тm, const time_t *Time);
errno_t _localtime64_s(struct tm *Тm, const _time64_t *Time);
Если ошибок нет, то функции возвращают значение о. При наличии ошибки воз
вращается значение макроса EINVAL (значение равно 22) и переменная errno
устанавливается равной EINVAL. Пример использования функции localtime_s():
struct tm ptm;
time_t t = time(NULL);
errno t err = localtime_s(&ptm, &t);
if (err) 1
printf("Error\n");
exit(l);
304 Главаl
#include <tirne.h>
tirne_t rnktirne(struct trn *Тm);
Пример использования функции:
struct trn ptrn;
tirne_t tl = tirne(NU11), t2 = О;
errno_t err = localtirne_s(&ptrn, &tl);
if (err) {
printf("Error\n");
exit(l);
t2 = rnktirne(&ptrn);
printf("%I64d\n", tl); // 1548384762
printf("%I64d\n", t2); // 1548384762
int rnain(void) {
setlocale(1C_AL1, "Russian_Russia.1251");
Tlgm: @it_boooks
Результат выполнения:
Сегодня:
пятница 25 января 2019 15:58:28
25.01.2019
306 Глава t
р = asctime(ptm);
if ( !р) {
printf("Error\n");
exit(l);
р = ctime(&t);
if (!р) {
printf("Error\n");
exit(1);
308 Глава■
jinclude <stdio.h>
jinclude <stdlib.h>
jinclude <locale.h>
jinclude <time.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russ�a.1251");
struct trn *ptrn = NULL;
char str[SIZE] = {0};
time_t t = time(NULL);
ptm = localtime(&t);
if ( !ptrn) {
printf("Error\n");
exit(l);
printf("%s\n", str);
return О;
Результат выполнения:
Сегодня:
пятница 25 янв 2019 16:17:15
25.01.2019
310 Глава 1
Для примера выведем числа от 1 до 10 (листинг 9.3). Между выводом чисел ''за
снем" на одну секунду.
int main(void) {
printf("Start\n");
for (int i = 1; i <= 10; ++i) {
Sleep(l000); // Засыпаем на секунду
printf("i = %d\n", i);
Tlgm: @it_boooks
fflush(stdout);
printf("End\n");
return О;
linclude <stdio.h>
linclude <time.h>
linclude <windows.h>
int main(void) [
clock_t tl, t2, tЗ;
tl = clock(); // Метка 1
printf("tl = %ld\n", tl);
fflush(stdout);
Sleep(З000); // Имитация фрагмента кода
t2 = clock(); // Метка 2
printf("t2 = %ld\n", t2);
tЗ = t2 - tl; // Разница.между метками
printf("tЗ = %ld\n", tЗ);
printf("%.2f sec.\n", (douЬle)tЗ / CLOCKS_PER_SEC);
return О;
Tlgm: @it_boooks
ГЛАВА 10
Пользовательские функции
<Тело функции>
[return[ <Возвращаемое значение>];]
Если переменная х имеет значение больше нуля, то все будет нормально, но если
переменная равна нулю или имеет отрицательное значение, то возвращаемое з11а
чение не определено и функция вернет произвольное значение, так называемый
•мусор". В этом случае при компиляции выводится предупреждающее сообщение:
varning: control reaches end of non-void function. Дпя того чтобы избежать по
лобной ситуации, следует в конце тела функции вставить оператор return со значе
нием по умолчанию:
int sum(int х, int у)
if (х > О) {
return х + у;
Tlgm: @it_boooks
return О;
Если перед названием функции указано ключевое слово void, то оператора re: -=
может не быть. Однако если необходимо досрочно прервать выполнение функUНL
то оператор return указывается без возвращаемого значения. Пример:
void print_ok(void) {
puts("OK");
return; // Преждевр�енное завершение функции
puts("Этa инструкция никогда не будет выполнена!!!");
#include <stdio.h>
#include <locale.h>
// Объявления функций
int sum(int х, int у) ; // или int sum(int, int);
void print(const char *str); // или void print(const char *);
void print_ok(void);
int rnain(void) {
setlocale(LC_ALL, "Russian Russia.1251");
// Вызов функций
рrint("Сообщение"); // Сообщение
print_ok(); // ок
int z = sum(l0, 20);
printf("%d\n", z); // 30
return О;
Tlgm: @it_boooks
// Определения функций
int sum(int х, int у) { // Два параметра
return х + у;
linclude <stdio.h>
int main(void) {
int z = sum_i(l0, 20);
printf("z = %d\n", z); // z 30
return О;
316 Глава 10
#include <stdio.h>
int main(void)
int z = sum i(l0, 20);
printf("z = %d\n", z); // z 30
return О;
#include "my_module.h"
Для создания файла my_module.h в окне Project Explorer щелкаем правой кtю11кой
мыши на названии каталога src и из контекстного меню выбираем пункт New 1
Header File (см. рис. 2.2). В открывшемся окне (см. рис. 2.6) в поле Source folder
должно быть уже вставлено значение, например, тest64c/src. В поле Header file
вводим my_module. h, а из списка Template выбираем пункт Default С header ten1-
plate. Нажимаем кнопку Finish.
В результате файл будет добавлен в каталог проекта, и его название отобразится
в окне Project Explorer, а сам файл будет открыт на отдельной вкладке. Вставляем
в этот файл код из листинга 10.5.
tifndef МУ MODULE н
ldefine МУ MODULE Н
linclude <stdio.h>
int sum_i(int, int); // Объявление функции
lendif /* МУ MODULE Н */
318
#include "my_module.h"
int main(void) {
int z = sum_i(l0, 20);
printf("z = %d\n", z); // z 30
return О;
finclude <stdio.h>
int main(void)
int х = 10;
func(х);
printf("%d\n", х); // 10
return О;
void func(int х)
х = х + 20; // Значение нигде не сохраняется!
Передача копии значения для чисел является хорошим решением, но при использо
ании массивов, а также при необходимости изменить значение исходной перемен
ной, лучше применять передачу указателя. Для этого при вызове функции перед
названием переменной указывается оператор & (взятие адреса), а в прототипе функ
llНИ объявляется указатель. В этом случае в функцию передается не значение пере
менной, а ее адрес. Внутри функции вместо переменной используется указатель.
Пример передачи указателя приведен в листинге 10.8.
tinclude <stdio.h>
int main(void)
int х = 10;
func(&x); // Передаем адрес
printf("%d\n", х); // 30, а не 10
return 01'
320 Глава Н1
int rnain(void) [
char str[] = "String";
printf("id\n", (int) sizeof(str)); // 7
funcl(str); // Оператор & перед str не указывается!!!
func2 (str);
printf("%s\n", str); // strinG
recurn О;
linclude <stdio.h>
int main(void) {
int i, j;
int arr[ARR_ROWS] [ARR_COLS]
{1, 2, 3, 41,
{5, 6, 7, 8}
};
func(arr);
// Выводим значения
for (i = О; i < ARR_ROWS; ++i) {
for (j = О; j < ARR_COLS; ++j)
printf("%2d ", arr[i][j]);
printf("\n");
return О;
1
8'id func(int а[][4]) { // или void func(int а[2] [4])
а[О][О] = 80;
1
322 Глава 10
#include <stdio.h>
int main(void) {
int arr[ARR_ROWS][ARR_COLS]
{ 1, 2, з, 41,
(5, 6, 7, 81
};
// Создаем массив указателей
int *parr[] = {arr[0], arr[l] };
// Передаем адрес массива указателей
func(parr, ARR_ROWS, ARR_COLS);
return О;
printf("\n");
#include <stdio.h>
int main(void) {
int arr[ARR_ROWS] [ARR_COLS]
{1, 2, 3, 41,
{5, 6, 7, 8}
};
// Передаем в функцию адрес первого элемента массива
func(arr[0], ARR_ROWS, ARR_COLS);
return О;
printf("\n");
Передача в функцию массива С-строк осуществляется так же, как передача массива
указателей. Для того чтобы в функцию не передавать количество элементов, можно
при инициализации массива С-строк последнему элеменrу присвоить нулевой ука
затель. Этот элемент будет служить ориентиром конца массива. В качестве примера
8Ь1Ведем все строки внутри функции (листинг 10.13).
linclude <stdio.h>
324 Глава 10
int main(void)
char *str[] { "Stringl", "String2", "StringЗ",
NULL // Вставляем нулевой указатель, чтобы был ориентир
1;
func(str);
return О;
linclude <stdio.h>
linclude <stdarg.h>
int main(void)
printf("%d\n", sum(2, 20, 30)); // 50
printf("%d\n", SIJПI. ( 3, 1, 2, 3)); // 6
printf("%d\n", sum(4, 1, 2, 3, 4)); // 10
return О;
va_end(p);
return result;
НА ЗАМЕТКУ
При передаче в функцию значения типов char и short автоматически расширяются до
типа int, а значение типа float - до типа douЬle.
326 Глава 11
#include <stdio.h>
int х = О;
void sum(int _х);
Tlgm: @it_boooks
int main(void)
sum(l0);
sum(20);
printf("ld\n", х); // 30
return О;
linclude <stdio.h>
int main(void) {
printf("%d\n", surn(l0)); // 10
printf("ld\n", surn(20)); // 30
printf("%d\n", surn(SS)); // 85
return О;
Ключевое слово static можно также указать для глобальной переменной или
функции. В этом случае область видимости будет ограничена текущим файлом.
Пример определения статической функции:
static void print_ok(void)
puts("OK");
Tlgm: @it_boooks
328 Глава 10
Если переменная х имеет значение больше нуля, то все будет нормально, но ес.,и
переменная равна нулю или имеет отрицательное значение, то возвращаемое зна
чение не определено и функция вернет произвольное значение, так называемый
Tlgm: @it_boooks
return О;
linclude <stdio.h>
linclude <string.h>
int main(void) {
char *р = NULL, str[] "String";
р = func(str);
if (р) { // Если ненулевой указатель
printf("%c\n", *р); // g
else puts("NULL");
return О;
O&PATffTE BHIIMAHffE
Нельзя возвращать указатель на переменные или массивы, объявленные внутри
функции, т. к. при выходе из функции локальные переменные будут удалены. Рабо
тать с массивом необхстимо через параметры функции, передавая указатель на него
или возвращая указатель на конкретный элемент.
Tlgm: @it_boooks
330 Глава 1,
#include <stdio.h>
void print_ok(void);
int add(int х, int у);
int suЬ(int х, int у);
int func(int х, int у, int (*pfunc)(int, int));
int main(void) {
// Объявление указателей на функции
void (*pfl)(void) = NULL;
int (*pf2)(int, int) = NULL;
// Присваивание адреса функции
pfl = print_ok;
pf2 = add;
// Вызов функций через указатели
pfl(); // ок
printf("%d\n", pf2(30, 10)); / / 40
// Передача указателя на функцию
// в качестве параметра
printf("%d\n", func(l0, 5, pf2)); // 15
printf("%d\n", func(l0, 5, add)); // 15
Tlgm: @it_boooks
void print_ok(void)
puts("OK");
332 Глава 11
enum TypeParam {
TINT, TLONG, TLONGLONG, TDOUBLE
};
int main(void) {
int х = INT_МAX;
println("x = ", &х, TINT); //х 2147483647
long у= LONG_МAX;
println("y = ", &у, TLONG); //у= 2147483647
long long z = LLONG_МAX;
println("", &z, TLONGLONG); // 9223372036854775807
douЫe k = 20.2123;
println("k = ", &k, TDOUBLE); // k = 20.21
return О;
10.11. Рекурсия
Рекурсия - это возможность функции вызывать саму себя. При каждом вызоес
функции создается новый набор локальных переменных. Рекурсию удобно испо.11r
зовать для перебора объекта, имеющего заранее неизвестную структуру, н..в
выполнения неопределенного количества операций. Типичным применение,�,
рекурсии является вычисление факториала числа (листинг 10.20).
Tlgm: @it_boooks
tinclude <stdio.h>
int main(void) {
for (unsigned i = 3; i < 11; ++i) {
printf("%d! = %I64u\n", i, factorial(i));
return О;
Результат выполнения:
3! = 6
4 ! = 24
5! 120
б! 720
71 5040
8! 40320
9! 362880
10! = 3628800
334 Глава 11
#include <stdio.h>
int main{void) {
printf{"%d\n", surn{l0, 20));
return О;
#include <stdio.h>
int main{void)
printf{"%d\n", SUМ(l0, 20));
return О;
ГЛАВА 11
Обработка ошибок
338 Глава 11
Settings
, Re:sourc.e
Builde:rs:
,,, CIC т.,. BuiJd Configuration: · l�-�.0!:!9.JA_ct_r,_e___________.._
......�.··i "' Manage Configur11tions...
Build Var1aЫes:
Environrnent
logging
Settings
* Т ool Sff.trr,gs Q Container Se:ttings ; ,.'1-· Build Steps Build Artifact ! !!!f Binary Parsers Q 4
►
Тool Chain Edrtor ,,, �! GCC Аие:mЫе:r [] Check synttx only {-f,;yntax-onl)')
I
С/( ... + General l� Genero:,I ГJ Ped,ntic [-pedantic)
Linux Tocls Path ,,, �) GCC С Compiler ·О Pedantic warnings as e:rrors {·pedant1c-errors)
Prcject Nat,дes � D1alect (.] lnhiЬit all wamings (-w)
Prcject Re:fere:nces J:!':
Pre:proпиor
1(�j AII \1'1arnin9s (-V/a11)
R.un/Debug Settings ,�1 lncludes
�� Optim12:at1on ΠExtra warn1ngs (-Wextra)
Т г�k. P.e:pository
Task Tags J:..�
Debugging [J\�iarnings as errcrs (·Werror)
Validatюn � Wгirning� ,.�; lmplicit con'n:rsion warnings (-\i\1cc-nvers1on)
1/frkiText ��: Miscellane:ous
,,, �� MinG�V С l1nker
� Gener�I
с�
-� l1brar1es
Miscellan�oш
J? Shared library Settings
340 Глава n
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void) {
char err[256] = {О};
strerror_s(err, 255, EILSEQ);
printf("%s\n", err); // Illegal byte sequence
strerror_s(err, 255, ERANGE);
printf("%s\n", err); // Result too large
return О;
Если в параметре указано значение №Л,L, то строка будет содержать тол1>ко оп�tс.а
ние последней ошибки. Если передать строку, то o,ia будет добавлена перед оrнк..
нием ошибки. В строку вставляется символ 11еревода строки. llример:
// #include <stdlib.h>
printf("%ld\n", strtol("2147483649", NULL, О)); // 2147483647
printf("%s", strerror(NULL)); // Result too large
printf("%s", strerror("Error")); // Error: Result too large
wprintf(1"%s", _wcserror(NULL)); // Res1.ilt too large
wprintf(L"%s", _wcserror(L"Error")); // Error: P.esult too large
342 Глава 1,
printf("\n");
printf ("\n");
Одна строка кода не должна содержать более 80 символов. Если количество сим�
лов больше, то следует выполнить переход на новую строку. При этом продолже
ние смещается относительно основной инструкции на величину отступа или вы
равнивается по какому-либо элементу. Иначе приходится пользоваться горизон
тальной полосой прокрутки, а это очень неудобно при поиске ошибок.
Если программа слишком большая, то следует задуматься о разделении програм,1ы
на отдельные функции, которые выполняют логически законченные действия
Помните, что отлаживать отдельную функцию гораздо легче, чем "спагетти"-ко.J
Причем, прежде чем вставить функцию в основную программу, ее следует протес
тировать в отдельном проекте, передавая функции различные значения и проверн
результат ее выполнения.
Обратите внимание на то, что форматирование кода должно выполняться при на
писании кода, а не во время поиска ошибок. Этим вы сократите время поиска
ошибки и, скорее всего, заметите ошибку еще на этапе написания. Если все же
ошибка возникла, то вначале следует инсценировать ситуацию, при которой ошиб
ка проявляется. После этого можно начать поиск ошибки.
Tlgm: @it_boooks
finclude <stdio.h>
finclude <errno.h>
finclude <locale.h>
finclude <stdlib.h>
int rnain(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
long х = 100, у О;
char buf[256] = {О}, *р = NULL;
for ( ; ; ) ( // Бесконечный цикл
printf("y = "); // Выводим подсказку
fflush(stdout); // СбраСЫБаем буфер вывода
fflush(stdin); // Очищаем буфер ввода
р = fgets(buf, 256, stdin); // Получаем значение
if (!р) { // Проверяем успешность операции
puts("Оимбка при вводе");
exit(l);
р = NULL;
errno = О; // Сбрасываем флаг ошибки
у= strtol(buf, &р, О); // Преобразуем строку в число
if (!р 11 р == buf) ( // Проверяем успешность. операции
puts("Bы ввели не число!");
continue;
344 Глава n
continue;
linclude <stdio.h>
linclude <locale.h>
•ctefine МУ DEBUG 1
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
int х = 10;
lifdef МУ DEBUG
// Эта инструкция будет выnолнена только при отладке
printf("x = %d\n", х);
lendif
lif defined(МY_DEBUG) && МУ DEBUG == 1
// Эта инструкция будет выnолнена только если макрос
// МY_DEBUG равен 1
printf("x = %d\n", х);
lendif
printf("Этoт текст выводится в любом случае. х %d\n", х);
return О;
346 Глава 1�
#include <stdio.h>
int main(void) {
int arr[ARR_ROWS] [ARR_COLS] // 9
{ 1, 2, з, 41,
{ 5, 6, 7, 81
);
int i = О, j = О; // 13
for (i = О; i <= ARR_ROWS; ++i) { // 14
for (j = l; j < ARR_COLS; ++j) // 15
echo(а rr [ i] [ j ] ); // 16
printf("\n"); // 18
return О;
void echo(int х) { // 22
printf (" "); // 23
printf("%d", х); // 24
Прежде чем начать отладку, необходимо пометить строки внутри программы с п..-
мощью точек останова. Для добавления точки останова делаем строку активной. 1
затем в меню Run выбираем пункт Toggle Breakpoint или нажимаем комбинашn.:
клавиш <Shift>+<Ctrl>+<B>, - слева от строки появится кружок, обозначаюшкi
точку останова. Повторное действие убирает точку останова. Добавить точку ОСТ';!
нова можно еще быстрее. Для этого достаточно выполнить двойной щелчок лев...-i
кнопкой мыши слева от строки. Повторный двойной щелчок позволяет уда.11�
точку останова. Для того чтобы изменить свойства точки, временно отключить 11,_,.
удалить ее, следует щелкнуть на ней правой кнопкой мыши и в открывшемся кон
текстном меню выбрать пункт Toggle Breakpoint, DisaЫe Breakpoint или Enablr
Breakpoint. Давайте установим в нашей программе две точки останова: напроnп
строк 13 и 16.
Когда точки останова расставлены, можно начать отладку. Для этого в меню Ru
выбираем пункт Debug или нажимаем клавишу <Fl 1>. После запуска отладки ин
терфейс редактора Eclipse может поменяться кардинальным образом (рис. 11.:,
После запуска отладки выполнение программы прерывается, и отладчик ожи.1ает
дальнейших действий программиста. Инструкция, которая будет выполняться на
следующем шаге, помечается стрелкой слева от строки. Для того чтобы прерваn.
Tlgm: @it_boooks
отладку, в меню Run выбираем пункт Terminate или нажимаем комбинацию кла
виш <Ctrl>+<F2>.
�L11-i� г�-;;;,;;��- - --�:Eт«tblc:�. V �; i � -:�: -...1 l]t, '" • N :,,. •� •. [ ... "'->:' .J ••
� Тбt64с.С � = ['j
{5" б, 7, 8} � stdio.h
}; # ARR_Rows·
int i �-� j' � Q:,:::.: # ARR_COLS
for (i = 0; i <= ARR ROWS; ++i) { Н �cho(int): .,::,d
for (j = 1; j < ARR_COLS; ++j) { // 15 • m"in(void) ;п!
echo(arr[i] [j]); // 16 • «ho(lnt): \�1d
printf("'\n .. ); // 13
return О;
348 Глава 11
пункт Show View I VariaЫes. При отладке можно контролировать значения отдел,
ных переменных, а не всех сразу. Для этого следует добавить название переменН<W
в окне Expressions (рис. 11.4), щелкнув левой кнопкой мыши на надписи Add мw
expression. Для того чтобы отобразить это окно, в меню Window выбираем пунп
Show View I Expressions. Можно также выделить название переменной и из к�
текстноrо меню выбрать пункт Add Watch Expression. Давайте добавим в этоw
окне переменные i и j. Сейчас значения этих переменных не определены, поэто�
в поле Value отображаются произвольные значения.
ф
- □
=
\Х1"' \tan.ab;es �·:-; ·" fu§ EJ 1 =-:.,'
:..___J
L,,.
;.,.._; EJ
Name Туре Value
350 Глава�·
- □
0о Breakpoi ts �2 • �
� о Т e.st64c.c [line: 13]
� о Test64c.c[line:16]
ГЛАВА 12
352 Глава 12
if (fp)
fputws(L"cтpoкa", fp);
fclose(fp);
354 Глава 12
356 Глава 11
После режима и модификатора через запятую можно указать параметр ccs. Па�
метр принимает значения umcooE, uтF-B или uтF-lбLE. Если указано значение с,-�
или uтF-lбLE, то файл создается в соответствующей кодировке и в начало фай."11
вставляются служебные байты, сокращенно называемые ВОМ (Byte Order Mark -
метка порядка байтов). Если открываемый файл содержит ВОМ, то значение, ук.
занное в параметре ccs, игнорируется. При отсутствии параметра ccs автоматиче-
Tlgm: @it_boooks
tinclude <stdio.h>
tinclude <stdlib.h>
tinclude <locale.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
char buf[200] = {О};
FILE *fp = NULL;
// открываем файл на запись в текстовом режиме
fp = fopen("C:\\Ьook\\file.txt", "w");
if (!fp) { // Проверяем успешность
perror("Error"); // ВЫво.ш,�м сообщение об ошибке
exit(l); // Выхо.ш,�м при ошибке
358 Глава 12
Функция fputs < J записывает С-строку в файл. Для ускорения работы производите�
буферизация записываемых данных. Информация из буфера записывается в фай.,
полностью только в момент закрытия файла. Сбросить буфер в файл явным обра
зом можно с помощью функции fflush ( J. Прототип функции:
#include <stdio.h>
int fflush(FILE *File);
Пример:
fputc( 'Т', fp);
putc('S', fp);
360 Глава 12
362 Глава 12
Пример:
int х = О, count = О;
count = fwscanf(fp, L"%d", &х); // Символ & обязателен!!!
printf("%d\n", count);
printf("%d\n", х);
В качестве примера запишем строку в файл, а затем произведем посимвольное счи
тывание (листинг 12.2).
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
FILE *fp = NULL;
int ch = О;
fp = fopen("C:\\book\\file.txt", "w+"); // Чтение и запись
if (!fp) {
perror("Error");
exit(l);
printf("%c", (char)ch);
ch = fgetc (fp); // Считываем один символ
fclose(fp);
return О;
364 Глава 12
struct Point {
int х, у;
} pointl, point2;
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
FILE *fp = NULL;
// Запись в файл
fp = fopen("C:\\book\\file.txt", "wb"); // Бинарный режим!
if (!fp} {
perror("Error");
exit(l);
pointl.x = 10;
pointl.y = 20;
size t n = fwrite(&pointl, sizeof(struct Point), 1, fp);
printf("%d\n", (int)n); // 1
fflush(fp);
fclose(fp);
// Чтение из файла
fp = fopen("C:\\book\\file.txt", "rb"); // Бинарный режим!
if (!fp) {
perror("Error");
exit(l);
Tlgm: @it_boooks
366 Глава тJ
#include <stdio.h>
int _fseeki64(FILE *File, _int64 offset, int origin);
n�tиtir..12.4. ФaimJ·npolfЗвonыtoro
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
FILE *fp = NULL;
int arr[ J = { 1, 2, 3, 4, 5 } , х = О;
long pos = О;
fp = fopen("C:\\Ьook\\file.txt", "w+b"); // Бинарный режим!
if (!fp) {
perror("Error");
exit(1);
368 Глава fl
370 Глава r2
fputs("Stringl\nString2", stdout);
fflush(stdout);
Для того чтобы ввести в программу данные из файла, следует выполнить кома��
test.exe < file.txt
Первые два параметра аналогичны параметрам функции fopen (). Пример пере
направления стандартного вывода в файл:
setlocale(LC_ALL, "Russian_Russia.1251");
FILE *fp = NULL;
fp = freopen('-'C:\\book\\file.txt", "w", stdout);
if (!fp) exit(l);
printf("%s\n", "ПИшем в файл!");
Пример:
setlocale(LC_ALL, "Russian Russia.1251");
FILE *fp = fopen("C:\\Ьook\\file.txt", "w");
if (!fp) exit(l);
if (_dup2(_fileno(fp), _fileno(stdout)) == -1)
perror("He удалось перенаправить\n");
exit(l);
372 Глава 12
Функция возвращает значение о, если буфер успешно сброшен, или значение мак
роса EOF в случае ошибки. Пример:
printf("%s\n", "строка");
fflush (stdout);
fprintf(stderr, "%s\n", "сообщение об ошибке");
fflush (stderr);
Проблема в том, что это нестандартный способ сброса буфера, который не работает
даже в некоторых версиях Windows, а в других операционных системах вообще не
работает. Тем не менее это единственный способ, позволяющий сбросить буфер
при отсутствии ошибок. Способы, рассматриваемые далее, при пустом буфере
будут ожидать ввода пользователя.
Более универсальным способом является использование функции getchar(), нс
только при наличии ошибки. Как уже говорилось, при пустом буфере функuи�
будет ждать ввода пользователя, который нигде не сохранится! В этом примере -.,ы
дополнительно проверим статус потока и при ошибке сбросим флаг ошибки:
int х = О;
fflush(stdin); // Здесь использовать функцию getchar() нельзя!
int status = scanf("%d", &х);
if (status != 1) {
puts("Bы ввели не число");
// Сбрасываем флаг ошибки и очищаем буфер
if (feof(stdin) 1 1 ferror(stdiп)) clear rr(stdin);
0
int ch = О;
Tlgm: @it_boooks
Пример:
printf("%d\n", _flushall());
Tlgm: @it_boooks
ГЛАВА 13
Низкоуровневые потоки
ввода и вывода
В этой главе мы рассмотрим функции, предназначенные для выполнения низкС'
уровневых операций ввода и вывода. Такие функции не поддерживают буфериза
цию и позволяют читать и писать только байты.
□ _о_CREAT 1 _о_EXCL - создать файл. Если файл уже существует, то вернуrь код
376 Глава 13
exit(l);
378 Глава 13
Для явного сброса файла на диск предназначена функция _cornmi t(). Функция воз
вращает значение о, если операция успешно выполнена, или значение -1 - в с.� -
чае ошибки. Прототип функции:
#include <io.h>
int _cornmit(int fileHandle);
Рассмотрим несколько примеров. Откроем файл на запись и запишем в него стро�..�
(листинг 13.1). Если файл не существует, то создадим его. Если файл существует.
то очистим его (режим w в функции fopen()).
#include <stdio.h>
!finclude <string.h>
#include <locale.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
int rnain(void) {
setlocale(LC_ALL, "Russian Russia.1251");
char str[] = "Строка1\nСтрока2";
unsigned int str_len = (unsigned int) strlen(str);
int openFlag _O_WRONLY 1 _O_CREAT I О TRUNC О ТЕХТ;
int prnode = _S_IREAD I S IWRITE;
int fd _open("C:\\book\\file.txt", openFlag, prnode);
if (fd != -1) { // Проверяем успешность
int count = _write(fd, str, str_len); // Записываем строку
printf("count = %d\n", couпt);
if (count == -1) {
perror("He удалось записать строку");
Добавим еще одну строку в конец файла (листинг 13.2). Если файл не существует.
то создадим его. Если файл существует, то установим курсор на конец фай.1а
(режим а в функции fopen()).
#include <stdio.h>
#include <string.h>
Tlgm: @it_boooks
linclude <locale.h>
tlinclude <io.h>
linclude <fcntl.h>
linclude <sys/stat.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
char str[] = "\nСтрокаЗ";
unsigned int str_len = (unsigned int) strlen(str);
int openFlag = _O_WRONLY 1 _O_CREAT 1 _O_APPEND 1 _О_ТЕХТ;
int pmode = _S_IREAD 1 _S_IWRITE;
int fd = _open("C:\\Ьook\\file.txt", openFlag, pmode);
if (fd != -1) { // Проверяем успешность
int count = _write(fd, str, str_len); // Записываем строку
printf("count = %d\n", count);
if (count == -1) {
perror("He удалось записать строку");
tinclude <stdio.h>
linclude <locale.h>
linclude <io.h>
linclude <fcntl.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
char buf[BUF_LEN] = {О};
int openFlag = _O_ROONLY 1 _О_ТЕХТ;
int fd = _open("C:\\Ьook\\file.txt", openFlag);
if (fd != -1) {
int count = О;
while ( !_eof(fd) ) { // Пока не достигнут конец файла
count _read(fd, buf, BUF LEN - 1);
Tlgm: @it_boooks
380 Глава 13
if (count == -1) {
perror("He удалось nрочитать");
break;
close(fd);
382 Глава 13
int rnain(void) {
setlocale(LC_ALL, "Russian Russia.1251");
char str [] = "строка";
unsigned int str len = (unsigned int) strlen(str);
int openFlag = О RDWR _O_CREAT 1 _O_TEMPORARY I О BINARY;
int prnode = _S_IREAD I S IWRITE;
int fd = _open("C:\\book\\trnpfile.txt", openFlag, prnode);
if (fd != -1) {
_write(fd, str, str_len); // Записываем строку
рuts("Понаблюдайте за _каталогом C:\\book в течение пяти секунд");
fflush(stdout);
Sleep(S000); // Имитация работы с файлом
рuts("Закрываем файл");
_close(fd);
384 Глава fJ
Пример:
int dup_stdout = _dup(_fileno(stdout));
printf("%d\n", dup_stdout); // 3
printf("%d\n", fileno(stdout)); // 1
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <io.h>
int main(void) (
setlocale(LC_ALL, "Russian_Russia.1251");
// Сохраняем поток stdout
int dup_stdout = _dup(_fileno(stdout));
// Открываем файл
FILE *fp = fopen("C:\\book\\file.txt", "w");
Tlgm: @it_boooks
if ( ! fp) exit(1);
// Перенаправляем поток в файл
if (_dup2(_fileno(fp), 1) == -1)
perror("He удалось перенаправить\n");
exit(l);
ГЛАВА 14
В предыдущих двух главах были описаны способы работы с файлами в языке С..
Теперь мы переходим к рассмотрению вопросов переименования и удаления фа.
лов, получения информации о файлах, а также созданию, удалению и чтению кат.
логов. Следует сразу заметить, что реализация функций, описанных в этой главе.
зависит от операционной системы и компилятора. Рассматриваемые функции пр•
менимы для операционной системы Windows.
388 Глава�
char full_path[] = "C:\\book\\test.txt";
char drive[_МAX_DRIVE] = {О}, dir[_МAX_DIR] = {О};
char name[_МAX_FNAМE] = (О}, ехt[_МАХ_ЕХТ] = {О};
err = _splitpath_s(full_path, drive, _МAX_DRIVE,
dir, _МAX_DIR, name, _МАХ_FNАМЕ, ext, МАХ_ЕХТ);
if (err == О) {
printf("drive: '%s'\n", drive); // drive: 'С:'
printf("dir: '%s'\n", dir); // dir: '\book\'
printf("name: '%s'\n", name); / / пате: 'test'
printf("ext: '%s'\n", ext); // ext: '.txt'
Пример:
setlocale(LC_ALL, "Russian_Russia.1251");
char full_path[] = "C:\\book\\test.txt";
char drive[_МAX_DRIVE] = {О}, dir[_МAX_DIR] = {О};
char name[_МAX_FNAМE] = {О}, ехt[_МАХ_ЕХТ] = {О};
_splitpath(full_path, drive, dir, name, ext);
p·rintf("drive: '%s'\n", drive); // drive: 'С:'
printf("dir: '%s'\n", dir); // dir: '\book\'
printf("name: '%s'\n", name); // пате: 'test'
printf("ext: '%s'\n", ext); // ext: ' . txt'
Tlgm: @it_boooks
390 Глава 14
else {
printf("%s\n", "Файл успешно переименован");
r = _wrenarne(L"C:\\book\\file2.txt",
L"C:\\Ьook\\folderl\\file2.txt");
if (r == О) 1
printf("%s\n", "Файл успешно перемещен в каталог folderl");
else (
printf("%s\n", "Файл удален");
#include <stdio.h>
#include <io.h>
#include <locale.h>
int main(void) {
setlocale (LC_ALL, "Russian_Russia•.1251");
Tlgm: @it_boooks
392 Глава М
else perror("");
if ( _access(path, 4) != -1) {
printf("%s\n", "Файл доступен для чтения");
else perror("");
if ( _access(path, 6) != -1) {
printf("%s\n", "Файл доступен для чтения и записи");
else perror("");
else perror("");
return О;
#include <stdio.h>
#include <locale.h>
#include <io.h>
#include <sys/stat.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
char path [] = "С:\\Ьook\\fileЗ.txt";
if (_chmod(path, _S_IREAD) != -1) {
printf("%s\n", "ОК");
else perror("Error");
return О;
Tlgm: @it_boooks
394 Глава 1◄
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
char path[] = "C:\\book\\fileЗ.txt";
if (_access(path, О) == -1) {
рuts("Файл не существует");
return 1;
else
if (SetFileAttributes(path, attr I FILE_ATTRIBUTE_HIDDEN))
printf("%s\n", "ОК");
else puts("Error");
Tlgm: @it_boooks
else puts("Error");
return О;
Поле st_rnode содержит маску типа устройства и прав доступа. Возможна комбина
ция следующих флагов:
Tlgm: @it_boooks
396 Глава 14
#include <sys/stat.h>
#define s пмт OxFOOO /* Маска для типа файла */
#define S IFDIR Ох4000 /* Каталог */
#define S IFCHR Ох2000 /* Символьное устройство */
#define S IFIFO OxlOOO /* Какал */
#define S IFREG ОхВООО /* Обычный файл */
#define S IREAD Ох0100 /* Разрешение на чтение */
#define S IWRITE ОхООВО /* Разрешение на запись */
#define S IEXEC Ох0040 /* Разрешение на вьmолнение/поиск */
При использовании функций _stat64 о и _wstat64 о 64 бита выделяется и под даl)·.
и под размер файла. Прототипы функций:
#include <sys/types.h>
#include <sys/stat.h>
int stat64(const char *name, struct _stat64 *stat);
#include <sys/stat.h> /* или #include <wchar.h> */
int _wstat64(const wchar_t *name, struct stat64 *stat);
Объявление структуры _stat64:
struct _stat64 {
_dev_t st_dev;
_ino_t st_ino;
unsigned short st_rnode;
short st nlink;
short st_uid;
short st_gid;
_dev_t st_rdev;
_int64 st_size;
tirne64 t st_atirne;
tirne64 t st_rntirne;
tirne64 t st_ctirne;
};
-
Лмстинr :14.4; Пример мсnоnwованмя функции etat О
. -
#include <stdio.h>
#include <locale.h>
#include <tirne.h>
#include <sys/types.h>
#include <sys/stat.h>
int rnain(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
Tlgm: @it_boooks
else perror("Error");
return О;
398 Глава 14
#include <stdio.h>
#include <locale.h>
#include <time .h>
#include <sys/utime.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
struct _utimЬuf new_time;
new_time.actime = time(NULL) - 3600;
new_time.modtime = time(NULL) - 7200;
int err = _utime("C:\\book\\file.txt", &new_time);
if (err == О) {
printf("%s\n", "ОК");
else perror("Error");
// Текущ ее время
err = _utime("C:\\book\\fileЗ.txt", NULL);
if (err == О) {
printf("%s\n", "ОК");
else perror("Error");
return О;
iinclude <sys/uti.me.h>
�nt _futi.me(int fileHandle, struct _utirnЬuf *utirnЬuf);
В первом параметре указывается дескриптор открытого файла. Во втором парамет
ре передается адрес структуры utirnЬuf. Функция возвращает значение о, если оши
бок не бьmо, и значение -1 в случае ошибки. Код ошибки сохраняется в перемен
ной errno.
Пример:
int drive = _getdrive{);
int ch = drive - 1 + 'А';
printf{"%c\n", (char)ch); // С
400 Глава Н
Для того чтобы получить информацию о типе диска в Windows, можно воспользо
ваться макросом GetDriveтype() из WinAPI, который в зависимости от определеНИI
макроса UNICODE вызывает одну из следующих функций:
#include <windows.h>
UINT GetDriveTypeA(LPCSTR lpRootPathName);
UINT GetDriveTypeW(LPCWSTR lpRootPathName);
Пример:
UINT t = GetDriveType("C:\\");
if (t == DRIVE_REМOVAВLE) puts("DRIVE_REMOVAВLE");
else if (t DRIVE_FIXED) puts("DRIVE_FIXED");
else if (t DRIVE_REMOТE) puts("DRIVE_REMOТE");
else if (t DRIVE_CDROO) puts("DRIVE_CDROO");
else if (t DRIVE_RAМDISК) puts("DRIVE_RAМDISK");
else puts("He удалось определить тип диска");
Для того чтобы получить информацию о размерах диска в Windows, можно вос
пользоваться макросом GetDiskFreeSpaceEx() из WinAPI, который в зависимости от
определения макроса UNICODE вызывает одну из следующих функций:
#include <windows.h>
BOOL GetDiskFreeSpaceExA(LPCSTR lpDirectoryName,
PULARGE INTEGER lpFreeBytesAvailaЬleToCaller,
PULARGE_INTEGER lpTotalNumЬerOfBytes,
PULARGE_INTEGER lpTotalNumЬerOfFreeBytes);
BOOL GetDiskFreeSpaceExW(LPCWSTR lpDirectoryName,
PULARGE INTEGER lpFreeBytesAvailaЬleToCaller,
PULARGE INTEGER lpTotalNumЬerOfBytes,
PULARGE INTEGER lpTotalNumЬerOfFreeBytes);
Пример:
unsigned int64 free_bytes;
unsigned int64 total_bytes;
unsigned int64 total_free_bytes;
BOOL st = GetDiskFreeSpaceExA("C:\\",
(PULARGE_INTEGER) &free_bytes,
(PULARGE_INTEGER) &total_bytes,
(PULARGE_INTEGER) &total_free_bytes);
if (st) {
printf("free_bytes = %I64u\n", free_bytes);
printf("total_bytes = %I64u\n", total_bytes);
printf("total_free_bytes = %I64u\n", total_free_bytes);
402 Глава 14
if (_wmkdir(L"C:\\book\\folderB") О) {
printf("%s\n", "ОК");
if (_wrrndir(L"C:\\book\\folderB") О) {
printf ("%s\n", "ОК");
*include <stdio.h>
Hnclude <io.h>
*include <locale.h>
int rnain(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
struct _finddata_t info;
intptr_t h = _findfirst("C:\\book\\*", &in·fo);
if (h == -1) perror("Error");
else {
do {
printf("%-30s", info.name);
if (info.attrib & _A_SUBDIR)
pr:intf("%s", " _A_SUBDIR");
if (info.attrib & _A_RDONLY)
printf("%s", " _A_RDONLY");
if (info.attrib & _A_HIDDEN)
printf("%s", " _A_HIDDEN");
if (info.attrib & _A_SYSTEM)
printf("%s", " _A_SYSTEМ");
if (info.attrib & _A_ARCH)
printf("%s", " _A_ARCH");
printf("\n");
while(_findnext(h, &info) == О);
_findclose(h);
return О;
ГЛАВА 15
Потоки. и процессы
408 Глава 1!
#include <windows.h>
НANDLE CreateThread(LPSECURITY ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD START ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadld);
typedef void *НANDLE;
typedef void *LPVOID;
typedef unsigned _LONG32 DWORD;
#define _LONG32 long
typedef DWORD *LPDWORD;
Пример функции:
DWORD WINAPI thread func(LPVOID param) {
// Инструкции, вьmолняемые в отдельном потоке
return О;
410 Глава 15
#include <stdio.h>
#include <locale.h>
#include <windows.h>
#include <strsafe.h>
Tlgm: @it_boooks
int rnain(void) {
НANDLE arr_threads[NUМ_TНREADS]; // Массив с дескрипторами
DWORD arr_threads_id[NUМ_TНREADS]; // Массив с идентификаторами
setlocale(LC_ALL, "Russian_Russia.1251");
DWORD main_id = GetCurrentThreadid();
printf("Bxoд поток main: %lu\n", main_id);
// запускаем потоки
for (int i = О; i < NUМ_TНREADS; ++i) {
arr_threads[i) = CreateThread(NULL, О, thread_func, NULL, О,
&arr_threads_id[i));
if ( !arr_threads[i]) {
DWORD err = GetLastError();
printf("He удалось запустить поток. Error: %lu\n", err);
return 1;
else {
printf("Coздaн поток: %lu\n", arr_threads_id(i]);
fflush(stdout);
Tlgm: @it_boooks
412 Глава 15
414 Глава 15
#include <stdio.h>
#include <locale.h>
#include <windows.h>
else {
// Здесь обрабатываем 01Ш1бки
return О;
int rnain(void)
НANDLE arr_threads[NUМ_TНREADS]; // Массив с дескрипторами
setlocale(LC_ALL, "Russian_Russia.1251");
DWORD rnain_id = GetCurrentThreadid();
printf("Bxoд поток rnain: %lu\n", rnain_id);
Tlgm: @it_boooks
// Запускаем потоки
for (iпt i = О; i < NUМ_TНREADS; ++i) {
arr_threads[i] = CreateThread(NULL, О, thread_fuпc, NULL, О, NULL);
if (!arr_threads(i]) returп 1;
CloseHaпdle(mutex_priпt);
416 Глава 15
#include <stdio.h>
#include <locale.h>
#include <windows.h>
return О;
int main(void) {
НANDLE arr threads[NUМ_THREADS]; // Массив с дескрипторами
InitializeCriticalSection(&lock_print);
setlocale(LC_ALL, "Russian_Russia.1251");
DWORD main_id = GetCurrentThreadid();
printf("Bxoд поток main: %lu\n", main_id);
// Запускаем потоки
for (int i = О; i < NUM_THREADS; ++i) {
arr_threads[i] = CreateThread(NULL, О, thread_func, NULL, О, NULL);
if (!arr_threads[i]) return 1;
DeleteCriticalSection(&lock_print);
НА ЗАМЕТКУ
Для синхронизации потоков помимо рассмотренных способов можно использовать
и другие средства, например семафоры. За подробной информацией обращайтесь
к документации.
#include <stdio.h>
#include <locale.h>
#include <process.h>
#include <windows.h>
Tlgm: @it_boooks
418 Глава 15
return О;
int rnain(void) {
НANDLE arr threads[NUМ_TНREADS]; // Массив с дескрипторами
setlocale(LC_ALL, "Russian_Russia.1251");
DWORD rnain id = GetCurrentThreadid();
printf("Bxoд поток rnain: %lu\n", rnain id);
// Запускаем потоки
for (int i = О; i < NUM_THREADS; ++i) {
arr_threads[i] = (НANDLE)_beginthreadex(NULL, О, thread_func,
NULL, О, NULL);
if (!arr_threads[i]) return 1;
ние: функция потока ничего не возвращает. Для завершения работы потока можно
вызвать функцию _endthread(), которая закроет дескриmор потока, поэтому ис
пользовать фун.кцию CloseHandle() не нужно. Функция _endthread() вызывается
автоматически при выходе из пользовательской функции потока. Прототип функ
ции:
#include <process.h>
void _endthread(void);
#include <stdio.h>
#include <locale.h>
#include <process.h>
#include <windows.h>
int main(void)
uintptr_t handle;
setlocale(LC_ALL, "Russian_Russia.1251");
DWORD main_id = GetCurrentThreadid();
printf("Bxoд поток main: %lu\n", main id);
// Запускаем потоки
for (int i = О; i < NUМ_THREADS; ++i)
handle = _beginthread(thread_func, О, NULL);
if (handle <= О) return l;
Tlgm: @it_boooks
420 Глава 15
Если модель win32, то нужно при компиляции указать флаг -pthread или при воз
никновении ошибки установить компилятор с поддержкой модели posix:
C:\book>gcc -Wall -Wconversion -03 -finput-charset=cpl251
-fexec-charset=cp1251 -pthread -о test.exe test.c
struct pthread_attr_t
{
unsigned p_state;
void *stack;
size t s_size;
struct sched_param param;
};
Рассмотрим параметры функции pthread_create () :
□ th - указывается адрес переменной, в которую будет записан дескриптор соз
данного потока;
□ attr - задает атрибуты потока. Дnя использования атрибутов по умолчанию
следует указать значение NULL;
□ func - указатель на пользовательскую функцию, инструкции внутри которой
будут выполнены в отдельном потоке. Пример функции:
void *thread_func(void *param) {
// Инструкции, вьтолняемые в отдельном потоке
return NULL;
□ arg - задает данные, указатель на которые будет доступен через параметр param
функции thread_func( J • Если данные не передаются, то указываем значение
NULL.
422 Глава 15
int rnain(void)
pthread_t arr_threads[NUМ_TНREADS];
int arr_threads_id[NUМ_TНREADS];
int status = О;
setlocale(LC_ALL, "Russian_Russia.1251");
printf("Bxoд поток rnain\n");
// Запускаем потоки
for (int i = О; i < NUМ_'l'НREADS; ++i) {
arr_threads_id[i] = i + 1;
status = pthread_create(&arr_threads[i], NULL, thread func,
(void *)&arr_threads_id[i]);
if (status != О) {
printf("Ошибка pthread_create(): %d\n", status);
return 1;
424 Глава 15
#include <stdio.h>
#include <locale.h>
#include <pthread.h>
#include <unistd.h>
else {
// Эдесь обрабатываем ошибки
Tlgm: @it_boooks
return NULL;
int main(void) {
pthread_t arr_threads[NUМ TНREADSJ;
int arr_threads_id[NUМ_TНREADS];
int status = О;
setlocale(LC_ALL, "Russian_Russia.1251");
printf{"Bxoд поток main\n");
// Запускаем потою,:
for (int i = О; i < NUМ_THREADS; ++i) {
arr_threads_id(i] = i + 1;
status = pthread_create(&arr_threads(i], NULL, thread func,
(void *)&arr_threads_id(i]);
if (status != О) {
printf("Oшибкa pthread_create(): %d\n", status);
return 1;
НА ЗАМЕТКУ
Для синхронизации потоков помимо мьютексов можно использовать и другие средст
ва. За подробной информацией обращайтесь к документации.
Tlgm: @it_boooks
426 Глава 15
NULL
} ;
_execle("C:\\book\\test.exe", "C:\\book\\test.exe",
"-paraml", "-pararn2", NULL, my_env);
// Сюда мы попадем только при ошибке
printf("%s\n", "Не удалось запустить процесс");
□ v- передается массив указателей argList на аргумешы командной строки (по
следний элемент массива должен иметь значение NULL):
const char *my_args[4] =
"C:\\book\\test.exe",
"-paraml",
"-pararn2",
NULL
};
const char *my_env_[] =
"МYDIR=C:\\book\\",
NULL
};
_execve("C:\\book\\test.exe", my_args, my_env);
// Сюда мы попадем только при ошибке
printf("%s\n", "Не удалось запустить процесс");
Если процесс успешно запущен, то функции ничего не возвращают. Текущий про
цесс при этом будет замещен запущенным процессом. Инструкции после вызова
этих функций выполняются только в случае ошибки при запуске процесса.
Следует учитывать, что строки, описывающие аргументы, не должны содержать
пробелов. Если пробелы присутствуют, то нужно дополнительно заключить значе
ние в кавычки:
setlocale(LC_ALL, "Russian_Russia.1251");
_wexecl(L"C:\\book\\test.exe", L"C:\\book\\test.exe",
L"\" -param 1\'"', L"\"-param 2\"", NULL)-;
// Сюда мы попадем только при ошибке
wprintf(L"%s\n", L"He удалось запустить процесс");
Для запуска процесса можно таюке воспользоваться следующими функциями:
#include <process.h>
intptr_t _spawnl(int mode, const char *filename,
const char *argList, ...);
intptr_t _wspawnl(int mode, const wchar_t *filename,
const wchar_t *argList, .•.);
intptr_t _spawnle(int mode, const char *filename,
const char *argList, ...);
intptr_t _wspawnle(int mode, const wchar_t *filename,
con,st wchar_t *argList, ... );
intptr_t _spawnlp(int mode, const char *filen ame,
const char *argList, ... );
Tlgm: @it_boooks
428 Глава 15
Пример:
// Код программы test.exe приведен в листинге 17.3
_spawnl(_P_OVERLAY, "C:\\book\\test.exe", "C:\\book\\test.exe",
"-paraml", "-param2", NULL);
// Сюда мы попадем только при ошибке
printf("%s\n", "Не удалось запустить процесс");
Пример:
const char *my_args[4] =
"C:\\book\\test.exe",
"-paraml",
"-param2",
NULL
};
intptr_t st;
st = _spawnv(_P_WAIT, "C:\\Ьook\\test.exe", my_args);
// Сюда мы попадем после завершения программ,� test.exe
printf("Koд завершения: %I64d\n", st);
□ _P_NOWAIT и _P_NowArтo- новый процесс будет выполняться параллельно с те
кущим процессом. Функции в этом случае возвращают дескриптор процесса.
Определения макросов:
#define ·р NOWAIT 1
#define Р NOWAITO 3
Пример:
intptr_t d;
d = _spawnl(_P_NOWAIT, "C:\\book\\test.exe",
"C:\\book\\test.exe", "-paraml", "-param2", NULL);
// Сюда мы попадем не дожидаясь завершения прогр� test.exe
рrintf("Дескриптор процесса: %I64d\n", d);
□ _Р_ DETACH - новый процесс будет выполняться параллельно с текущим про
цессом в фоновом режиме без доступа к консоли и клавиатуре. Определение
макроса:
#define Р DETACH 4
ГЛАВА 16
Создание библиотек
#ifndef MODULEl Н
#define MODULEl Н
Tlgm: @it_boooks
#ifdef _cplusplus
extern "С" {
#endif
#ifdef _cplusplus
#endif
#endif /* MODULEl Н */
#include "modulel.h"
#ifndef MODULE2 Н
#define MODULE2 Н
#ifdef _cplusplus
extern "С" {
#endif
#1fdef _cplusplus
#endif
#endif /* MODULE2 Н */
Таким образом, мы создали два модуля, содержащих по одной функции. Можно все
функции объединить в один модуль, но в качестве примера компиляции нескольких
файлов будем использовать два модуля. Обратите внимание на содержимое заrоло-
Tlgm: @it_boooks
432 Глава 16
// Объявления функций.и т. д.
#ifdef _cplusplus
#endif
Теперь создадим объектные файлы:
C:\Users\Unicross>cd C:\book\mylib
C:\book\mylib>set Path=C:\msys64\mingw64\bin;%Path%
В этих командах мы указали флаг -с, чтобы создавались объектные файлы, а также
флаг -о, для указания названий объектных файлов. Последние две команды можно
объединить в одну команду:
C:\book\mylib>gcc -Wall -Wconversion -с -finput-charset=cpl251
-fexec-charset=cpl251 modulel.c module2.c
В результате компиляции были созданы два файла: module1 .о и module2.o. Эти два
объектных файла можно объединить в статическую библиотеку с помощью сле
дующей команды:
ar rcs liЬ<Название библиотеки>.а <Объектные файлы через пробел>
В этой команде мы воспользовались программой аг.ехе, расположенной в каталоге
C:\msys64\mingw64\Ьin. По существу программа создает архив, содержащий объект
ные файлы статической библиотеки. После названия программы указываются сле
дующие флаги:
□ r - замена существующих или вставка новых файлов в архив;
□ с - создать новый архив, если нужно;
□ s - создание индекса архива (действовать как программа ranlib.exe).
Далее указывается слово lib, после которого задается название статической биб
лиотеки и расширение а. В конце команды через пробел перечисляются объектные
файлы, добавляемые в архив. Давайте создадим статическую библиотеку с назва
нием modules 1 2:
ar rcs libmodules 1 2.а modulel.o module2.o
Tlgm: @it_boooks
#include <stdio.h>
#include <locale.h>
#include <rnodulel.h>
#include <rnodule2.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
рrintf("Функция surn_int(): %d\n", surn_int(5, 10));
рrintf("Функция surn_douЫe(): %.2f\n", surn_douЬle(З.125, 5.6));
return О;
C:\book>test.exe
Функция surn_int(): 15
Функция surn_douЬle(): 8,72
В первой команде мы переходим в каталог С:\Ьооk. Вторая команда преобразует
файл с исходным кодом в объектный файл. На этом этапе компилятору нужно знать
сигнатуры методов, которые находятся в заголовочных файлах. Указать местопо
ложение заголовочных файлов можно с помощью флага -r. В третьей команде вы
полняется сборка приложения. На этом этапе с помощью флага -L нужно передать
местоположение статических библиотек, а после флага -1 задать название подклю
чаемой библиотеки. Обратите внимание: название библиотеки указывается без
префикса нь и расширения файла. При использовании статических библиотек за
головочные файлы обычно размещают в каталоге include (например, C:\msys64\
mingw64\include), а сами библиотеки - в каталоге lib (например, C:\msys64\mingw64\lib).
Последние две команды можно объединить в одну:
C:\Ьook>gcc test.c -Wall -Wconversion -finput-charset=cpl251
-fexec-charset=cpl251 -IC:\book\mylib
-LC:\book\rnylib -lrnodules 1 2 -о test.exe
Tlgm: @it_boooks
434 Глава 11
#ifndef MODULEЗ Н
#define MODULEЗ Н
#ifdef _cplusplus
extern "С" {
#endif
Tlgm: @it_boooks
#ifdef _cplusplus
}
#endif
#endif /* MODULEЗ Н */
С Prbject - □
С Projкt
Create С project of selected type
••
" (:3, ExecutaЫe
Empty Project
Hello 't/orld ANSI С Project
Microsoft Visual С++
MinGWGCC
•
"' (:3, Shared Library
Empty Project
"' (:3, Static Library
1 • Empty Project1
v (:3, Makefile project
� Show project types and toolchains only if they are �upported on the platform
436 Глава 16
Переходим на вкладку GCC Archiver I General и в поле Archiver flags вводим зна
чение: rcs. На вкладке Build Artifact (рис. 16.2) можно настроить название библио
теки, префикс и расширение файла. Оставляем значения по умолчанию. Сохраняем
настройки проекта.
Cancel
•c:\cpp\projIOs\module_З\Rdeaп-"
!:
438 Глава 16
#include <stdio.h>
#include <locale.h>
#include <rnodulel .h>
#include <rnodule2 .h>
#include <rnoduleЗ.h>
int main(void) {
setlocale(LC_ALL, "Russian Russia.1251");
рrintf("Функция surn_int(): %d\n", surn_int(5, 10));
рrintf("Функция surn_douЫe(): %.2f\n", surn_douЫe(З.125, 5.6));
рrintf("Функция surn_long(): %ld\n", surn_long(бL, 5L));
return О;
C:\book\mylib>set Path=C:\msys64\mingw64\bin;%Path%
440 Глава 16
В разд. 1.1 мы создали каталог C:\cpp\lib, а в разд. 1.2 добавили его в пути поиска.
поэтому копируем библиотеку libmodules_ 1_2_0.dll в этот каталог и запускаем про
грамму из командной строки:
C:\book>test.exe
Функция sшn_int(): 15
Функция sшn_douЫe(): 8,72
442 Глава 16
о·
Environment
Logging ,----�---·т·ооl Settings Contain� S�i� 9�� Build Steps t Build Artifact j "!ill Binary Parsers, о· ◄:.1 •
Settings -
Tool Chain Editor • �; GCC AssemЫer !!
Libraries {-IJ
r-, С/С•+ General [� General '=====
i'
linux Т ools Path • ® GCC С Compiler
!\ mcdule_3_0
Project Natures � Dialect
Project Referenc:б � Preproceяcr
Run/Debug Settings � lncludes
Тask Repository � Optimization
TaskTags � Debugging
,.,. Validation 8 \.Yarnings
Wiki1ext 3 Miscellaneo-us
• @ MinGVI С Linker
8 General
!@ Librari61
@ Mi.scellaneo-us
@ 5ha_red library 5ettings
jf "C:\cpp\proje:cts\module_З_O\Release:"
- □
0
М Modules � \ 1 l@i
1
�
� С:\Windo�\SYSТEМ32\ntdll.dll
• C,\Windows\system32\kerne132.dll
• C:\Windows\system32\Kerne1Base.dll
• С:\Windows\system32\msvcrt.dll
• C:\cpp\lib\libmodule_З_O.dll
� C:\cpp\lib\libmodules_ 1 _2_0.dll
444 Глава 16
#include <stdio.h>
#include. <locale.h>
#include <windows.h>
int rnain(void)
setlocale(LC_ALL, "Russian_Russia.1251");
// Путь поиска библиотек
SetDllDirectoryA(".\\rnylib");
// Загружаем библиотеку
НINSTANCE hdll = LoadLibraryA("librnodules_l_2_0.dll");
if (hdll == NULL) {
рuts("Библиотека не найдена");
return 1;
psum_douЬle surn_douЬle
(psum_douЬle)GetProcAddress(hdll, "surn_douЬle");
if (surn_douЬle == NULL) {
puts("Функция surn _douЫe() не найдена");
return l;
)
// Используем функции
рrintf("Функция surn_int(): %d\n", surn_int(5, 10));
рrintf("Функция surn_douЬle(): %.2f\n", surn_douЫe(З.125, 5.6));
// Освобождаем ресурсы библиотеки
FreeLibrary(hdll);
return О;
C:\book>set Path=C:\msys64\mingw64\bin;%Path%
C:\Ьook>test.exe
Путь: C:\Ьook\rnylib\liЬmodules_1_2_0.dll
Функция surn_int(): 15
Функция surn_douЬle(): 8,72
В программе из листинга 16.9 мы указали относительный путь к библиотекам.
Библиотека libmodules_1_2_0.dll должна быть расположена во вложенном каталоге
mylib. Если библиотека не найдена, то в окне консоли получим следующее сооб
щение:
C:\Ьook>test.exe
Библиотека не найдена
При использовании функций LoadLibraryA( J и LoadLibraryw( J поиск динамической
библиотеки выполняется в следующем порядке:
□ в каталоге с запускаемой программой;
□ в каталоге, указанном с помощью функций SetDllDirectoryA() и
SetDllDirectoryW();
□ в системном каталоге Windows\System32. Путь к этому каталогу можно получить с
помощью функций GetSysternDirectoryA( J и GetSysternDirectoryW( J. Прототипы
функций:
#include <windows.h>
UINT GetSysternDirectoryA(LPSTR lpBuffer, UINT uSize);
UINT GetSysternDirectoryW(LPWSTR lpBuffer, UINT uSize);
Tlgm: @it_boooks
446 Глава 16
returп TRUE;
Tlgm: @it_boooks
ГЛАВА 17
Прочее
#include <stdio.h>
#include <locale.h>
#include <stdlib.h>
void funcl(void) {
puts( "Выход из программы: funcl");
Tlgm: @it_boooks
Прочее 449
void func2(void)
puts("Bыxoд из программы: func2");
int funcЗ(void) {
рuts("Выход из программы: funcЗ");
return О;
int rnain(void)
setlocale(LC_ALL, "Russian_Russia .1251");
if (atexit(funcl) != О) {
puts("He удалось зарегистрировать функцию funcl");
if (atexit(func2) != О) {
puts("He удалось зарегистрировать функцию func2");
if ( ! _onexit{funcЗ) ) {
puts("He удалось зарегистрировать функцию funcЗ");
450 Глава 17
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include <windows.h>
int main(void) {
setlocale(LC_ALL, "Russian_Russia.1251");
FILE *fp = _popen("dir", "rt");
Tlgm: @it_boooks
Прочее 451
if (fp)
char buf[200) = {О};
while (fgets(buf, 200, fp))
if (ferror(fp)) { // Проверяем наличие ошибок
printf("Error: %s\n", strerror(ferror(fp)));
exit(l);
}
// Преобразование windows-866 в windows-1251
OemToCharA(buf, buf);
printf("%s", buf);
else рuts("Ошибка");
return О;
#include <stdio.h>
return О;
C:\tюok>set Path=C:\msys64\mingw64\bin;%Path%
452 Глава 17
Прочее 453
454 Глава 17
Прочее . 455
456 Глава 17
Other objects
"C:\\book\\resources.o"
Прочее 457
IQ,111)(!1. Release □
■
� Бибпиоте.и
Видео
§1 Документы
2.7.012019 9:19
27.01.2019 9:19
Паnка �. файлами
Приложен►1е
� И:юбр<IЖ�ИЯ
Ji Музыка
4 ДомашН>IЯ группа
111 Компьютер
Мi10S(C:J
d, DATA(D:)
Oescription:
"
1дрр1у -ic1ose1 1.___с_..се1�__,
458 Глава 17
Заключение
ПРИЛОЖЕНИЕ
Файл Описание
Предметный указатель
# А ARCH 404
А HIDDEN 404
#define 51,63,101, 102,187,317,334,344, А NORМAL404
454 А RDONLY 404
#elif454 А SUBDIR 404
#else 455 А SYSTEM 404
#endif51,63,317,454,455 _abs64() 160
#error 455 _access() 391
#if454 _access_s() 392
#ifdef455 _atof_l() 171
#ifndef51,63,317,455 _atoi _l() 165
#include 45, 51, 62,316,318,454 _atoi64() 167
#pragma 51,63,455 _atol_l() 166
#undef103,454 _beginthread() 418
#waming 455 _beginthreadex() 417
_Bool 93, 156
_chdir() 402
_ chdrive() 399
DATE 103 _chmod() 393
_declspec( dllexport) 446 _close() 377
_FILE_ 103 _commit() 378
_inline 333 _countof() 183
_intl6 97 _create_locale() 214
_int32 97 _CRT_SECURE_NO_ WARNINGS 232,277
_int64 97 _ctime64() 306
_int8 97 _ctime64_s() 307
_LINE_ 103 _diffiime64() 304
_max() 163 _dup() 384
_min() 163 _dup2() 371,384
_mingw_printf{) 69,70, 100 _dupenv_s() 453
_SТDC_VERSION_ 9 _endthread() 419
_TlME_ 103 _endthreadex() 417
_time32_t 300 _eof() 380
_time64_t 300 _execl() 426
VERSION 9 _execle() 426
....,...wcserror() 340 _execlp() 426
_wcserтor_s() 340 _ execlpe() 426
Tlgm: @it_boooks
А clearerr_s() 363
clock() 300,311
abort() 89 clock t 300
аЬs() 160 CLOCKS PER SEC 311
acos() 165 CloseHandle() 410,413
acosf() 165 Cmake 28
acosl() 165 const 101,116,182,183,187,225,258,325
ar.exe 432 continue 153
ASCII 207 cos() 165
asctime() 305 cosf() 165
asctime_s() 306 cosl() 165
asin() 165 ср1251 209
asinf() 165 ср866 209
asinl() 165 CreateMutexA() 413
atan() 165 CreateMutexW() 413
atanf() 165 CreateThread() 407
atanl() 165 ctime() 306
atexit() 448 ctime_s() 307
atof() 171 С-строка 110,206
atoi() 165
atol() 166
atoll() 167
D
auto 104 -D REENTRANT 420
Debug 54
в default 147
DeleteCriticalSection() 415
ВОМ356 diffiime() 304
Ьоо193 div() 162
break 148, 149,151,153 DLL 438
bsearch() 200 DIIMain() 446
btowc() 255 do ... while 152,153
Byte Order Mark 356 douЫe 94, 158
DWORD 71
с
Е
-с 432
cl6rtomb() 298 Eclipse 29,34,47,64
c32rtomb() 298 else 142-144
calloc() 119,189 EnterCriticalSection() 415
case 147,148,149 enum 129
ceil() 164 EOF 354
ceilf() 164 ERANGE 339
ceill() 164 errno 339, 341,358,374
char 93,110,156,206,207,222,255 exit() 89, 449
CHAR BIT 93 EXIТ_FAILURE 61,89
CHAR МАХ93 EXIТ_SUCCESS 61,89
CHAR MIN 93 · ExitThread() 409
charl6 t 297 ехр() 161
char32 t 297 expf() 161
CharToOemA() 266 expl() 161
CharToOemBuffA() 266 extem 104
clearerr() 363
Tlgm: @it_boooks
F fseek() 366
fsetpos() 366
F ОК 391 ftell() 365
fabs() 160 -fwide-exec-charset 244
fabsf() 160 fwprintf() 360
fabsl() 160 fwprintf_s() 360
false 93 fwrite() 363
fclose() 353 fwscanf() 362
feof() 363
ferror() 363
-fexec-charset 38, 39,47,244 G
fflush() 71, 75,85,358,372 g++.exe 18
fgetc() 360 gcc.exe 9,15,18,20,47
fgetpos() 365 gdb.exe 345
fgets() 77, 358,361 getc() 360
fgetwc() 360 getchar() 71, 77,85
fgetws() 261, 361 GetCurrentThread() 41О
FILE 351,352 GetCurrentThreadld() 41 О
FILE_ATTRIBUTE HIDDEN 394 GetDiskFreeSpaceEx() 400
FILENAME_MAX 354 GetDiskfreeSpaceExA() 400
-finput-charset 38,39,47,244
GetDiskFreeSpaceExW() 400
tloat 94,158
GetDriveType() 400
floor() 164
GetDriveTypeA() 400
tloorf() 164
GetDriveTypeW() 400
tloorl() 164
fmax() 163 getenv() 452
fmaxf() 163 getenv_s() 452
fmaxl() 163 GetExitCodeThread() 409
fmin() 163 GetFileAttributes() 394
fminf() 163 GetLastError() 409
fminl() 163 GetModuleFileNameA() 444
fmod() 162 GetModulefileNameW() 444
fmodf() 162 GetProcAddress() 444
fmodl() 162 gets() 77
fopen() 351,356 GetSystemDirectoryA() 445
FOPEN_MAX 352 GetSystemDirectoryW() 445
fopen_s() 352 getwc() 360
for 150,151,185,226,259 getwcщir() 248,261
-fPIC 439 GetWindowsDirectoryA() 446
fpos_t 365 GetWindowsDirectoryWO 446
fprintf() 358,359 Glade 28
fprintf_s() 360 gmtime() 301
fputc() 358 gmtime_s() 302
fputs() 358,359 goto 154
fputwc() 359 GTK+ 28
fputws() 359
fread() 364
free() 119,189
н
FreeLibrary() 443 HUGE_VAL 172
freopen() 370 Нулевой указатель 112
freopen_s() 371
fscanf() 361
Tlgm: @it_boooks
iswupper() 253
iswxdigit() 251
-151,433 isxdigit() 218
if 142,144,146
imaxabs() 161 J
imaxdiv() 163
INFINITE 409 Java 29
INFINITY 134,179 Java Development Kit 29
InitializeCriticalSection() 415 Java Runtime Environment 29
inline 333 JDK 29
int 93,97,156 JRE29
INT_MAX 94
INT MIN 94
INТI 6_МАХ98 к
INТ16 MIN 98 koi8- r 209
intl 6 t 98
INT32_MAX 98
INT32_MIN 98 L
int32_t 98 -1 433
INT64 МАХ98 -L 433
INT64 MIN 98 labs() 160
int64_t 98,99 LC ALL 88,212
INT8 МАХ98 LC=COLLATE 88,212
INT8_MIN 98 LC СТУРЕ 88,212
int8 t 98 LC-MONETARY 88,212
INТМАХ_МАХ170 LC=NUMERIC 88,212
INТМAX_MIN 170 LC_ТIME 88,212
intmax t 170 lconv 215
isalnum() 219 ldiv() 162
isalpha() 219 LeaveCriticalSection() 415
isЫank() 221 llabs() 160
iscntrl() 221 lldiv() 162
isdigit() 218 LLONG_МАХ97
isgraph() 221 LLONG_MIN 97
isinf() 180 LoadLibraryA() 443
islower() 220 LoadLibraryW() 443
isnan() 180 localeconv() 215
iso8859- 5 209 localtime() 302
isprint() 221 localtime_s() 303
ispunct() 220 log() 161
isspace() 219 logl0() 161
isupper() 220 logl0f() 161
iswalnum() 252 logl 01() 161
iswalpha() 252 logf() 161
iswЫank() 254 logl() 161
iswcntrl() 254 long 69,96,156
iswdigit() 251 long douЫe 70, 97, 158
iswgraph() 253 long int 96
iswlower() 252 long long 69,96, 156
iswprint() 253 long long int 96
iswpunct() 253 LONG_LONG_MAX 97
iswspace() 252 LONG_LONG_MIN 97
Tlgm: @it_boooks
LONG_MAX96
LONG MIN 96
N
-lpthread 420 NAN 134,179,180
Lucida Console 86 nanosleep() 31О
L-строки 244 Notepad++ 19
NULL 79,112, 113
м
M_l_PI 159
о
М 2 PI 159 -о 47,432
M_2_SQRTPI 160 -03 47
М Е 159 OemToCharA() 266
М LNl0 160 OemToCharBuffA() 266
М LN2 159
М LOGl 0E 159
М LOG2E 159 р
М PI 159 РАТН 12,17,37,426,446,452
М_Р1_2 159 pause 85
М PI 4 159 perror() 341,358
M_SQRТl_2 160 pow() 161
M_SQRT2 160 powf() 161
mac-cyrillic 209
powl() 161
main() 45,60,61,82,451
PRldl 6 99
Make 28
PRld32 99
make.exe 23
malloc() 118,119,189 PRld64 99
mbrtoc16() 298 PRid8 99
mbrtoc32() 299 printf() 45,65,66,324,344
mbstowcs() 263,265 PRlul 6 99
mbstowcs_s() 264 PRlu32 99
memchr() 233 PRlu64 99
memcmp() 203,241 PRlu8 99
memcpy() 201 -pthread 420
memcpy_s() 202 pthread_cancel() 422
memmove() 202 pthread_create() 420
memmove_s() 202 pthread_exit() 421,422
memset() 235 pthreadjoin() 42-1,422
Microsoft Visual С++ 42 pthread_mutex_destroy() 424
MinGW 14 pthread_mutex_init() 423
MINGW_HOME 37,41 pthread_mutex_lock() 424
mingw32.exe 24 pthread_mutex_trylock() 424
mingw64.exe 24 pthread_mutex_unlock() 424
MinGW-W64 20,24 PTHREAD_THREADS_MAX 421
mktime() 303 putc() 358
modf() 162 putchar() 65
modff() 162 puts() 65
modfl() 162 putwc() 359
MSYS 14 putwchar() 247
MSYS_HOME 37, 41
MSYS2 24
msys2.exe 24 Q
MultiByteToWideChar() 267 qsort() 196
Tlgm: @it_boooks
R sprintf_s() 176,242
sqrt() 161
R ОК 391 . sqrtf() 161
rand() 177 sqrtl() 161
RAND_МAX 177 srand() 177
ranlib.exe432 sscanf() 173
realloc() 122,189 static104,105,327,333
register 104 -static434
Release 54 -std9
ReleaseMutex() 413 stderr 341,358,369,382, 383
remove() 390 STDERR FILENO 382
rename() 390 stdin75,78,369, 382
retum 45,60,312,313,328 STDIN FILENO 382
rewind() 363,366 stdout71,369,382,384
round() 164 STDOUT_FILENO 382
roundf() 164 strcat() 229
roundl() 164 strcat_s() 230
strchr() 232
s strcmp() 237
strcoll() 238
scanf() 72,343 strcpy() 223,228
SCHAR_MAX 95 strcpy_s() 223,228
SCHAR MIN 95 strcspn() 234
SEEK_CUR366,381 strerror() 339,358
SEEK_END 366,381 strerror_s() 339
SEEK_SET366,381 strftime() 307,308
SetDIIDirectoryA() 443 strlen() 225-227
SetDIIDirectoryW() 443 strncat() 230
SetFileAttributes() 394 strncat_s() 230
setlocale() 73,74,78,88,212,247,260 stmcmp() 237
-shared 439 stmcpy() 228
short69,95, 156 stmcpy_s() 229
short int 95 stmlen() 228
SHRT МАХ96 strpbrk() 233
SHRT MIN96 strrchr() 233
signed 94,156 strspn() 234
signed char 94 strstr() 234
signed int 95 strtod() 171
signed long 96 strtof() 172
signed long int96 strtoimax() 170
signed long long96 strtok() 231
signed long long int 96 strtok _ s() 231
signed short 95 strtol() 168
signed short int 95 strtold() 172
sin() 165 strtoll() 168
sinf() 165 strtoul() 168
sinl() 165 strtoull() 168
size_t 99,225,258 strtoumax() 170
sizeof99,118,124,183,246 struct123,126
sleep() 309 strxfrm() 239
Sleep() 71,310,311,410 switch 147, 148
sprintf() 176, 242 swprintf_s() 296
Tlgm: @it_boooks
А Вывод данных 65
Выполнение программы по шагам 345
Абсолютное значение l 61 Вычитание 133
Адрес переменной 112
Адресная арифметика 116
Арккосинус 165
г
Арксинус 165 Глобальная переменная 105
Арктангенс 165
Б
д
Дата 300
Бесконечность l 34, 179 ◊ форматирование 305
Библиотека 430 Двоичные числа 175,295
◊ динамическая 438 Декремент 134
◊ статическая 430 Деление 133
Бинарный поиск 199 Десятичные числа 157
Битовые поля 126 Десятичный логарифм 161
Блок 106, 144,155 Динамическая библиотека 438
Динамическое выделение памяти 118
в Директивы препроцессора 454
Диск 399
Ввод данных 71
Вещественные числа 158
◊ точность вычислений 158
з
Возведение в степень l 61 Завершение выполнения программы 89, 448
Восьмеричные числа 157,175,295 Заголовочный файл 50
Время 300 Значок приложения 455
Встраиваемая функция 333
Tlgm: @it_boooks
и ◊
◊
многомерный109,190
объявление181
Именование переменных 92 ◊ перебор элементов185
Инициализация переменных 100 ◊ переворачивание204
Инкремент134 ◊ поиск значения 198
◊ получение значения элемента 184
к ◊
◊
проверка наличия значения 198
размер183
Каталог401 ◊ сортировка195
◊ перебор объектов403 ◊ составной литерал188,320
◊ преобразование пути386 ◊ сравнение203
◊ создание402 ◊ указателей114,189
◊ текущий рабочий354 ◊ указатель на элемент186
◊ удаление402 Метка порядка байтов356
Квадратный корень161 Многострочный комментарий64
Ключевые слова92 Мьютекс413,423
Кодировка49,86,263,265
◊ файла с программой244
Командная строка14 н
Комментарии63
Натуральный логарифм161
◊ многострочные64 Нулевой указатель 79
◊ однострочные 63
Компилятор14
Консоль14 о
◊ предотвращение закрытия окна 84
Области видимости переменных 105
Консольное приложение 44
Объединение127
Константа1 О1
Объявление
Косинус165
◊ переменной91
л ◊ функции312
Однострочный' комментарий63
Операторы133
Логарифм
◊ ?: 146
◊ десятичный161
◊ break 153
◊ натуральный 161
Локаль212 ◊ continue153
Локальная переменная105 ◊ do...while152
◊ for149
м ◊ goto154
◊ if142
Макроопределение101,334 ◊ switch147
Макрос101,334 ◊ while152
Массив107,181 ◊ ветвления142
◊ двумерный109,190 ◊ выбора147
◊ динамический189 ◊ декремента134
◊ доступ к элементам184 ◊ запятая138
◊ зубчатый121 ◊ инкремента 134
◊ изменение значения элемента 184 ◊ математические133
◊ инициализация181 ◊ побитовые135
◊ количество элементов 183 ◊ приоритет выполнения 141
◊ копирование элементов 201 ◊ присваивания138
◊ максимальное значение193 ◊ сравнения139
◊ минимальное значение193 ◊ ЦИКЛЫ 149
Tlgm: @it_boooks
Определение р
◊ переменной 91
◊ функции 60,312 Разыменование указателя 112
Остаток от деления 134,162 Рекурсия 332
Отладка программы 345
Ошибки 336
◊ времени выполнения 337
с
◊ логические 337,341 Символы 206
◊ отладка программы 345 ◊ широкие 245
◊ поиск 341 Синус 165
Синхронизация потоков 412,423
◊ синтаксические 336
Системные команды 449
◊ типы ошибок 336 Системные переменные 451
Сложение 133
п Сортировка массива 195
Специальные символы 206
Пароль Спецификаторы хранения 104
◊ ввод 80 Статическая библиотека 430
◊ генерация 178 Статическая переменная 105,326,327
Переменная 91 Статическая функция 327
◊ автоматическая 104 Строки 11О,206
◊ адрес 112 ◊ длина 225,258
◊ глобальная 58,105 ◊ доступ к символам 224,257
◊ именование 92 ◊ замена 234,279
◊ инициализация 100 ◊ из широких символов 244
◊ локальная 58, 105 ◊ кодировки 263, 265
◊ конкатенация 223, 257
◊ область видимости 105
◊ поиск 232,277
◊ объявление 91
◊ регистр символов 216,249
◊ объявление в другом месте 104
◊ специальные символы 206
◊ окружения 451
◊ сравнение 237,282
◊ регистровая l 04 ◊ форматирование 242
◊ статическая 104,105,326 Структура 123
Перенаправление потоков 369,384
Перечисление 129
Потоки ввода/вывода 369 т
Потоки управления 407 Тангенс 165
◊ POSIX420 Текущий рабочий каталог 354
◊ WinAPI 407 Типы данных 93
◊ синхронизация 412,423 ◊ приведение 13 1
Препроцессор 454 Точки останова 346
Приведение типов 130, 131
Приоритет выполнения операторов 141
Присваивание 100
у
Прототип функции 59,312 Указатель 111
Процесс 426 ◊ void* 331
Псевдоним 100 ◊ арифметические и логические операции
Пузырьковая сортировка 195 116
Путь ◊ на структуру 126
◊ абсолютный 354 ◊ на функцию 330
◊ относительный 354 ◊ нулевой 112
◊ преобразование 386 ◊ разыменование 112
Tlgm: @it_boooks
ф ц
Целые числа 156,157
Файл 351 Цикл
◊ временный 367,381 ◊ do ...while 152
◊ ДВОИЧНЫЙ 363 ◊ for 149
◊ закрытие 353,377 ◊ while 152
◊ запись 358, 377 ◊ переход на следующую итерацию 153
◊ низкоуровневые потоки 374 ◊ прерывание 153
◊ открытие 351,374
◊ переименование 390
◊ перемещение 390 ч
◊ получение информации 395 Числа 156
◊ права доступа 393 ◊ NAN 179
◊ преобразование пути 386 ◊ бесконечность 179
◊ произвольного доступа 365, 380
◊ вещественные 158
◊ режим открытия 356,374 D точность вычислений 158
◊ ресурсов 456
◊ восьмеричные 157,175,295
◊ скрытый 394
◊ двоичные 175,295
◊ удаление 390
◊ десятичные 157
◊ чтение 360, 377
◊ округление 164
Файл с программой 49
Факториал 332 ◊ основные функции 160
Форматирование программы 143,341 ◊ преобразо�ание 165,286
Функция 312 ◊ преобразование в строку 174,294
◊ возврат значения 328 .◊ случайные 177
◊ встраиваемая 333 ◊ тригонометрические функции 165
◊ объявление 312 ◊ целые 156,157
◊ определение 312 ◊ шестнадцатеричные 157,175,295
◊ передача данных произвольного типа
331
◊ передача массива 320
ш
◊ передача параметров 318 Шестнадцатеричные числа 157, i75, 295
◊ передача строки 320
◊ переменное число параметров 324
◊ прототип 312
э
◊ расположение определений 315 Экспонента 161
Tlgm: @it_boooks
Культин Н.
С/С++ в задачах и примерах
www.bhv.ru
3-е изд.
Отдел оптовых поставок:
e-mail: opt@bhv.ru
Более 200 задач
Никита Куnьтмн
• Ввод данных с клавиатуры
• Вывод на экран
• Циклы
• Сортировка и поиск в массиве
• Рекурсия
t:
Исходные тексты
nрог,мuм
с ко1t1менm•риями • Операции с файлами
Спраео,,,ник по языку
программирования
Описание
• Работа с объектами
сm•нд.ртных фуниций
1if:
Сборник примеров и задач для самостоятельного решения по программированию
на языке С/С++ охватывает практически все разделы начального курса программи
рования: от задач консольного ввода/вывода, использования циклов и операций
с массивами до работы со строками, файлами и объектами. Примеры представлены
в виде хорошо документированных исходных текстов программ. Книга содержит
справочник - описание основных типов данных, операторов и наиболее часто ис
пользуемых функций. Адресована студентам, школьникам старших классов и всем
тем, кто изучает программирование в учебном заведении или самостоятельно.
В третьем издании добавлены и обновлены примеры.
Никита Борисович Культин, кандидат технических наук, доцент Санкт-Петербурrскоrо
rосударственноrо политехнического университета, rде читает курс «Теория и технология
программирования>>. Автор кнш по программированию в Delphi, Microsoft Visual С++,
Microsoft Visual С# и др., которые BЫIWIИ общим тиражом более 350 тыс. экземпляров.
Tlgm: @it_boooks
Полубенцева М.
С/С++. Процедурное программирование
www.bhv.ru
-·--
• Динамическое выделение памяти
---
-·- ,__ • Ссылки
---
----·i • Функции
• Структуры и объединения
В книге описан базовый синтаксис языка Python 3: типы данных, операторы, усло
вия, циклы, регулярные выражения, встроенные функции, классы и объекты, итера
торы и перечисления, обработка исключений, часто используемые модули стан
дартной библиотеки. Даны основы SQLite, описан интерфейс доступа к базам
данных SQLite и MySQL, в том числе посредством ODBC. Рассмотрена работа
с изображениями с помощью библиотек Pillow и Wand, получение данных из Ин
тернета и работа с архивами различных форматов. Книга содержит более двухсот
практических примеров, помогающих начать программировать на языке Python са
мостоятельно. Весь материал тщательно подобран, хорошо структурирован и ком
пактно изложен, что позволяет использовать книгу как удобный справочник.
Прохоренок Николай Анатольевич, профессиональный программист, имеющий большой практиче
ский опыт создания и продвижения динамических сайтов с использованием HTML, JavaScript, РНР,
Pert и MySQL, автор книг «HTML, JavaScript, РНР и MySQL. Джентльменский набор WеЬ-мастера»,
<<Разработка WеЬ-сайтов с помощью Perl и MySQL», «Python 3 и PyQt. Разработка приложений»,
«Python. Самое необходимое».
Дронов Владимир Александрович, профессиональный программист, писатель и журналист, работает
с компьютерами с 1987 года. Автор более 20 популярных компьютерных книг, в том числе «НТМL 5,
CSS 3 и Web 2.0. Разработка современных WеЬ-сайтов», «РНР 5/6, MySQL 5/6 и Dreamweaver CS4. Раз
работка интерактивных WеЬ-сайтов», «JavaScript и AJAX в WеЬ-дизайне», «Windows 8: разработка
Меtrо-приложений для мобильных устройств» и книг по продуктам Adobe Flash и Adobe Dreamweaver
различных версий. Его статьи публикуются в журналах «Мир ПК►► и «ИнтерФейс» (Израиль) и интер
нет-порталах «IZ City» и «TheVista.ru».
Tlgm: @it_boooks
Прохоренок Н.
Основы Java
www.bhv.ru 2-е изд.
Если вы хотите научиться программировать на языке Java, то эта книга для вас.
В книге описан базовый синтаксис языка Java: типы данных, операторы, условия,
циклы, регулярные выражения, лямбда-выражения, ссылки на методы, объектно
ориентированное программирование. Рассмотрены основные классы стандартной
библиотеки, получение данных из сети Интернет, работа с базой данных MySQL.
Во втором издании приводится описание большинства нововведений: модули, ин
терактивная оболочка JShell, инструкция var и др.
Книга содержит большое количество практических примеров, помогающих начать
программировать на языке Java самостоятельно. Весь материал тщательно подоб
ран, хорошо структурирован и компактно изложен, что позволяет использовать
книгу как удобный справочник.
Прохоренок Николай Анатольевич, профессиональный программист, автор книг «HTML,
JavaScript, РНР и MySQL. Джентльменский набор WеЬ-мастера», «Python 3. Самое необхо
димое», «Python 3 и PyQt. 5. Разработка приложений», «OpenCV и Java. Обработка изобра
жений и компьютерное зрение» и др.
Tlgm: @it_boooks