Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ С\С++ _ Начал изучать Си. Первые трудности.

Автор: micro-chipset 29.1.2011, 10:54

Возникла проблема со следующей задачей. Задача из из книги Б. Керниган, Д. Ритчи "Язык программирования Си" Глава 1.6 упражнение 1.13 Книгу онлайн можно найти http://iakovlev.org/index.html?p=1351&m=1&l1=5

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


набросок программы

#include <stdio.h>

/*Программа вывода гистограммы длин слов во входном потоке*/

main ()
{
    int i, c, j;
    int length[15];
    
    j = 0;
    
    for (i = 0; i < 15; i++)
        length[i] = 0;
    while ((c = getchar()) != EOF)
        if (c != ' ' || c != '\t' || c != '\n'){
            ++j;
            ++length[c-j];
        }
    printf("Длинна слов =");
    for (i = 0; i < 15; i++)
        printf(" %d", length[i]);
}

Программа компилируется, но выдает не верные результаты. Наверное не верно заполняю массив, как поправить. Зарание спасибо за помощь.

Автор: Алексей1153 29.1.2011, 12:51

а почему именно C, а не C++ ? Или тебе для микроконтроллеров ?

Для начала - учимся основам:
1) форматирование кода (тут я лично мой варинт покажу, но он не самый плохой. Я даже уверен, что он хороший :) )
2) инициализация

с учётом вышесказанного, программа в стиле C будет выглядеть уже так

Раскрывающийся текст
#include <stdio.h>

/*Программа вывода гистограммы длин слов во входном потоке*/

main()
{
    //не лепи все объявления в одну строчку!
    //И не забывай про инициализацию
    int i=0;
    int c=0;
    int j=0;
    
    //константы надо именовать
    enum
    {
        arrlen=15,
    };
    
    int length[arrlen];
    //массив тоже полезно инитить
    memset(length,0,arrlen*sizeof(*length));
    
    j=0;
    
    while((c=getchar()) != EOF)
    {
        if(c!=' ' || c!='\t' || c!='\n')
        {
            j++;
            
            //полезно проверять выход за край, если это неочевидно
            if(c-j<arrlen)
            {
                length[c-j]++;
            }
        }
    }
    
    printf("Длина слов =");
    
    for(i=0; i<arrlen; i++)
    {
        printf(" %d", length[i]);
    }
}

Автор: micro-chipset 29.1.2011, 14:43

1) С форматированием согласен еще не вошло в привычку придерживаться нормального стиля.

На данный код компилятор ругается:

Ошибки компилятора

gcc power.c -o power
power.c: В функции ‘main’:
power.c:21: предупреждение: несовместимая неявная декларация внутренней функции ‘memset’


enum
    {
        arrlen=15,
    };
    
    int length[arrlen];
    //массив тоже полезно инитить
    memset(length,0,arrlen*sizeof(*length));


Этот кусочек не совсем ясен. Особенно что такое enum и зачем так делается. И зачем инитить массив что это дает. У Керниган и Ритчи пока такого не встречал если можно поясните пожалуста. Хотелось бы понять.

Автор: Алексей1153 29.1.2011, 16:17

Цитата(micro-chipset @ 29.1.2011, 16:43) *
На данный код компилятор ругается:


надо заголовочник добавить
<memory.h>

enum - это объявление константы типа int (причём, эта константа будет иметь область видимости, что очень хорошо)

arrlen*sizeof(*length)
- это выражение даст длину массива в байтах ( arrlen * длину_одного_элемента_массива)

(*length) - это разыменование указателя, так что sizeof этого выражения вернёт размер указуемого элемента

для статического массива можно было бы и просто sizeof(length) написать, но я не стал тебя путать и показал универсальный вариант для статических и для динамических массивов


Цитата(micro-chipset @ 29.1.2011, 16:43) *
И зачем инитить массив что это дает

инициализировать нужно абсолютно всё, иначе сюрпризы будут появляться в программах постоянно

Автор: Litkevich Yuriy 29.1.2011, 20:16

Цитата(Алексей1153 @ 29.1.2011, 18:17) *
причём, эта константа будет иметь область видимости
фразу-то не закончил. Какую область видимости будет иметь константа?

Автор: DEADHUNT 29.1.2011, 20:22

Цитата(Алексей1153 @ 29.1.2011, 16:17) *
Цитата(micro-chipset @ 29.1.2011, 16:43) *
На данный код компилятор ругается:


надо заголовочник добавить
<memory.h>

enum - это объявление константы типа int (причём, эта константа будет иметь область видимости, что очень хорошо)

arrlen*sizeof(*length)
- это выражение даст длину массива в байтах ( arrlen * длину_одного_элемента_массива)

не понятно чему ты учишь, но в C нету memory.h, функция memset определёна в string.h/cstring.
по поводу arrlen*sizeof(*length), проще так:
memset(arr, 0, sizeof(arr));

Автор: Rocky 29.1.2011, 21:01

Кстати, а в ANSI C есть операторы "++" и "--" постфиксной и префиксной форм? (имеющие тот же смысл что и в С++)

Я просто помню помню фразу преподователя со 2-го курса института "Знаменитый оператор ++ языка С++....." которая предполагала, что все это было введено уже в С++.... А т.к. компилятора ANSI C у меня никогда не было =), я так и остался в неведении...

Автор: BRE 29.1.2011, 21:09

Цитата(Rocky @ 29.1.2011, 21:01) *
Кстати, а в ANSI C есть операторы "++" и "--" постфиксной и префиксной форм? (имеющие тот же смысл что и в С++)

Есть.

Автор: Rocky 29.1.2011, 22:10

Буду знать, пасиб)

Автор: micro-chipset 30.1.2011, 10:55

Программа компилируется и работает. Но выдает не верный результат. Проверяю так:

./1.13 < 1

то есть должна посчитать длинны слов в файле 1 в файле один содержится:
при привет в

А программа выдает:
Длина слов = 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0

А должно быть:
Длина слов = 0 1 0 1 0 0 1 0 0 0 0 0 1 0 0


Программа имеет вид:
Исходный код программы
#include <stdio.h>
#include <memory.h>

/*Программа вывода гистограммы длин слов во входном потоке*/

main()
{
    //не лепи все объявления в одну строчку!
    //И не забывай про инициализацию
    int i=0;
    int c=0;
    int j=0;
    
    //константы надо именовать
    enum
    {
        arrlen=15,
    };
    
    int length[arrlen];
    //массив тоже полезно инитить
    memset(length,0,arrlen*sizeof(*length));
    
    j=0;
    
    while((c=getchar()) != EOF)
    {
        if(c!=' ' || c!='\t' || c!='\n')
        {
            j++;
            
            //полезно проверять выход за край, если это неочевидно
            if(c-j<arrlen)
            {
                length[c-j]++;
            }
        }
    }
    
    printf("Длина слов =");
    
    for(i=0; i<arrlen; i++)
    {
        printf(" %d", length[i]);
    }
}

Автор: DEADHUNT 30.1.2011, 11:27

файл utf8, а ты его как ASCII читаешь.

Автор: micro-chipset 30.1.2011, 11:34

Цитата(DEADHUNT @ 30.1.2011, 11:27) *
файл utf8, а ты его как ASCII читаешь.

А как поправить?

Автор: Алексей1153 30.1.2011, 15:00

Цитата(Litkevich Yuriy @ 29.1.2011, 22:16) *
фразу-то не закончил. Какую область видимости будет иметь константа?

как то есть где, в том блоке, где определена


Цитата(DEADHUNT @ 29.1.2011, 22:22) *
не понятно чему ты учишь, но в C нету memory.h, функция memset определёна в string.h/cstring.
по поводу arrlen*sizeof(*length), проще так:
memset(arr, 0, sizeof(arr));

http://msdn.microsoft.com/en-us/library/aa246471(VS.60).aspx
тут два заголовочника. Можно любой :) А что именно есть в его среде, я не знаю
Насчёт "проще" - я выше написал, почему я именно так сделал - а то он и динамический массив так инициализировать будет



Автор: micro-chipset 30.1.2011, 15:59

А все же что с решением задачки. Работает она не верно же

Автор: Алексей1153 30.1.2011, 16:49

micro-chipset, покажи алгоритм на обычном русском языке - что ты делаешь. Без алгоритма не напишешь и не отладишь

Автор: DEADHUNT 30.1.2011, 17:12

Цитата(micro-chipset @ 30.1.2011, 11:34) *
А как поправить?

для работы с UTF можешь использовать например ICU.

Цитата(Алексей1153 @ 30.1.2011, 15:00) *
http://msdn.microsoft.com/en-us/library/aa246471(VS.60).aspx
тут два заголовочника. Можно любой :) А что именно есть в его среде, я не знаю
Насчёт "проще" - я выше написал, почему я именно так сделал - а то он и динамический массив так инициализировать будет

когда я начинал писать программы на C, я подключал <malloc.h> вместо <stdlib.h>(когда мне были нужны malloc/realloc/calloc/free). Да работает, но это не правильно. И в один момент компилятор может выдать ошибку, потому-что это не стандартный заголовочный файл.

Автор: micro-chipset 30.1.2011, 17:37

Цитата(DEADHUNT @ 30.1.2011, 17:12) *
Цитата(micro-chipset @ 30.1.2011, 11:34) *
А как поправить?

для работы с UTF можешь использовать например ICU.

А поподробней

Алгоритм словами если ну примерно так представляю:
Считываем посимвольно входной поток при помощи getchar(). Начиная с первого символа проверяем слово это или пробел или табуляция или перевод на новую строку если буква увеличиваем переменную на один если следующая снова буква то увеличиваем еще на один в итоге в переменной накопится количество букв слова. Далее нужно вывести в нужную ячейку массива ячейка масива с номером равным количеству букв. и так повторить снова со следующим словом. Может грубо сформулировано но как понимаю она должна работать. Поправьте пожалуйста если что то не так.

Автор: Алексей1153 30.1.2011, 17:58

micro-chipset, это ты уже почти программу пытаешься написать, только словами :) А алгоритм будет примерно такой:

1) инициализировать переменные и массив, в котором хранится текущее содержимое гистограммы
2) считать очередное слово. Определить его длину, инкрементировать нужный счётчик в массиве гистограммы
3) если строка не закончилась, перейти к пункту 2
4) иначе - распечатать гистограмму
5) ждать нажатие клавиши
6) выйти

Автор: micro-chipset 30.1.2011, 18:06

Цитата(Алексей1153 @ 30.1.2011, 17:58) *
micro-chipset, это ты уже почти программу пытаешься написать, только словами :) А алгоритм будет примерно такой:

1) инициализировать переменные и массив, в котором хранится текущее содержимое гистограммы
2) считать очередное слово. Определить его длину, инкрементировать нужный счётчик в массиве гистограммы
3) если строка не закончилась, перейти к пункту 2
4) иначе - распечатать гистограмму
5) ждать нажатие клавиши
6) выйти


Тут все понятно только наверное не согласен с пунктом 3 думаю не строка а входной поток(проверить через EOF) строк может быть и несколько. И дальше как это реализовать в программе с алгоритмом все ясно.

Автор: Алексей1153 30.1.2011, 18:54

строку можно тоже представить потоком, а новые строки разделены символами возврата каретки и перевода строки .

А дальше берёшь каждый пункт основного плана, так сказать, и детализируешь

1 - это уже сделали выше.

2 - например, сделай процедуру, которая
на вход получает:
-начало потока (вернее, текущее место, откуда начинать),
-максимальная длина, которую разрешено обработать,

возвращает:
-длину слова
-позицию начала ещё не обработанных символов

прототип примерно такой

const char* void F(const char* beg, const int len, int& wordlen);

Автор: Iron Bug 31.1.2011, 19:08

Цитата(micro-chipset @ 29.1.2011, 16:43) *
enum { arrlen=15, };

в этом куске неявно определён НЕИМЕНОВАННЫЙ элемент. в хвосте, перед закрывающей скобкой, не должно быть запятой. иначе компилятор считает, что там ДВА элемента и при переборе констант он таки будет считать, что есть неименованный элемент, значение которого равно 16.
по-моему, нет смысла юзать enum там, где можно обойтись define'ами (поверьте старому системному программисту) :) если хотите проверки типов, определяйте дефайн с явным приведением типа, например: #define arrlen ((size_t)15)

далее, ошибка в самой исходной программе в определении j: если ты сканируешь алфавитный диапазон, то смещение первой буквы будет j='a'; (это ASCII код 61h) а у тебя смещение фиг знает где и length[c-j] где-то явно вне границ диапзона length. да и сама строка ++length[c-j]; стоит не в том месте, где полагается - она плюсует количество слов на каждой букве.
в общем, проверяй диапазоны и логику.

Автор: Алексей1153 31.1.2011, 19:18

ну, запятую я случайно оставил, да.


Цитата(Iron Bug @ 31.1.2011, 21:08) *
в этом куске неявно определён НЕИМЕНОВАННЫЙ элемент. в хвосте, перед закрывающей скобкой, не должно быть запятой. иначе компилятор считает, что там ДВА элемента и при переборе констант он таки будет считать, что есть неименованный элемент, значение которого равно 16

а доказательства ? И какие-то разве последствия будут ? Я не просто так спрашиваю, дело в том, что у меня довольно часто вот так запятая там стоит - по причине визуального выравнивания кода (ну это долгая и никому неинтересная история), однако никогда ошибок или проблем не было. Создаётся впечатление, что пустоту после запятой в енуме компилятор просто игнорирует. А перечислял я и маски, и инкрементные идешники. Никогда ничего не покривилось ) Ежели тут есть опасность, то перестану так делать

Автор: Iron Bug 31.1.2011, 19:44

Цитата(Алексей1153 @ 31.1.2011, 21:18) *
а доказательства ? И какие-то разве последствия будут ?

это определение структуры языка. в enum можно пропускать символические константы (типа { one=1, ,three=3,,,six=6 }). последняя константа - не исключение. можешь проверить, у меня просто было достаточно опыта с такими глюками. потенциальные проблемы - в пропускании тегов посередине. если потом используются манипуляции с последовательной нумерацией. не смертельно, но найти сложно. хотя, может, не все компиляторы такое позволяют, но у меня точно была такая проблема однажды.

Автор: Алексей1153 31.1.2011, 20:02

Iron Bug, так я же не спорю, что если в середине пропустить, будет лишний инкремент. Но вот с последней запятой - что страшного ? И даже не узнать, что там компилятор сделал дальше :)

Автор: DEADHUNT 31.1.2011, 20:43

Цитата(Iron Bug @ 31.1.2011, 19:44) *
это определение структуры языка. в enum можно пропускать символические константы (типа { one=1, ,three=3,,,six=6 }).

вообще-то так нельзя делать. ISO/IEC 9899(Programming Language C):
Цитата
enum-specifier:
enum identifieropt { enumerator-list }
enum identifieropt { enumerator-list , }
enum identifier
enumerator-list:
enumerator
enumerator-list , enumerator
enumerator:
enumeration-constant
enumeration-constant = constant-expression

Автор: Алексей1153 31.1.2011, 21:08

Цитата(DEADHUNT @ 31.1.2011, 22:43) *
enum identifieropt { enumerator-list , }

о! Они знали, что я буду этим пользоваться :D

Автор: Iron Bug 31.1.2011, 21:41

Цитата(DEADHUNT @ 31.1.2011, 22:43) *
вообще-то так нельзя делать. ISO/IEC 9899(Programming Language C):

а, наверное, это в плюсах.

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

Автор: Litkevich Yuriy 31.1.2011, 22:02

DEADHUNT, нет ли у тебя в закромах ссылки на текст стандарта? А то я как-то рылся по инету и пришёл к выводу, что это только за деньги, либо обрывки (кто, что выложил).

Автор: Rocky 31.1.2011, 22:05

У меня был где-то... если нужен поищу.. дай мыло скину )

Автор: DEADHUNT 31.1.2011, 23:02

Цитата(Iron Bug @ 31.1.2011, 21:41) *
а, наверное, это в плюсах.

нету такого ни в C ни в C++.

Цитата(Litkevich Yuriy @ 31.1.2011, 22:02) *
DEADHUNT, нет ли у тебя в закромах ссылки на текст стандарта? А то я как-то рылся по инету и пришёл к выводу, что это только за деньги, либо обрывки (кто, что выложил).

официальные draft'ы: open-std.org (они бесплатные, далее стандартизируются и оф. стандарт платный)

Автор: Iron Bug 1.2.2011, 14:05

Цитата(DEADHUNT @ 1.2.2011, 1:02) *
нету такого ни в C ни в C++.

как минимум gcc и msvc это поддерживают. и сдаётся мне, что есть такой стандарт, ибо я это не из головы самостоятельно придумала.

Автор: DEADHUNT 1.2.2011, 15:48

Цитата(Iron Bug @ 1.2.2011, 14:05) *
как минимум gcc и msvc это поддерживают. и сдаётся мне, что есть такой стандарт, ибо я это не из головы самостоятельно придумала.

может не стандартное расширение

не скомпилировалось: http://codepad.org/dhHhA6n1

Цитата
$ g++ test.cpp
test.cpp:5: error: expected identifier before ‘,’ token
test.cpp:5: error: expected identifier before ‘,’ token

Автор: igor_bogomolov 1.2.2011, 16:48

У меня компилируется. Но значения не сдвигаются
gcc version 4.4.5 20101112 (ALT Linux 4.4.5-alt3) (GCC)

Автор: Jason-GTK 9.5.2014, 1:16


как-то так ... сам недавно начал .
#include <stdio.h>

#define IN 1
#define OUT 0

int main()
{
    int c, i, wlength=0, state=OUT;
    int ngismo[32];
    for(i=0;i<32;++i)
    {
        ngismo[i]='\0';
    }
    while((c=getchar())!=EOF)
    {
        if((c>='a' && c<='z') || (c>='A' && c<='Z'))
        {
            ++wlength;
            state=IN;
        }
        else if(state==IN)
        {
            ++ngismo[wlength];
            state=OUT;
            wlength=0;
        }
    }
    for(i=0;i<32;i++)
    {
        printf("%2d ",i+1);
        for(wlength=0;wlength<32;wlength++)
        {
            if((ngismo[wlength]-1)>-1)
            {
                printf("%2s ","*");
                ngismo[wlength]--;
            }
            else
            {
                printf("%2s ","-");
            }
        }
        putchar('\n');
    }
    printf("%3s"," ");
    for(i=0;i<32;i++)
    {
        printf("%2d ",i);
    }
    putchar('\n');
    return 0;
}

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

Автор: Jason-GTK 11.5.2014, 5:02

Может на этом форуме и нет живых , но для случайных посетителей оставлю здесь поправленный код с книги Кернигана (2 и 3 издание) . Ибо у меня не заводилось с первого раза с главы 1.9. Символьные массивы .

Раскрывающийся текст
:p
#include <stdio.h>

#define MAXLINE 1000

void copies(char to[], char from[])
{
    int c=0;
    while((to[c]=from[c])!='\0')
    {
        ++c;
    }
}

int bandget(char s[], int lim)
{
    int i;
    for(i=0;(i<lim-1) && (s[i]=getchar())!=EOF && s[i]!='\n';++i);
    if(s[i]=='\n')
    {
        s[i]='\n';
        ++i;
    }
    s[i]='\0';
    return i;
}

int main()
{
    int max=0,len=0;
    char arr[MAXLINE],mas[MAXLINE];
    while((len=bandget(arr,MAXLINE))>0)
    {
        if(len>max)
        {
            max=len;
            copies(mas,arr);
        }
    }
    if(max>0)
    {
        printf("%s\n",mas);
    }
    return 0;
}

Автор: Martein 3.7.2014, 11:18

Цитата(micro-chipset @ 29.1.2011, 14:43) *
Ошибки компилятора

gcc power.c -o power
power.c: В функции ‘main’:
power.c:21: предупреждение: несовместимая неявная декларация внутренней функции ‘memset’


Слушайте, а где это мне нарыть русскоязычный компилятор?

Автор: lanz 3.7.2014, 20:23

Студию Express можно поставить русскоязычную. Я с ней намучился когда к Qt Creator подключал :lol:

Автор: Iron Bug 4.7.2014, 7:49

Цитата(Martein @ 3.7.2014, 14:18) *
Слушайте, а где это мне нарыть русскоязычный компилятор?

а зачем? программист в любом случае должен свободно читать по-английски. потому что в 99% случаев никто ему не будет переводить документацию. так что если собираешься работать программистом - лучше сразу как можно раньше начинать изучать английский, хотя бы технический.
ну и при использовании русскоязычного компилятора могут возникнуть проблемы взаимодействия с другими программистами: я, например, не могу читать вывод русскоязычных компиляторов и затрудняюсь дать какой-либо совет, когда человек приводит такой вывод. слишком сложно догадаться, что имелось в виду, когда читаешь ужасы перевода сообщений компилятора на русский язык :)

Автор: Анна 30.7.2014, 13:42

Цитата(Iron Bug @ 4.7.2014, 8:49) *
Цитата(Martein @ 3.7.2014, 14:18) *
Слушайте, а где это мне нарыть русскоязычный компилятор?

а зачем? программист в любом случае должен свободно читать по-английски. потому что в 99% случаев никто ему не будет переводить документацию. так что если собираешься работать программистом - лучше сразу как можно раньше начинать изучать английский, хотя бы технический.
ну и при использовании русскоязычного компилятора могут возникнуть проблемы взаимодействия с другими программистами: я, например, не могу читать вывод русскоязычных компиляторов и затрудняюсь дать какой-либо совет, когда человек приводит такой вывод. слишком сложно догадаться, что имелось в виду, когда читаешь ужасы перевода сообщений компилятора на русский язык :)


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



Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)