2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу 1, 2, 3  След.
 
 Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 09:35 


02/10/12
308
Пытаюсь написать парсер Си-кода для подсветки синтаксиса. На входе Си-файл, на выходе html-страница. Предназначен для внутренних нужд, но при надобности можно и для внешних нужд (на сервере). Самый парсер я уже написал (ссылка внизу), и вроде бы он работает. У меня несколько вопросов по языку Си.

Терминология: строковые константы вида "<html>\n" я называю литералами.

Вопрос-1.
Пример Си-кода, выдающего html-страницу. Эту html-страницу можно открыть в браузере. Глобальный массив, определённый как:
const char *kodirovka[] = {...}; -работает. А определённый как:
const char **kodirovka = {...}; -не работает (закомментирован).
код: [ скачать ] [ спрятать ]
Используется синтаксис C
// строка 1. Для привязки нумерации строк.


//   /home/test/SI/SI/parser_c_cod_2/zprint.c
//   cd /home/test/SI/SI/parser_c_cod_2/
//   gcc zprint.c -o zprint.cgi -Wall -Werror -O3
//   ./zprint.cgi


#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

// set KODIROVKA from array kodirovka[]
// установить кодировку из массива kodirovka. Это для выдачи
// кодировки в HTTP-заголовке и в МЕТА-данных html-страницы.
#define KODIROVKA   1

// глобальный массив
const char *kodirovka[] = {
    "windows-1251", // (0)
    "utf-8",        // (1)
};


/*
// глобальный массив
const char **kodirovka = {
    "windows-1251", // (0)
    "utf-8",        // (1)
};
*/


//const char *kod = kodirovka[KODIROVKA]; // глобальная, METKA-1 =====

//----------- set_kod -------------------

const char *set_kod(const char **kodirovka)
{
    int k = KODIROVKA;
    return(kodirovka[k]);
}


//const char *kod = set_kod(kodirovka); // глобальная, METKA-2 =======

//-------------------- main -------------------

int main(int argc, char const **argv)
{
    FILE *fp2;
    char const *f1="zprint.html";
    const char *kod = kodirovka[KODIROVKA]; //========= METKA-3 ======
    //const char *kod = set_kod(kodirovka); //========= METKA-4 ======


    printf("kod=%s\n", kod);

    fp2=fopen(f1, "w");
    if(fp2==NULL){ printf("fp1=fopen(%s)=NULL\n", f1); exit(0);}

    fprintf(fp2, "<html>\n");
    fprintf(fp2, "<head>\n");
    fprintf(fp2, "<title>p_C_2</title>\n");

    fprintf(fp2, "<meta charset=\"%s\">\n", kod); // кодировка

    fprintf(fp2, "</head>\n");
    fprintf(fp2, "<body>\n");

    fprintf(fp2, "<a href=\"index.html\">Главная</a>\n");

    fprintf(fp2, "<style>body {background-color: #e0e0ff;}</style>\n");
    fprintf(fp2, "<style>p {background:#ffffff}</style>\n");

    fprintf(fp2, "<p>Proba---Проба</p>\n");

    fprintf(fp2, "</body></html>\n");

    exit(0);
}
 

Комилятор со вторым определением (закомментировано) выдаёт:
Код:
$ gcc zprint.c -o zprint.cgi -Wall -Werror -O3
zprint.c:31:5: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
     "windows-1251", // (0)
     ^
zprint.c:31:5: note: (near initialization for ‘kodirovka’)
zprint.c:32:5: error: excess elements in scalar initializer [-Werror]
     "utf-8",        // (1)
     ^
zprint.c:32:5: note: (near initialization for ‘kodirovka’)
cc1: all warnings being treated as errors
$

Почему так? Казалось бы, две строки об одном и том же
const char *kodirovka[] = ... -работает.
const char **kodirovka = ... -не работает.
И в аргументах функции
const char *set_kod(const char **kodirovka);
массив определён по второму варианту, нерабочему, но работает. Моё предположение: когда память под массив уже выделена, то можно без квадратных скобок, а если ещё не выделена, то нужны квадратные скобки.

Вопрос-2.
Переменая const char *kod в парсере и в серверной обёртке парсера используется в трёх функциях, и я хотел сделать её глобальной. Но не получилось. В программе выше есть 4 метки, ими помечены кусочки кода, причастные к вопросу. Пробовал активировать каждую из них по отдельности. Глобальные (METKA-1, METKA-2) не работают. Выдача компилятора:
Для METKA-1
Код:
$ gcc zprint.c -o zprint.cgi -Wall -Werror -O3
zprint.c:36:19: error: initializer element is not constant
const char *kod = kodirovka[KODIROVKA]; // ���������, METKA-1 =====
                   ^
$

Для METKA-2
Код:
$ gcc zprint.c -o zprint.cgi -Wall -Werror -O3
zprint.c:47:19: error: initializer element is not constant
const char *kod = set_kod(kodirovka); // ����������, METKA-2 =======
                   ^
$

А локальные (МЕТКА-3, МЕТКА-4) работают. Почему так? Подозреваю, что это баг компилятора.

Вопрос-3.
Фрагмент Си-кода:
Используется синтаксис C
    printf("Content-type: text/html; charset=windows-1251\n\n");
    printf("<html>\n"
        "<head>\n"
        "<title>p_C_2</title>\n"
        "</head>\n"
        "<body>\n"

          ....
 

Здесь все литералы конкатенационно склеиваются, и получается один большой литерал. У меня это работает, но будет ли это работать на всех компьютерах? Существует ли правило, согласно которому последовательные литералы, не разделённые запятыми, склеиваются в один литерал?

Вопрос-4.
Верно ли, что вся латиница, все символы ASC-ii кода всгда в этом ASC-коде? Т. е. собственно Си-программа в ASC-коде, и только в литералах и в комментариях могут встретиться символы в других кодировках? Верно ли это хотя бы в абсолютном большинстве компьютеров, кроме какой-нибудь экзотики?

Парсер Си-кода (25 кб). При запуске программы:
Первым аргументом указать исходный файл ***.c
Вторым аргументом указать выходной файл ***.html
http://paste.org.ru/index.pl?8jnxg4


 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 09:53 
Заслуженный участник


02/08/11
7004
По первому вопросу. Для начала замечу что это один из тех немногих случаев, когда пустые квадратные скобки не эквивалентны звёздочке и означают тип-массив, а не тип-указатель. Это потому что в данном контексте компилятор воспринимает пустые квадратные скобки как указание подсчитать количество элементов самостоятельно и код эквивалентен const char *kodirovka[2] = {...};. Разница между типами видна, если взять sizeof — он разный у указателя и массива. Но главное, что в отсутствие квадратных скобок пропадает всякое указание компилятору, что где-то тут ожидается массив. В результате компилятор ожидает справа от равенства простое выражение с типом двойного указателя. А там неожиданные фигурные скобки. Поэтому фигурные скобки игнорируются, второй элемент отбрасывается как лишний (тут я не могу сходу сказать какая именно разрешённая языковая конструкция срабатывает) и код эквивалентен const char *kodirovka[2] = "...";.

oleg_2 в сообщении #1577147 писал(а):
И в аргументах функции const char *set_kod(const char **kodirovka); массив определён по второму варианту, нерабочему, но работает.
А вот тут как раз никакого массива нет, здесь именно указатель. Язык Си не позволяет передавать массивы в качестве параметра напрямую (позволяет косвенно если завернуть массив в структуру) и пустые квадратные скобки здесь не означают тип-массив, а просто являются синонимом звёздочки. (А в случае непустых квадратных скобок из содержимое всё равно игнорируется здесь — в полной противоположности с предыдущим случаем глобального массива, где наоборот пустые квадратные скобки неявно воспринимались как указание компилятору самом посчитать количество элементов.)

Что касается второй проблемы, то всё получится, если сделать ваши глобальные переменные константами (чем они видимо у вас и являются неявно):
Используется синтаксис C
const char * const kodirovka[] = {
    "windows-1251", // (0)
    "utf-8",        // (1)
};

const char * const kod = kodirovka[KODIROVKA];


(Первое слово const относится к тому на что указывает указатель, не в самой переменной.)

На самом деле я не могу объяснить почему с const код компилируется. По-идее не должен, потому что const в Си — штука "слабенькая", она не делает "настоящую" константу, а выражение-инициализатор глобальной переменной должны состоять исключительно из настоящих констант — таких как литералы и енумы. Возможно const "усилили" в "новых" стандартах, надо копать. Но то что ваш код без const не компилируется как раз понятно.

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 10:39 


02/10/12
308
warlock66613 в сообщении #1577149 писал(а):
Но главное, что в отсутствие квадратных скобок пропадает всякое указание компилятору, что где-то тут ожидается массив.

Я примерно это и предполагал, компилятор ещё память должен выделить. Вот хочу мысленно поставить себя на место компилятора, как бы я мог понять эти звёздочки. Но не так-то это легко, это скорее относится к написанию компилятора.

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 10:48 
Заслуженный участник


02/08/11
7004
oleg_2 в сообщении #1577154 писал(а):
Вот хочу мысленно поставить себя на место компилятора, как бы я мог понять эти звёздочки.
Просто есть ряд мест где происходит т. н. деградация типа-массива до указателя. В случае когда массив — тип параметра функции, например. Обратного не происходит никогда: звёздочки всегда указатели.

-- 15.01.2023, 11:50 --

oleg_2 в сообщении #1577147 писал(а):
Существует ли правило, согласно которому последовательные литералы, не разделённые запятыми, склеиваются в один литерал?
Да, последовательные строковые литералы склеиваются.

-- 15.01.2023, 11:57 --

oleg_2 в сообщении #1577147 писал(а):
Верно ли, что вся латиница, все символы ASC-ii кода всгда в этом ASC-коде? Т. е. собственно Си-программа в ASC-коде, и только в литералах и в комментариях могут встретиться символы в других кодировках? Верно ли это хотя бы в абсолютном большинстве компьютеров, кроме какой-нибудь экзотики?
Да, в общем верно. Но вообще программа на С — это именно текст и её двоичное представление на уровне стандарта не фиксировано. Это довольно неудобно (как раз из-за строк в первую очередь) и новые языки явно специфицируют, что программа — это текст в UTF-8 кодировке, но это так. В целом в большинстве случаев это должно быть неважно. А почему вам это оказалось важно?

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 11:30 


02/10/12
308
У меня не работает, сделал как Вы написали:
const char * const kod = kodirovka[KODIROVKA]; // глобальная, METKA-1 =====
Выдача компилятора:
Код:
$ gcc zprint.c -o zprint.cgi -Wall -Werror -O3
zprint.c:36:26: error: initializer element is not constant
const char * const kod = kodirovka[KODIROVKA]; // ����������, METKA-1
                          ^

Больше всего возмущает, что локальные работают, как это понять?
Слово const, как я понимаю, должно сохранить имя переменной в памяти компилятора, и если где-то, этому имени попробуют присвоить значение, то выдать ошибку. Но всёже самый первый раз компилятор должен позволить присвоить этому имени значение.
А как правильно понять этот вывод компилятора:
zprint.c:36:26: error: initializer element is not constant
Не константа то, что инициализируют, или то, чем инициализируют? Плохое справа или слева от знака присваивания?

warlock66613 в сообщении #1577156 писал(а):
и новые языки явно специфицируют, что программа — это текст в UTF-8 кодировке, но это так. В целом в большинстве случаев это должно быть неважно. А почему вам это оказалось важно?

Потому что с кодировками все мучаются, а самое главное для парсера подсветки, например встретилось слово int, поискали его в массиве встроенных типов, оно там есть, значит подсвечивать. А если кодировки разные в массиве и во входном файле, то не выйдет ничего. В UTF-8 вроде бы латиница в ASC-ii, потому что комментарии становятся крякозябрами, если что не так, а сам Си-код нормальный.

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 11:49 
Заслуженный участник


02/08/11
7004
oleg_2 в сообщении #1577163 писал(а):
локальные работают, как это понять?
Локальные переменные можно инициализировать чем угодно, а вот глобальные — нет. Это не только в С так, это довольно принципиальное ограничение.
oleg_2 в сообщении #1577163 писал(а):
Плохое справа или слева от знака присваивания?
Справа. Поэтому важен const при объявлении kodirovka. Обратите внимание на мой код в этом месте. Вы сделали так же?

-- 15.01.2023, 12:52 --

oleg_2 в сообщении #1577163 писал(а):
самое главное для парсера подсветки
Очень вряд ли в ваш парсер будут засовывать текст в кодировке несовместимой с ASCII.

-- 15.01.2023, 12:57 --

Хотя UTF-16 полностью не исключён, пожалуй.

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 12:55 


02/10/12
308
warlock66613 в сообщении #1577166 писал(а):
Локальные переменные можно инициализировать чем угодно, а вот глобальные — нет. Это не только в С так, это довольно принципиальное ограничение.

Этого я не знал, и теперь не понимаю, в чём смысл как-то по разному поступать с глобальными и с локальными.

warlock66613 в сообщении #1577166 писал(а):
Поэтому важен const при объявлении kodirovka. Обратите внимание на мой код в этом месте. Вы сделали так же?


Я допустил ошибку, не заметил, что в определение массива Вы тоже подставили const, но всё равно всё то же самое. Привожу целиком фрагмент кода:

код: [ скачать ] [ спрятать ]
Используется синтаксис C
// глобальный массив
const char * const kodirovka[] = {
    "windows-1251", // (0)
    "utf-8",        // (1)
};


/*
// глобальный массив
const char **kodirovka = {
    "windows-1251", // (0)
    "utf-8",        // (1)
};
*/


const char * const kod = kodirovka[KODIROVKA]; // глобальная, METKA-1 =====
 

Сейчас вроде бы полное соответствие, но выдача такая же
Код:
$ gcc zprint.c -o zprint.cgi -Wall -Werror -O3
zprint.c:36:26: error: initializer element is not constant
const char * const kod = kodirovka[KODIROVKA]; // ����������, METKA-1
                          ^
$

А Вы пробовали ли компилироать и так и так, и с const и без него? И если да, то получалась ли у Вас ошибка? А то может у Вас компилятор другой?

-- 15.01.2023, 14:09 --

Я бы уж и согласен определить переменные локально, подумаешь - три строчки кода вместо одной, но плохо, что я не понимаю, что происходит.

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 13:21 
Заслуженный участник


18/09/21
1756
oleg_2 в сообщении #1577147 писал(а):
Пытаюсь написать парсер Си-кода для подсветки синтаксиса.
Таких парсеров уже много написано. Проще готовый использовать.

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 13:30 


02/10/12
308
zykov в сообщении #1577188 писал(а):
Таких парсеров уже много написано. Проще готовый использовать.

Считайте, что это моё хобби, навыки-то как приобретать иначе. А кроме того, мой парсер лучше наверное.

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 14:55 
Заслуженный участник


02/08/11
7004
oleg_2 в сообщении #1577181 писал(а):
не понимаю, в чём смысл как-то по разному поступать с глобальными и с локальными
Это просто очень разные вещи. Локальная переменная лежит в стеке, место под неё выделяется при выполнении соответствующего кода, выполняется код, вычисляющий начальное значение и переменная инициализируется. Глобальная переменная лежит в одной из секций исполняемого файла (обычно .data), место под неё выделяется при загрузке исполнимого файла в память, начальное значение должно быть записано в исполнимом файле, поэтому оно вычисляется компилятором, поэтому оно должно быть выражением времени компиляции, то есть константой.

Можно попробовать сделать так:
Используется синтаксис C
const char * const kodirovka[] = {
    "windows-1251", // (0)
    "utf-8",        // (1)
};


const char* kod() {
    static const char * const kod = kodirovka[KODIROVKA];
    return kod;    
}
 

Соответственно, везде вместо обращения к переменной kod надо будет звать функцию, то есть добавить скобки: kod(). Но на самом деле это не очень хороший код, так как потенциально эту функцию может позвать любой поток, а она не потоко-безопасна. Чтобы не разбираться с мьютексами и атомиками, и не писать то, чего лучше не писать, я бы просто предложил проинициализировать переменную в main. Некрасиво, зато правильно. хотя лично я бы сделал так как показал выше, только сделал бы статическую переменную атомарной.

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 15:30 


02/10/12
308
warlock66613 в сообщении #1577197 писал(а):
Глобальная переменная лежит в одной из секций исполняемого файла (обычно .data), место под неё выделяется при загрузке исполнимого файла в память, начальное значение должно быть записано в исполнимом файле, поэтому оно вычисляется компилятором, поэтому оно должно быть выражением времени компиляции, то есть константой.

Вот это уже вроде бы проясняет, по-моему тут главное слово - время компиляции. Но пока не понял, а почему глобальные переменные не могут лежать там же, где и локальные, т. е. в стеке? Ведь, как я понимаю, отличие глобальной от локальной в том только, что её видно отовсюду.

Попробовал Ваш вариант с функцией kod(), не компилирует:
Код:
gcc zprint.c -o zprint.cgi -Wall -Werror -O3
zprint.c: In function ‘kod’:
zprint.c:44:37: error: initializer element is not constant
     static const char * const kod = kodirovka[KODIROVKA];
                                     ^
$

А Вы сами пробовали компилировать? Если да, то хоть в каком-нибудь варианте получили ли отказ от компилятора? Может у меня компилятор барахлит?

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 15:37 
Заслуженный участник


02/08/11
7004
oleg_2 в сообщении #1577199 писал(а):
А Вы сами пробовали компилировать?
Да, онлайн-компилятором. Хотя локальным системным сейчас попробовал тоже работают все варианты. А какой у вас компилятор?

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 15:38 


02/10/12
308
Вот так штука. Ваш вариант с функцией kod gcc бракует, а g++ не бракует. Вот так компилирует чисто:
g++ zprint.c -o zprint.cgi -Wall -Werror -O3

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 15:43 
Заслуженный участник


02/08/11
7004
oleg_2 в сообщении #1577202 писал(а):
Ваш вариант с функцией kod gcc бракует, а g++ не бракует
Дак это компилятор совсем другого языка с совсем другими правилами.

 Профиль  
                  
 
 Re: Язык Си. Парсер для подсветки Си-кода, глобальные переменные
Сообщение15.01.2023, 15:45 


02/10/12
308
warlock66613 в сообщении #1577201 писал(а):
А какой у вас компилятор?

Я не знаю. Знаю, что gcc и всё.
Дал команду
Код:
gcc -v

Вывалило 17 строк текста, приведу первые строки:
Код:
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.12' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 38 ]  На страницу 1, 2, 3  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group