Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабы и курсачи 2022-2023 / 2 сем / Baraeva_Elizaveta_cw.doc
Скачиваний:
0
Добавлен:
09.09.2026
Размер:
8 Мб
Скачать

1.6. Соглашение

Взаимодействие с программой происходит с помощью CLI (Command Line Interface). Сначала считываются основные аргументы, затем уже флаги для каждого основного аргумента. За 1 раз программа обрабатывает 1 основной аргумент. Важно: все побочные аргументы должны идти строго после основного аргумента, иначе программа выведет help страницу, и обработка изображения не случится. Имя файла для обработки должно идти после своего флага --file. Однако флаг с именем файла может быть указан в любом месте после основного флага операции. В случае, если программе будут поданы не валидные аргументы для ширины, цвета и т.д. (например, строка, вместо числа), программа выведет сообщение о не валидности, и обработка произведена не будет. Пользователь может передать один флаг только один раз (за исключением -h/--help и -i/--info). Программа работает с PNG-файлами без сжатия, 24 бита на цвет.

2. Примеры

1. Функция help см. рис. 1.

Рисунок 1 – Пример функции help

2. Функция info см. рис. 2.

Рисунок 2 – Пример функции info

3. Функция circle см. рис. 3, см. рис. 4.

./a.out -c --centre 600,320 --radius 200 --color 154,24,125 --width 70 --fill 255,255,255 --file Example.png

Рисунок 3 – Пример функции circle №1

./a.out --circle --start 250,250 --end 600,600 --color 34,65,165 --width 30 --file Example.png

Рисунок 4 – Пример функции circle №2

4. Функция filter см. рис. 5, см. рис. 6.

./a.out -f -b 255 --file Example.png

Рисунок 5 – Пример функции filter №1

./a.out --filter -g 0 --file Example.png

Рисунок 6 – Пример функции filter №2

5. Функция split см. рис. 7, см. рис. 8.

./a.out -s -n 2 -m 4 --color 255,84,22 --width 50 --file Example.png

Рисунок 7 – Пример функции split №1

./a.out -s -n 5 -m 7 --color 65,24,187 --width 4 --file Example.png

Рисунок 8 – Пример функции split №2

Заключение

В процессе выполнения курсовой работы была разработана программа для обработки PNG-изображений. Разработанная программа имеет интерфейс командной строки и может гибко взаимодействовать с форматом. Кроме того, на основе классических алгоритмов растеризации для отрисовки геометрических фигур были реализованы функции. Программа имеет help страницу и возможность вывода необходимой информации на экран.

Список использованных источников

1. Язык программирования Си: монография / Б.Керниган, Д.Ритчи; Пер. с

англ. под ред. В.С.Штаркмана. - 3-е изд., испр. - СПб. : Невский диалект, 2001. - 351 с.

2. Реализации алгоритмов/Алгоритм Брезенхэма – Викиучебник URL: https://ru.wikibooks.org/wiki/Реализации_алгоритмов/Алгоритм_Брезенхэма

3. libpng-manual.txt URL: http://www.libpng.org/pub/png/libpng-manual.txt

4. Лекция по программированию URL: https://youtu.be/fCQRboLFAbg

5. Заливка – Википедия URL: https://ru.wikipedia.org/wiki/Заливка

Приложение а иСходный код программы

Название файла: main.c

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <unistd.h>

#include <stdarg.h>

#include <png.h>

#include <getopt.h>

#include <stdbool.h>

typedef struct Png{

int width, height;

png_byte color_type;

png_byte bit_depth;

png_structp png_ptr;

png_infop info_ptr;

int number_of_passes;

png_bytep *row_pointers;

} PNG;

bool read_png(char *file_name, PNG *image);

void write_png(char *file_name, PNG *image);

void filter(PNG *image, char color, int value);

void split(PNG *image, int N, int M, int W, int* color);

void circle(PNG* image, int* start, int* end, int* center, int r, int w, int* color, int* fill);

bool scan_color(char* str_color, int* color){

if(str_color == NULL)

return false;

char *end1 = NULL, *end2 = NULL, *end3 = NULL;

char* c1 = strtok(str_color, ",");

char* c2 = strtok(NULL, ",");

char* c3 = strtok(NULL, ",");

if(c1 != NULL && c2 != NULL && c3 != NULL){

color[0] = (int) strtol(c1, &end1, 10);

color[1] = (int) strtol(c2, &end2, 10);

color[2] = (int) strtol(c3, &end3, 10);

if(*end1 == '\0' || *end2 == '\0' || *end3 == '\0'){

if(color[0] >= 0 && color[0] <= 255 && color[1] >= 0 && color[1] <= 255 && color[2] >= 0 && color[2] <= 255){

return true;

}

}

}

return false;

}

bool scan_coordinates(char* str, int* coord){

if(str == NULL)

return false;

char *end1 = NULL, *end2 = NULL;

char* c1 = strtok(str, ",");

char* c2 = strtok(NULL, ",");

if(c1 != NULL && c2 != NULL){

coord[0] = (int) strtol(c1, &end1, 10);

coord[1] = (int) strtol(c2, &end2, 10);

if(*end1 == '\0' || *end2 == '\0'){

if(coord[0] >= 0 && coord[1] >= 0){

return true;

}

}

}

return false;

}

bool scan_number(char* str, int* number){

if(str == NULL)

return false;

char* end = NULL;

*number = (int) strtol(str, &end, 10);

if(*end != '\0')

return false;

if(*number < 0)

return false;

return true;

}

void case_filter(int argc, char* argv[]){

opterr = 0;

const char* short_options = "r:g:b:";

const struct option long_options[] = {

{"red", required_argument, NULL, 'r'},

{"green", required_argument, NULL, 'g'},

{"blue", required_argument, NULL, 'b'},

{"file", required_argument, NULL, 'i'},

{NULL, 0, NULL, 0}};

int rez, option_index, value = -1, sum = 0;

char *file_name = NULL;

char color = '0';

while((rez = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1){

switch(rez){

case 'r':

case 'g':

case 'b':

color = rez;

if(optarg == NULL){

fprintf(stderr, "Неправильный ввод числа\n");

return;

}

char* end = NULL;

value = (int) strtol(optarg, &end, 10);

if(*end != '\0'){

fprintf(stderr, "Неправильный ввод числа\n");

return;

}

if(!(value == 255 || value == 0)){

fprintf(stderr, "Неправильный ввод числа\n");

return;

}

sum++;

break;

case 'i':

if(optarg == NULL){

fprintf(stderr, "Неправильный ввод имени файла\n");

return;

}

file_name = optarg;

sum++;

break;

default:

fprintf(stderr, "Неправильный ввод флага\n");

return;

}

}

if(sum != 2 || file_name == NULL || color == '0'){

fprintf(stderr, "Введено неправильное количество аргументов\n");

return;

}

PNG image;

if(read_png(file_name, &image)){

filter(&image, color, value);

write_png(file_name, &image);

}

}

void case_circle(int argc, char* argv[]){

opterr = 0;

const char* short_options = "";

const struct option long_options[] = {

{"start", required_argument, NULL, 't'},

{"end", required_argument, NULL, 'e'},

{"centre", required_argument, NULL, 'x'},

{"radius", required_argument, NULL, 'a'},

{"width", required_argument, NULL, 'w'},

{"color", required_argument, NULL, 'o'},

{"fill", required_argument, NULL, 'l'},

{"file", required_argument, NULL, 'i'},

{NULL, 0, NULL, 0}};

int rez, option_index, w = -1, r = -1, start[2] = {-1, -1}, end[2] = {-1, -1}, center[2] = {-1, -1}, color[3] = {-1, -1, -1}, fill[3] = {-1, -1, -1}, sum = 0;

char *file_name = NULL;

bool bool_start = false, bool_end = false, bool_center = false, bool_color = false, bool_fill = false;

while((rez = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1){

switch(rez){

case 't':

if(!(scan_coordinates(optarg, start))){

fprintf(stderr, "Неправильный ввод координат\n");

return;

}

bool_start = true;

sum++;

break;

case 'e':

if(!(scan_coordinates(optarg, end))){

fprintf(stderr, "Неправильный ввод координат\n");

return;

}

bool_end = true;

sum++;

break;

case 'x':

if(!(scan_coordinates(optarg, center))){

fprintf(stderr, "Неправильный ввод координат\n");

return;

}

bool_center = true;

sum++;

break;

case 'a':

if(!(scan_number(optarg, &r))){

fprintf(stderr, "Неправильный ввод числа\n");

return;

}

sum++;

break;

case 'w':

if(!(scan_number(optarg, &w))){

fprintf(stderr, "Неправильный ввод числа\n");

return;

}

sum++;

break;

case 'o':

if(!(scan_color(optarg, color))){

fprintf(stderr, "Неправильный ввод цвета\n");

return;

}

bool_color = true;

sum++;

break;

case 'l':

if(!(scan_color(optarg, fill))){

fprintf(stderr, "Неправильный ввод цвета\n");

return;

}

bool_fill = true;

sum++;

break;

case 'i':

if(optarg == NULL){

fprintf(stderr, "Неправильный ввод имени файла\n");

return;

}

file_name = optarg;

sum++;

break;

default:

fprintf(stderr, "Неправильный ввод флага\n");

return;

}

}

if(!(file_name != NULL && w != -1 && bool_color == true && (bool_start == true && bool_end == true && (sum == 5 || sum == 6 && bool_fill == true) ||

bool_center == true && r != -1 && (sum == 5 || sum == 6 && bool_fill == true)))){

fprintf(stderr, "Введено неправильное количество аргументов\n");

return;

}

PNG image;

if(read_png(file_name, &image)){

circle(&image, start, end, center, r, w, color, fill);

write_png(file_name, &image);

}

}

void case_split(int argc, char* argv[]){

opterr = 0;

const char* short_options = "n:m:";

const struct option long_options[] = {

{"width", required_argument, NULL, 'w'},

{"color", required_argument, NULL, 'o'},

{"file", required_argument, NULL, 'i'},

{NULL, 0, NULL, 0}};

int rez, option_index, n = -1, m = -1, w = -1, color[3] = {-1, -1, -1}, sum = 0;

char *file_name = NULL;

bool bool_color = false;

while((rez = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1){

switch(rez){

case 'n':

if(!(scan_number(optarg, &n))){

fprintf(stderr, "Неправильный ввод числа\n");

return;

}

sum++;

break;

case 'm':

if(!(scan_number(optarg, &m))){

fprintf(stderr, "Неправильный ввод числа\n");

return;

}

sum++;

break;

case 'w':

if(!(scan_number(optarg, &w))){

fprintf(stderr, "Неправильный ввод числа\n");

return;

}

sum++;

break;

case 'o':

if(!(scan_color(optarg, color))){

fprintf(stderr, "Неправильный ввод числа\n");

return;

}

bool_color = true;

sum++;

break;

case 'i':

if(optarg == NULL){

fprintf(stderr, "Неправильный ввод имени файла\n");

return;

}

file_name = optarg;

sum++;

break;

default:

fprintf(stderr, "Неправильный ввод флага\n");

return;

}

}

if(sum != 5 || file_name == NULL || n == -1 || m == -1 || w == -1 || bool_color == false){

fprintf(stderr, "Введено неправильное количество аргументов\n");

return;

}

PNG image;

if(read_png(file_name, &image)){

split(&image, n, m, w, color);

write_png(file_name, &image);

}

}

void help(){

puts("Руководство по использованию программы:");

puts("Программа обрабатывает PNG-файлы c глубиной изображения 24 бита на пиксель\n");

puts("Обозначения:");

puts("\t x,y - целые, неотрицательный числа");

puts("\t a - целое, положительное число");

puts("\t c - число равное либо 0, либо 255");

puts("\tr,g,b - целые числа в диапозоне [0,255]\n");

puts("Функции/флаги:");

puts("-c/--circle -- рисование окружности");

puts("\t--start x,y - координаты левого верхнего квадрата, в который будет вписана окружность");

puts("\t--end x,y - координаты правого нижнего квадрата, в который будет вписана окружность");

puts("\tили");

puts("\t--centre x,y - координаты центра окружности");

puts("\t--radius a - радиус окружности\n");

puts("\t--width a - значение толщины окружности");

puts("\t--color r,g,b - значения цветов окружности");

puts("\t--fill r,g,b - значения цветов заливки окружности (данный флаг может отсутствовать)");

puts("\t--file <путь> - файл для записи изображения\n");

puts("-f/--filter -- наложение фильтра на изображение");

puts("\t-r c - значение цвета, устанавливаемое в красный цвет");

puts("\t-g c - значение цвета, устанавливаемое в зеленый цвет");

puts("\t-b c - значение цвета, устанавливаемое в синий цвет");

puts("\t--file <путь> - файл для записи изображения\n");

puts("-s/--split -- разделение изображения на N*M частей");

puts("\t-n a - количество частей по оси Y");

puts("\t-m a - количество частей по оси X");

puts("\t--width a - значение толищны линии");

puts("\t--color r,g,b - значения цветов линии");

puts("\t--file <путь> - файл для записи изображения\n");

puts("-h/--help - вывод справки о программе");

puts("-i/--info <путь> - вывод информации о PNG-файле");

}

void info(char* file_name){

PNG image;

if(read_png(file_name, &image)){

puts("Информация о файле:");

printf("Ширина изображения: %d\n", image.width);

printf("Высота изображения: %d\n", image.height);

printf("Тип цвета: %d\n", image.color_type);

printf("Глубина цвета: %d\n", image.bit_depth);

}

write_png(file_name, &image);

}

int main(int argc, char *argv[]){

opterr = 0;

const char* short_options = "hi:cfs";

const struct option long_options[] = {

{"help", no_argument, NULL, 'h'},

{"info", required_argument, NULL, 'i'},

{"circle", no_argument, NULL, 'c'},

{"filter", no_argument, NULL, 'f'},

{"split", no_argument, NULL, 's'},

{NULL, 0, NULL, 0}};

int rez, option_index;

rez = getopt_long(argc, argv, short_options, long_options, &option_index);

switch(rez){

case 'h':

help();

break;

case 'i':

info(optarg);

break;

case 'c':

case_circle(argc, argv);

break;

case 'f':

case_filter(argc, argv);

break;

case 's':

case_split(argc, argv);

break;

default:

fprintf(stderr, "Неправильный ввод флага\n");

help();

}

return 0;

}

void put_pixel(PNG *image, int x, int y, int* color){

image->row_pointers[y][x * 3] = color[0];

image->row_pointers[y][x * 3 + 1] = color[1];

image->row_pointers[y][x * 3 + 2] = color[2];

}

void draw_vertical_line(PNG *image, int x, int* color){

if(x < 0 || x > image->width)

return;

for(int y = 0; y < image->height; y++){

put_pixel(image, x, y, color);

}

}

void draw_horizontal_line(PNG *image, int y, int* color){

if(y < 0 || y > image->height)

return;

for(int x = 0; x < image->width; x++){

put_pixel(image, x, y, color);

}

}

void xLine(PNG* image, int x1, int x2, int y, int* color){

while(x1 <= x2){

if(x1 >= 0 && x1 < image->width && y >= 0 && y < image->height)

put_pixel(image, x1, y, color);

x1++;

}

}

void yLine(PNG* image, int x, int y1, int y2, int* color){

while(y1 <= y2){

if(y1 >= 0 && y1 < image->height && x >= 0 && x < image->width)

put_pixel(image, x, y1, color);

y1++;

}

}

void fill_circle(PNG* image, int x, int y, int* color, int* fill){

if(0 <= y && y < image->height && 0 <= x && x < image->width

&& !(image->row_pointers[y][x * 3] == fill[0] && image->row_pointers[y][x * 3 + 1] == fill[1] && image->row_pointers[y][x * 3 + 2] == fill[2])

&& !(image->row_pointers[y][x * 3] == color[0] && image->row_pointers[y][x * 3 + 1] == color[1] && image->row_pointers[y][x * 3 + 2] == color[2])){

put_pixel(image, x, y, fill);

fill_circle(image, x + 1, y, color, fill);

fill_circle(image, x, y + 1, color, fill);

fill_circle(image, x - 1, y, color, fill);

fill_circle(image, x, y - 1, color, fill);

}

}

void circle_part(PNG* image, int x0, int y0, int r, int w, int* color, int part){

int out, in, y = 0;

if(w % 2){

out = r + w / 2;

in = r - w / 2;

}

else{

out = r + w / 2;

in = r - w / 2 + 1;

}

int erro = 1 - out, erri = 1 - in;

while(out >= y){

switch(part){

case 1:

xLine(image, x0 + in, x0 + out, y0 - y, color);

yLine(image, x0 + y, y0 - out, y0 - in, color);

break;

case 2:

xLine(image, x0 - out, x0 - in, y0 - y, color);

yLine(image, x0 - y, y0 - out, y0 - in, color);

break;

case 3:

xLine(image, x0 - out, x0 - in, y0 + y, color);

yLine(image, x0 - y, y0 + in, y0 + out, color);

break;

case 4:

xLine(image, x0 + in, x0 + out, y0 + y, color);

yLine(image, x0 + y, y0 + in, y0 + out, color);

}

y++;

if(erro < 0)

erro += 2 * y + 1;

else{

out--;

erro += 2 * (y - out + 1);

}

if(y > r - w / 2)

in = y;

else{

if(erri < 0)

erri += 2 * y + 1;

else{

in--;

erri += 2 * (y - in + 1);

}

}

}

}

void circle(PNG* image, int* start, int* end, int* center, int r, int w, int* color, int* fill){

int R = -1;

if(center[0] == -1 && center[1] == -1 && r == -1){

if(end[0] < start[0] || end[1] < start[1]){

fprintf(stderr, "Неправильный ввод координат\n");

return;

}

if((end[0] - start[0]) != (end[1] - start[1])){

fprintf(stderr, "Неправильный ввод координат, не квадрат\n");

return;

}

if((end[0] - start[0]) % 2){

int r = (end[0] - start[0]) / 2;

int x0 = start[0] + r, y0 = start[1] + r;

circle_part(image, x0, y0, r, w, color, 2);

circle_part(image, x0 + 1, y0, r, w, color, 1);

circle_part(image, x0, y0 + 1, r, w, color, 3);

circle_part(image, x0 + 1, y0 + 1, r, w, color, 4);

if(fill[0] != -1 && fill[1] != -1 && fill[2] != -1){

fill_circle(image, x0, y0, color, fill);

}

return;

}

R = (end[0] - start[0]) / 2;

center[0] = start[0] + R;

center[1] = start[1] + R;

}

if(R != -1)

r = R;

int out, in, y = 0;

if(w % 2){

out = r + w / 2;

in = r - w / 2;

}

else{

out = r + w / 2;

in = r - w / 2 + 1;

}

int erro = 1 - out, erri = 1 - in;

while(out >= y){

xLine(image, center[0] + in, center[0] + out, center[1] + y, color);

yLine(image, center[0] + y, center[1] + in, center[1] + out, color);

xLine(image, center[0] - out, center[0] - in, center[1] + y, color);

yLine(image, center[0] - y, center[1] + in, center[1] + out, color);

xLine(image, center[0] - out, center[0] - in, center[1] - y, color);

yLine(image, center[0] - y, center[1] - out, center[1] - in, color);

xLine(image, center[0] + in, center[0] + out, center[1] - y, color);

yLine(image, center[0] + y, center[1] - out, center[1] - in, color);

y++;

if(erro < 0)

erro += 2 * y + 1;

else{

out--;

erro += 2 * (y - out + 1);

}

if(y > r - w / 2)

in = y;

else{

if(erri < 0)

erri += 2 * y + 1;

else{

in--;

erri += 2 * (y - in + 1);

}

}

}

if(fill[0] != -1 && fill[1] != -1 && fill[2] != -1){

fill_circle(image, center[0], center[1], color, fill);

}

}

void split(PNG *image, int N, int M, int W, int* color){

if(N > image->height || M > image->width){

fprintf(stderr, "Неправильный ввод числа\n");

return;

}

float len = (float) (image->width - (M - 1)) / M;

float x = len;

while(x < image->width){

for(int i = x - W / 2; i < x - W / 2 + W; i++){

draw_vertical_line(image, i, color);

}

x += len + 1;

}

len = (float) (image->height - (N - 1)) / N;

float y = len;

while(y < image->height){

for(int i = y - W / 2; i < y - W / 2 + W; i++){

draw_horizontal_line(image, i, color);

}

y += len + 1;

}

}

void filter(PNG *image, char color, int value){

switch (color){

case 'r':

for(int y = 0; y < image->height; y++){

png_byte *row = image->row_pointers[y];

for(int x = 0; x < image->width * 3; x += 3){

row[x] = value;

}

}

break;

case 'g':

for(int y = 0; y < image->height; y++){

png_byte *row = image->row_pointers[y];

for(int x = 1; x < image->width * 3; x += 3){

row[x] = value;

}

}

break;

case 'b':

for(int y = 0; y < image->height; y++){

png_byte *row = image->row_pointers[y];

for(int x = 2; x < image->width * 3; x += 3){

row[x] = value;

}

}

}

}

bool read_png(char *file_name, PNG *image){

char header[8];

FILE *fp = fopen(file_name, "rb");

if(!fp){

fprintf(stderr, "Ошибка в открытии файла\n");

return false;

}

fread(header, sizeof(char), 8, fp);

image->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

if(!image->png_ptr){

fprintf(stderr, "Ошибка в png_create_read_struct\n");

return false;

}

image->info_ptr = png_create_info_struct(image->png_ptr);

if (!image->info_ptr){

fprintf(stderr, "Ошибка в png_create_info_struct failed\n");

return false;

}

if(setjmp(png_jmpbuf(image->png_ptr))){

fprintf(stderr, "Ошибка во время init_io\n");

return false;

}

png_init_io(image->png_ptr, fp);

png_set_sig_bytes(image->png_ptr, 8);

png_read_info(image->png_ptr, image->info_ptr);

image->width = png_get_image_width(image->png_ptr, image->info_ptr);

image->height = png_get_image_height(image->png_ptr, image->info_ptr);

image->color_type = png_get_color_type(image->png_ptr, image->info_ptr);

image->bit_depth = png_get_bit_depth(image->png_ptr, image->info_ptr);

image->number_of_passes = png_set_interlace_handling(image->png_ptr);

png_read_update_info(image->png_ptr, image->info_ptr);

if (setjmp(png_jmpbuf(image->png_ptr))){

fprintf(stderr, "Ошибка во время read_image\n");

return false;

}

image->row_pointers = (png_bytep *) malloc(sizeof(png_bytep) * image->height);

for(int y = 0; y < image->height; y++)

image->row_pointers[y] = (png_byte *) malloc(png_get_rowbytes(image->png_ptr, image->info_ptr));

png_read_image(image->png_ptr, image->row_pointers);

fclose(fp);

return true;

}

void write_png(char *file_name, struct Png *image){

FILE *fp = fopen(file_name, "wb");

if (!fp){

fprintf(stderr, "Ошибка в открытии файла\n");

return;

}

image->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

if (!image->png_ptr){

fprintf(stderr, "Ошибка в png_create_write_struct\n");

return;

}

image->info_ptr = png_create_info_struct(image->png_ptr);

if (!image->info_ptr){

fprintf(stderr, "Ошибка в png_create_write_info_struct\n");

return;

}

if (setjmp(png_jmpbuf(image->png_ptr))){

fprintf(stderr, "Ошибка во время init_io\n");

return;

}

png_init_io(image->png_ptr, fp);

if (setjmp(png_jmpbuf(image->png_ptr))){

fprintf(stderr, "Ошибка во время записи байт\n");

return;

}

png_set_IHDR(image->png_ptr, image->info_ptr, image->width, image->height, image->bit_depth, image->color_type,

PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

png_write_info(image->png_ptr, image->info_ptr);

if (setjmp(png_jmpbuf(image->png_ptr))){

fprintf(stderr, "Ошибка во время записи байт\n");

return;

}

png_write_image(image->png_ptr, image->row_pointers);

if (setjmp(png_jmpbuf(image->png_ptr))){

fprintf(stderr, "Ошибка во время конца записи\n");

return;

}

png_write_end(image->png_ptr, NULL);

for(int y = 0; y < image->height; y++)

free(image->row_pointers[y]);

free(image->row_pointers);

fclose(fp);

}

31

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