Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

spz / lect / lekc4_2_sem_analiz

.pdf
Скачиваний:
19
Добавлен:
23.02.2016
Размер:
291.05 Кб
Скачать

Системне програмне забезпечення.

1

Синтаксичний та семантичний аналіз.

1.Синтаксично-керований переклад.

2.Основні принципи роботи синтаксичних аналізаторів.

3.Дерево розбору.

4.Перетворення дерева розбору в дерево операцій.

5.Призначення семантичного аналізу.

6.Етапи семантичного аналізу.

7.Ідентифікація лексичних одиниць мов програмування.

8.Розподіл пам’яті.

Синтаксичний аналізатор (синтаксичний розбір) – це частина компілятора, яка відповідає за виявлення основних синтаксичних конструкцій вхідної мови. У задачу синтаксичного аналізу входить: знайти й виділити основні синтаксичні конструкції і тексті вхідної програми, встановити тип та перевірити правильність кожної синтаксичної конструкції і, нарешті, представити синтаксичні конструкції у виді, зручному для подальшої генерації тексту результуючої програми.

В основі синтаксичного аналізатору лежить разпізнавач тексту вхідної програми на основі граматики вхідної мови. Як правило, синтаксичні конструкції мов програмування можуть бути описані за допомогою КВграматик (Контекстно-вільні мови), рідше зустрічаються мови, що можуть бути описані за допомогою регулярних граматик (Регулярні мови). Найчастіше регулярні граматики застосовуються до мов ассемблера, а мови високого рівня побудовані на основі синтаксису КВ-мов.

Розпізнавач дає відповідь на питання про те, належить чи ні ланцюжок вхідних символів заданій мові. Однак, як і у випадку лексичного аналізу, задача синтаксичного розбору не обмежується тільки перевіркою належності ланцюжка заданій мові. Необхідно виконати всі перераховані вище задачі, які повинен вирішити синтаксичний аналізатор. У такому варіанті аналізатор уже не є

різновидом Мп-автомата — його функції можна трактувати ширше. Синтаксичний аналізатор

повинний мати деяку результуючу мову, за допомогою якої він передає наступним фазам компіляції всю інформацію про знайдені і розібрані синтаксичні структури. У такому випадку він уже є перетворювачем з магазинною пам'яттю — Мп-перетворювачем1 .

Синтаксичний розбір — це основна частина компілятора на етапі аналізу. Без виконання синтаксичного розбору робота компілятора безглузда, у той час як лексичний розбір у принципі є необов'язковою фазою. Усі задачі по перевірці синтаксису вхідної мови можуть бути вирішені на етапі

1 Во всех разобранных выше в главах «Контекстно-свободные языки» и «Классы КС-яэыков и грамматик» примерах результатом работы МП-автомата являлся не только ответ на вопрос о принадлежности входной цепочки заданному языку («да» или «неті»), но и последовательность номеров правил грамматики, использованных для построения входной цепочки. Затем на основе этих правил строились цепочка вывода и дерево вывода. Поэтому, строго говоря, все рассмотренные ранее примеры МП-автоматов являлись не только МПавтоматами, но и МП-преобразователями.

Системне програмне забезпечення.

2

синтаксичного розбору. Сканер тільки дозволяє звільнити складний за структурою синтаксичний аналізатор від рішення примітивних задач по виявленню і запам'ятовуванню лексем вхідної програми.

Виходом лексичного аналізатора є таблиця лексем (чи ланцюжок лексем). Ця таблиця утворить вхід синтаксичного аналізатора, що досліджує тільки один компонент кожної лексеми — її тип. Інша інформація про лексеми використовується на більш пізніх фазах компіляції при семантичному аналізі, підготовці до генерації і генерації коду результуючої програми. Синтаксичний аналіз (чи розбір) — це процес, у якому досліджується таблиця лексем і встановлюється, чи задовольняє вона структурним умовам, явно сформульованим у визначенні синтаксису мови.

Синтаксичний аналізатор сприймає вихід лексичного аналізатора і розбирає його відповідно до граматики вхідної мови. Однак і граматиці вхідної мови програмування звичайно не уточнюється, які конструкції варто вважати лексемами. Прикладами конструкцій, що звичайно розпізнаються під час лексичного аналізу, служать ключові слова, константи й ідентифікатори. Але ці ж конструкції можуть розпізнаватися і синтаксичним аналізатором. На практиці не існує твердого правила, що визначає, які конструкції повинні розпізнаватися на лексичному рівні, а які треба залишати синтаксичному аналізатору. Звичайно це визначає розроблювач компілятора виходячи з технологічних аспектів програмування, а також синтаксису і семантики вхідної мови. Принципи

взаємодії лексичного і синтаксичного аналізаторів були розглянуті нами і розділі «Лексичні аналізатори (сканери). Принципи побудови сканером».

Нижче розглянуті технічні аспекти, зв'язані з реалізацією синтаксичних аналізаторів для використання результатів їхньої роботи на етапі генерації коду. Проте основу будь-якого синтаксичного аналізатора завжди складає розпізнавач, побудований на основі якого-небудь класу Кс-грамматик Тому головну роль у тім, як функціонує синтаксичний аналізатор і який алгоритм лежить у його основі, грають принципи побудови розпізнавачей КВ-мов, розглянуті в главі «Контекстно-вільні мови». Без застосування цих принципів неможливо виконати ефективний синтаксичний розбір вхідної мови.

Дерево розбору. Перетворення дерева розбору в дерево операцій

Результатом роботи розпізнавача КВ-грамматики вхідної мови є послідовність правил граматики, застосованих для побудови вхідного ланцюжка. По знайденій послідовності, знаючи тип розпізнавача, можна побудувати ланцюжок виведення або дерево виведення. У цьому випадку дерево виведення виступає як дерево синтаксичного розбору і являє собою результат роботи синтаксичного аналізатора в компіляторі.

Однак ні ланцюжок виведення, ні дерево синтаксичного розбору не є метою роботи компілятора. Дерево виведення містить масу надлишкової інформації, що для подальшої роботи компілятора не потрібно. Ця інформація містить у собі всі нетермінальні символи, що містяться у вузлах дерева, — після того як дерево побудоване, вони не несуть ніякого значущого навантаження і не представляють для подальшої роботи цікавості. Для повного

Системне програмне забезпечення.

3

представлення про тип і структуру знайденої і розібраної синтаксичної конструкції вхідної мови в принципі досить знати послідовність номерів правил граматики, застосованих для її побудови.

Однак форма представлення цієї достатньої інформації може бути різною як у залежності від реалізації самого компілятора, так і від фази компіляції. Ця форма називається внутрішнім представленням програми (іноді використовуються також терміни «проміжне представлення» чи «проміжна програма»).

Тут піде мова тільки про зв'язні спискові структури, що представляє дерева, тому що імені, ці структури найбільше просто й ефективно будувати на етапі синтаксичного розбору, керуючись правилами і типом виведення, породженого синтаксичним розпізнавачем.

У синтаксичному дереві внутрішні вузли (вершини) відповідають операціям, а листи являють собою операнди. Як правило, листи синтаксичного дерева зв'язані з записами в таблиці ідентифікаторів. Структура синтаксичного дерева відображає синтаксис мови програмування, на якому написана вихідна програма.

Синтаксичні дерева можуть бути побудовані компілятором для будь-якої частини вхідної програми. Не завжди синтаксичному дереву повинний відповідати фрагмент коду результуючої програми — наприклад, можливо побудована синтаксичних дерев для декларативної частини мови. У цьому випадку операції, що є в дереві, не вимагають породження об'єктного коду, але несуть інформацію про дії, що повинний виконати сам компілятор над відповідними елементами. У тому випадку, коли синтаксичному дереву відповідає деяка послідовність операцій, що тягне породження фрагмента об'єктного коду, говорять про дерево операцій.

Дерево операцій можна безпосередньо побудувати з дерева виведення, породженого синтаксичним аналізатором. Для цього досить виключити з дерева виведення ланцюжка нетермінальних символів, а також вузли, що не несуть семантичного (значущого) навантаження при генерації коду. Прикладом таких вузлів можуть служити різні дужки, що змінюють порядок виконання операцій і операторів, але після побудови дерева ніякого значеннєвого навантаження не несуть, тому що їм не відповідає ніякий об'єктний код.

Те, який вузол у дереві є операцією, а який — операндом, ніяк неможливо визначити з граматики, що описує синтаксис вхідної мови. Також нізвідки не слідує, яким операціям повинний відповідати об'єктний код, в у результуючій програмі, а яким — немає. Усе це визначається тільки виходячи із семантики — «змісту» — мови вхідної програми. Тому тільки розроблювач компілятора може чітко визначити, як при побудові дерева операцій повинні розрізнятися операнди і самі операції, а також то, які операції є семантично незначущими для породження об'єктного коду.

Алгоритм перетворення дерева семантичного розбору в дерево операцій можна представити в такий спосіб.

Системне програмне забезпечення.

4

Крок 1. Якщо в дереві більше не міститься вузлів, позначених нетермінальними символами, то виконання алгоритму завершене; інакше — перейти до кроку 2.

Крок 2. Вибрати крайній лівий вузол дерева, позначений нетермінальним символом граматики і зробити його поточним. Перейти до кроку 3.

Крок 3. Якщо поточний вузол має тільки один нижчележачий вузол, то поточний вузол необхідно видалити з дерева, а зв'язаний з ним вузол приєднати до вузла вищлежачого рівня (виключити з дерева ланцюжок) і повернутися до кроку 1; інакше — перейти до кроку 4.

Крок 4. Якщо поточний вузол має нижчележачий вузол (лист дерева), позначений термінальним символом, що не несе семантичного навантаження, тоді цей лист потрібно видалити з дерева, і повернутися до кроку 3; інакше перейти до кроку 5.

Крок 5. Якщо поточний вузол має один нижчележачий вузол (лист дерева), позначений термінальним символом, що позначає знак операції, а інші вузли позначені як операнди, то лист, позначений знаком операції, і треба видалити з дерева, текучий вузол позначити цим знаком операції і перейти до кроку 1; інакше — перейти до кроку 6.

Крок 6. Якщо серед нижчележачих вузлів для поточного вузла є вузли, позначені нетермінальними символами граматики, то необхідно вибрати крайній лівий серед цих вузлів, зробити його поточним вузлом і перейти до кроку 3; інакше виконання алгоритму завершене.

Цей алгоритм завжди працює з вузлом дерева, що вважається поточним, і прагне виключити з дерева усі вузли, позначені нетермінальними символами. Те, які із символів вважати семантично незначними, а які знаками операцій, вирішує розроблювач компілятора. Якщо семантика мови задана коректно, то в результаті роботи алгоритму з дерева будуть виключені всі нетермінальні символи.

Візьмемо як приклади синтаксичні дерева, побудовані для побудови ланцюжка (а+а)*b з мови, заданого різними варіантами граматики арифметичних виразів. Ці дерева приведені вище як приклади на мал. 12.7 і 12.9. Семантично незначущими символами в цій граматиці є дужки (вони задають порядок операцій і впливають на синтаксичний розбір, але результуючого коду не породжують) і порожні рядки. Знаки операцій задані символами +, -, / і *, інші символи (а і b) є операндами.

У результаті застосування алгоритму перетворення дерев синтаксичного розбору в дерево операцій до дерев, представленим на мал. 12.3, 12.7 і одержимо дерево операцій, представлене на мал. 13.9. Причому, незважаючи на те, що вихідні синтаксичні дерева мали різну структуру, що залежить від

Системне програмне забезпечення.

5

використовуваної граматики, результуюче дерево операцій завжди має ту саму структуру, що залежить тільки від семантики вхідної мови.

*

+b

a a

Рис. 13.9. Приклад дерева операцій для мови арифметичних виражень

Дерево операцій є формою внутрішнього представлення програми, котрою зручно користуватися на етапах синтаксичного розбору, семантичного аналізу і підготовки до генерації коду, коли ще немає необхідності працювати з кодами команд результуючої програми. Дерево операцій чітко відображає зв'язок всіх операцій між собою, тому його зручно використовувати також для перетворень, зв'язаних з перестановкою і перевпорядкуванням операцій без зміни кінцевого результату (таких, як арифметичні перетворення).

Семантичний аналіз і підготовка до генерації коду

Призначення семантичного аналізу

Ми вже неодноразово згадували, що практично всі мови програмування є КВ-мовами. Тому повний розбір ланцюжків символів вхідної мови компілятор не може виконати в рамках КВ-мов за допомогою КВграматик і МП-автоматів. Повний розпізнавач для більшості мов програмування може бути побудований у рамках КЗ-мов, оскільки всі реальні мови програмування контекстно-залежні2.

Отже, повний розпізнавач для мови програмування можна побудувати, на основі розпізнавача КЗ-мови. Однак відомо, що такий розпізнавач має експонентну залежність необхідних для виконання розбору ланцюжка обчислювальних ресурсів від довжини вхідного ланцюжка. Компілятор, побудований на основі такого розпізнавача, буде неефективним з погляду або швидкості роботи, або обсягу необхідної пам'яті. Тому такі компілятори практично не використовуються, а всі реально існуючі компілятори на етапі розбору вхідних ланцюжків перевіряють тільки синтаксичні конструкції вхідної мови, не враховуючи його семантику.

2 Примером контекстной зависимости, часто встречающейся во многих языках программирования, может служить необходимость предварительно описать идентификатор до его первого использования.

Системне програмне забезпечення.

6

З метою підвищити ефективність компіляторів розбір ланцюжків вхідної мови виконується в два етапи: перший — синтаксичний розбір на основі розпізнавача одного з відомих класів КВ-мов; другий — семантичний аналіз вхідного ланцюжка.

Для перевірки семантичної правильності вхідної програми необхідно мати всю інформацію про знайдені лексичні одиниці мови. Ця інформація розміщується в таблицю лексем на основі конструкцій, знайдених синтаксичним розпізнавачем. Прикладами таких конструкціями є блоки опису констант і ідентифікаторів (якщо вони передбачені семантикою мови ) чи оператори, де той чи інший ідентифікатор зустрічається вперше (якщо опис відбувається по факті першого використання). Тому повний семантичний аналіз вхідної програми може бути зроблений тільки після повного завершення її синтаксичного розбору.

Таким чином, вхідними даними для семантичного аналізу служать:

-таблиця ідентифікаторів;

-результати розбору синтаксичних конструкцій вхідної мови.

Результати виконання синтаксичного розбору можуть бути представлені в одній з форм внутрішнього представлення програми в компіляторі. Як правило на етапі семантичного аналізу використовуються різні варіанти дерев синтаксичного розбору, оскільки семантичний аналізатор цікавить насамперед структура вхідної програми.

Семантичний аналіз звичайно виконується на двох етапах компіляції: на етапі синтаксичного розбору і на початку етапу підготовки до генерації коду. У першому випадку всякий раз по завершенні розпізнавання визначеної синтаксичної конструкції вхідної мови виконується її семантична перевірка на основі наявних у таблиці ідентифікаторів даних (такими конструкціями, як правило, є процедури, функції і блоки операторів вхідної мови). В другому випадку, після завершення усієї фази синтаксичного розбору, виконується повний семантичний аналіз програми на підставі даних у таблиці ідентифікаторів (сюди попадає, наприклад, пошук неописаних ідентифікаторів). Іноді семантичний аналіз виділяють в окремий етап (фазу) компіляції.

У кожнім компіляторі звичайно присутні обидва варіанти семантичного аналізатора. Конкретна їхня реалізація залежить від версії компілятора і семантики вхідної мови.

Етапи семантичного аналізу

Семантичний аналізатор виконує наступні основні дії:

1.перевірка дотримання у вхідній програмі семантичних угод вхідної мови;

2.доповнення внутрішнього представлення програми в компіляторі операторами і діями, неявно передбаченими семантикою вхідної мови;

3.перевірка елементарних семантичних (значеннєвих) норм мов програмування, прямо не зв'язаних із вхідною мовою.

Системне програмне забезпечення.

7

Перевірка дотримання у вхідній програмі семантичних угод вхідної мови полягає в зіставленні вхідних ланцюжків програми з вимогами семантики вхідної мови програмування. Кожна мова програмування має чітко задані і специфіковані семантичні угоди, що не можуть бути перевірені на етапі синтаксичного розбору.

Саме їх у першу чергу перевіряє семантичний аналізатор. Прикладами таких угод є наступні вимоги:

1.кожна мітка, на яку є посилання, повинна один раз бути присутня у програмі;

2.кожен ідентифікатор повинний бути описаний один раз, і жоден ідентифікатор не може бути описаний більш одного разу (з урахуванням блокової структури описів);

3.всі операнди у вираженнях і операціях повинні мати типи, припустимі для даного виразу або операції;

4.типи змінних у виразах повинні бути погоджені між собою;

5.при виклиці процедур і функцій число і типи фактичних параметрів повинні бути погоджені з числом і типами формальних параметрів.

Це тільки зразковий перелік такого роду вимог.

Конкретний склад вимог, що повинний перевіряти семантичний аналізатор, жорстко зв'язаний із семантикою вхідної мови (наприклад, деякі мови допускають не описувати ідентифікатори визначених типів).

Наприклад, якщо ми візьмемо оператор мови Pascal, що має вид:

а :=b + с;

з погляду синтаксичного розбору це буде абсолютно правильний оператор. Однак ми не можемо сказати, чи є цей оператор правильним з погляду вхідної мови (Pascal), поки не перевіримо семантичні вимоги для усіх вхідних у нього лексичних елементів. Такими елементами тут є ідентифікатори a, b і с. Не знаючи, що вони собою представляють, ми не можемо не тільки остаточно стверджувати правильність приведеного вище оператора, але і зрозуміти його зміст. Фактично необхідно знати опис цих ідентифікаторів.

У тому випадку, якщо хоча б один з них не описаний, має місце явна помилка. Якщо це числової змінні і константи, то ми маємо справу з оператором додавання, якщо ж це строкові змінні і константи — з оператором конкатенації рядків. Крім того, ідентифікатор а, наприклад, ні в якому разі не може бути константою — інакше порушена семантика оператора присвоювання. Також неможливо, щоб одні з ідентифікаторів були числами, а інші — рядками, чи, скажемо, ідентифікаторами масивів або структур — таке сполучення аргументів для операції додавання неприпустимо. І це тільки деяка частина угод, що повинний перевірити компілятор з погляду семантики вхідної мови (у даному прикладі — Pascal).

Слід ще зазначити, що від семантичних угод залежить не тільки правильність оператора, але і його зміст. Дійсно, операції алгебраїчного додавання і

Системне програмне забезпечення.

8

конкатенації рядків мають різний сенс, хоча і позначаються в розглянутому прикладі одним знаком операції - «+». Отже, від семантичного аналізатора залежить також і код результуючої програми.

Якщо яке-небудь із семантичних вимог вхідної мови не виконується, то компілятор видає повідомлення про помилку і процес компіляції на цьому, як правило, припиняється.

Доповнення внутрішнього представлення програми операторами і діями, неявно передбаченими семантикою вхідної мови, зв'язано з перетворенням типів операндів у виразах і при передачі параметрів у процедури і функції.

Якщо повернутися до розглянутого вище елементарного оператору мови

Pascal: а := b + с;

те можна відзначити, що тут виконуються дві операції: одна операція додавання (чи конкатенації, у залежності від типів операндів) і одна операція присвоєння результату. Відповідним чином повинний бути породжений і код результуючої програми.

Однак не все так просто. Допустимо, що десь перед розглянутим оператором ми маємо опис його операндів у виді:

var а: real;

b : іntеgеr; с : double;

з цього опису слідує, що а — дійсна змінна мови Pascal, b — цілочисельна змінна, с — дійсна змінна з подвійною точністю. Тоді зміст розглянутого оператора з погляду вхідної програми істотно міняється, оскільки в мові Pascal не можна прямо виконувати операції над операндами різних типів. Існують правила перетворення типів, прийняті для даної мови. Хто виконує ці перетворення?

Це може зробити розроблювач програми — але тоді перетворення типів у явному виді будуть присутні в тексті вхідної програми (у розглянутому прикладі це не так). В іншому випадку це робить код, породжуваний компілятором, коли перетворення типів у явному виді в тексті програми не присутні, але неявно передбачені семантичними угодами мови. Для цього в складі бібліотек функцій, доступних компілятору, повинні бути функції перетворення типів. Виклики цих функцій саме і будуть убудовані в текст результуючої програми для задоволення семантичних угод про перетворення типів у вхідній мові, хоча в тексті програми в явному виді вони не присутні, Щоб це відбулося, ці функції повинні бути убудовані і у внутрішнє представлення програми в компіляторі. За це також відповідає семантичний аналізатор. З урахуванням запропонованих типів даних, у розглянутому прикладі будуть не дві, а чотири операції: перетворення цілочисельної змінної b у формат дійсних чисел з подвійною точністю; додавання двох дійсних чисел з подвійною „точністю; перетворення результату в дійсне число з одинарною точністю; присвоєння результату змінній с. Кількість операцій зросла вдвічі,

Системне програмне забезпечення.

9

причому додалося два виклики дуже нетривіальних функцій перетворення типів.

Перетворення типів — це тільки один варіант операцій, які додаються неявно компілятором у код програми на основі семантичних угод. Іншим прикладом такого роду операцій можуть служити операції обчислення адреси, коли відбувається звертання до елементів складних структур даних. Існують і інші варіанти такого роду операцій (перетворення типів — тільки найпоширеніший приклад).

Таким чином, і тут дії, виконувані семантичним аналізатором, істотно впливають на породжуваний компілятором код результуючої програми. Перевірка елементарних значеннєвих норм мов програмування, прямо не зв'язаних із вхідною мовою, — це сервісна функція, що надають більшість сучасних компіляторів. Ця функція забезпечує перевірку компілятором деяких угод, застосовних до більшості сучасних мов програмування, виконання яких зв'язано зі змістом як усієї вхідної програми в цілому, так і окремих її фрагментів. Прикладами таких угод є наступні вимоги:

1.кожна змінна або константа повинна хоча б один раз використовуватися в програмі;

2.кожна змінна повинна бути визначена до її першого використання при будьякому ході виконання програми (першому використанню змінної повинне завжди передувати присвоєння їй якого-небудь значення);

3.результат функції повинний бути визначений при будь-якому ході її виконання;

4.кожен оператор у вхідній програмі повинний мати можливість хоча б один раз виконатися;

5.оператори умови і вибору повинні передбачати можливість ходу виконання програми по кожній зі своїх віток;

6.оператори циклу повинні передбачати можливість завершення циклу.

Звичайно, це тільки зразковий перелік основних угод. Конкретний склад угод, що перевіряються, залежить від семантики мови. Однак, на відміну від семантичних вимог мови, які перевіряються семантичним аналізатором, виконання даних угод не є обов'язковим. Тому те, які конкретно угоди будуть перевірятися і як вони будуть оброблятися, залежить від якості компілятора, від функцій, закладених у нього розроблювачами. Найпростіший компілятор узагалі може не виконувати цей етап семантичного аналізу і не перевіряти жодного такої угоди, навіть якщо це і можливо з погляду семантики вхідної мови.

Необов'язковість угод такого типу накладає ще одну особливість на їхню обробку в семантичному аналізаторі: їхнє недотримання не може трактуватися як помилка. Навіть якщо компілятор цілком упевнений у своїй правоті, той факт, що якесь із зазначених угод не дотримується, не повинний приводити до припинення компіляції вхідної програми. Звичайно факт виявлення недотримання такого роду угод трактується компілятором як попередження

Системне програмне забезпечення.

10

(warning). Компілятор видає користувачу повідомлення про виявлення недотримання одного з вимог, не перериваючи сам процес компіляції, — тобто він просто звертає увагу користувача на те чи інше місце у вихідній програмі. Те, як реагувати на «попередження» (вносити зміни у вхідний код або проігнорувати цей факт), — це вже турбота і відповідальність розроблювача програми.

Необов'язковість зазначених угод порозумівається тим, про що вже говорилося вище, — жоден компілятор не здатний цілком зрозуміти й оцінити зміст вихідної програми. А оскільки зміст програми доступний тільки людині, то він і повинний нести відповідальність за семантичні угоди.

Задача перевірки семантичних угод вхідної мови багато в чому зв'язана з проблемою верифікації програм.

Розглянемо як приклад функцію, що представляє собою фрагмент вхідної програми мовою С:

int f_test(int a)

{int b,c; b = 0; с = 0;

if (b=l) { return a; } с = a + b;

}

Практично будь-який сучасний компілятор з цієї мови знайде в даному місці вхідної програми масу «неточностей». Наприклад, змінна с описана, їй привласнюється значення, але вона ніде не використовується; значення змінної b, привласнене в операторі b=0, теж ніяк не використовується; нарешті, умовний оператор позбавлений змісту, тому що завжди передбачає хід виконання тільки по одній своїй гілці (і виходить, оператор с=a+b; ніколи виконаний не буде). Швидше за все, компілятор видасть ще одне попередження, характерне саме для мови С, — в операторі іf(b=1) присвоєння стоїть в умові (це не заборонено ні синтаксисом, ні семантикою мови, але є дуже розповсюдженою значеннєвою помилкою в С). У принципі зміст (а точніше, безглуздість) цього фрагмента буде правильно сприйнятий і оброблений компілятором.

***

Однак якщо взяти аналогічний за змістом, але синтаксично більш складний фрагмент програми, то картина буде трохи інша:

int f_test__add (int* a. int* b)

{

*а = 1; *b = 0; return *a;

}

Соседние файлы в папке lect