Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Начал изучать Си. Первые трудности.
Форум на CrossPlatform.RU > Разработка > С\С++
micro-chipset
Возникла проблема со следующей задачей. Задача из из книги Б. Керниган, Д. Ритчи "Язык программирования Си" Глава 1.6 упражнение 1.13 Книгу онлайн можно найти тут

Сама задача
Програма должна выводить гистограмму длин слов во входном потоке. т.е. надо чтобы выводился массив допустим длинной 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
а почему именно 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
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
Цитата(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
Цитата(Алексей1153 @ 29.1.2011, 18:17) *
причём, эта константа будет иметь область видимости
фразу-то не закончил. Какую область видимости будет иметь константа?
DEADHUNT
Цитата(Алексей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
Кстати, а в ANSI C есть операторы "++" и "--" постфиксной и префиксной форм? (имеющие тот же смысл что и в С++)

Я просто помню помню фразу преподователя со 2-го курса института "Знаменитый оператор ++ языка С++....." которая предполагала, что все это было введено уже в С++.... А т.к. компилятора ANSI C у меня никогда не было =), я так и остался в неведении...
BRE
Цитата(Rocky @ 29.1.2011, 21:01) *
Кстати, а в ANSI C есть операторы "++" и "--" постфиксной и префиксной форм? (имеющие тот же смысл что и в С++)

Есть.
Rocky
Буду знать, пасиб)
micro-chipset
Программа компилируется и работает. Но выдает не верный результат. Проверяю так:
./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
файл utf8, а ты его как ASCII читаешь.
micro-chipset
Цитата(DEADHUNT @ 30.1.2011, 11:27) *
файл utf8, а ты его как ASCII читаешь.

А как поправить?
Алексей1153
Цитата(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
А все же что с решением задачки. Работает она не верно же
Алексей1153
micro-chipset, покажи алгоритм на обычном русском языке - что ты делаешь. Без алгоритма не напишешь и не отладишь
DEADHUNT
Цитата(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
Цитата(DEADHUNT @ 30.1.2011, 17:12) *
Цитата(micro-chipset @ 30.1.2011, 11:34) *
А как поправить?

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

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

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

1) инициализировать переменные и массив, в котором хранится текущее содержимое гистограммы
2) считать очередное слово. Определить его длину, инкрементировать нужный счётчик в массиве гистограммы
3) если строка не закончилась, перейти к пункту 2
4) иначе - распечатать гистограмму
5) ждать нажатие клавиши
6) выйти
micro-chipset
Цитата(Алексей1153 @ 30.1.2011, 17:58) *
micro-chipset, это ты уже почти программу пытаешься написать, только словами :) А алгоритм будет примерно такой:

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


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

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

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

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

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

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

const char* void F(const char* beg, const int len, int& wordlen);
Iron Bug
Цитата(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
ну, запятую я случайно оставил, да.


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

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

это определение структуры языка. в enum можно пропускать символические константы (типа { one=1, ,three=3,,,six=6 }). последняя константа - не исключение. можешь проверить, у меня просто было достаточно опыта с такими глюками. потенциальные проблемы - в пропускании тегов посередине. если потом используются манипуляции с последовательной нумерацией. не смертельно, но найти сложно. хотя, может, не все компиляторы такое позволяют, но у меня точно была такая проблема однажды.
Алексей1153
Iron Bug, так я же не спорю, что если в середине пропустить, будет лишний инкремент. Но вот с последней запятой - что страшного ? И даже не узнать, что там компилятор сделал дальше :)
DEADHUNT
Цитата(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
Цитата(DEADHUNT @ 31.1.2011, 22:43) *
enum identifieropt { enumerator-list , }

о! Они знали, что я буду этим пользоваться :D
Iron Bug
Цитата(DEADHUNT @ 31.1.2011, 22:43) *
вообще-то так нельзя делать. ISO/IEC 9899(Programming Language C):

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

но я помню, что однажды был какой-то страшный гемор из-за этих запятых у меня в одном из проектов. деталей не помню, ибо проектов просто до чёрта. если вдруг вспомню или наткнусь в коде - сообщу.
Litkevich Yuriy
DEADHUNT, нет ли у тебя в закромах ссылки на текст стандарта? А то я как-то рылся по инету и пришёл к выводу, что это только за деньги, либо обрывки (кто, что выложил).
Rocky
У меня был где-то... если нужен поищу.. дай мыло скину )
DEADHUNT
Цитата(Iron Bug @ 31.1.2011, 21:41) *
а, наверное, это в плюсах.

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

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

официальные draft'ы: open-std.org (они бесплатные, далее стандартизируются и оф. стандарт платный)
Iron Bug
Цитата(DEADHUNT @ 1.2.2011, 1:02) *
нету такого ни в C ни в C++.

как минимум gcc и msvc это поддерживают. и сдаётся мне, что есть такой стандарт, ибо я это не из головы самостоятельно придумала.
DEADHUNT
Цитата(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
У меня компилируется. Но значения не сдвигаются
gcc version 4.4.5 20101112 (ALT Linux 4.4.5-alt3) (GCC)
Jason-GTK
Нажмите для просмотра прикрепленного файла
как-то так ... сам недавно начал .
#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
Может на этом форуме и нет живых , но для случайных посетителей оставлю здесь поправленный код с книги Кернигана (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
Цитата(micro-chipset @ 29.1.2011, 14:43) *
Ошибки компилятора

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


Слушайте, а где это мне нарыть русскоязычный компилятор?
lanz
Студию Express можно поставить русскоязычную. Я с ней намучился когда к Qt Creator подключал :lol:
Iron Bug
Цитата(Martein @ 3.7.2014, 14:18) *
Слушайте, а где это мне нарыть русскоязычный компилятор?

а зачем? программист в любом случае должен свободно читать по-английски. потому что в 99% случаев никто ему не будет переводить документацию. так что если собираешься работать программистом - лучше сразу как можно раньше начинать изучать английский, хотя бы технический.
ну и при использовании русскоязычного компилятора могут возникнуть проблемы взаимодействия с другими программистами: я, например, не могу читать вывод русскоязычных компиляторов и затрудняюсь дать какой-либо совет, когда человек приводит такой вывод. слишком сложно догадаться, что имелось в виду, когда читаешь ужасы перевода сообщений компилятора на русский язык :)
Анна
Цитата(Iron Bug @ 4.7.2014, 8:49) *
Цитата(Martein @ 3.7.2014, 14:18) *
Слушайте, а где это мне нарыть русскоязычный компилятор?

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


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


Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.