Тема: С. Повернути масив масивів символів

Задача: зчитати з файлу слова і дещо з ними зробити. У файлі буде кілька груп слів, у кожній групі спочатку йде число - кількість слів, а далі самі слова, тобто структура ось така:

5
i
like
to
drink
beer

Хочу вирішити задачу так, щоб було гарно і доступно учням/студентам. Але як пайтоно-джаваскрипто-рубісту важкувато приходиться у світі С :)
Наразі перший шматок задачі Я зробив ось так:

Прихований текст

#include<stdio.h>
#include<string.h>

const int MAX_WORDS = 10;
const int WORD_LENGTH = 15;

//typedef here can't take variables, even const, why?
typedef char word_array[10][15];

int read_words(FILE *f, int words_count, word_array words)
{
    int word;
    for (word=0; word<words_count; word++)
    {
        fscanf(f, "%s", &words[word]);
    }
    return 0;
}

int main()
{
    // read input data
    int i,d,p,r;
    FILE *fi, *fo;
    fi = fopen("sentence_input.txt", "r");

    word_array sentence1;

    fscanf(fi, "%d", &i);
    read_words(fi, i, &sentence1);

    // just printing to make sure it works
    for (int word=0; word<i; word++)
    {
      printf("%s => ", sentence1[word]);
      printf("%d\n", strlen(sentence1[word]));
    }
    fclose(fi);
}

Воно працює:

./sentence 
i => 1
like => 4
to => 2
drink => 5
beer => 4

Але:

1) чому в typedef не можна задати розміри як константи, адже це не просто змінні, а саме константи?
2) компіляція стандартним gnu компілятором (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4) ) видає ще ось такі попердження:

Прихований текст

$ make sentence
cc     sentence.c   -o sentence
sentence.c: In function ‘read_words’:
sentence.c:14:19: warning: format ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘char (*)[15]’ [-Wformat=]
         fscanf(f, "%s", &words[word]);
                   ^
sentence.c: In function ‘main’:
sentence.c:29:23: warning: passing argument 3 of ‘read_words’ from incompatible pointer type [-Wincompatible-pointer-types]
     read_words(fi, i, &nouns);
                       ^
sentence.c:9:5: note: expected ‘char (*)[15]’ but argument is of type ‘char (*)[10][15]’
 int read_words(FILE *f, int words_count, word_array words)
     ^
sentence.c:33:14: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘size_t {aka long unsigned int}’ [-Wformat=]
       printf("%d\n", strlen(nouns[word]));
              ^

3) як правильно повернути із функції такий масив "слів", тобто return words і тоді присвоїти його у змінну у головній функції? (певно, через вказівники, бо в C хіба копіювати strcpy чи ше шось таке). Функція точно потрібна, бо групи слів будуть читатися неоднократно;
4) чи може є якийсь ще кращий підхід зчитування з файлу слів і заповнення масиву?

Мій блог про ОС сімейства *nix - http://nixtravelling.blogspot.com/

2

Re: С. Повернути масив масивів символів

1. Тому що значення констант, насправді, може змінитися. Вам для такої конструкції потрібні макроси (а насправді треба це все робити динамічним через malloc/calloc).
2. Ну і правильно. Вам треба

fscanf(f, "%s", &words[word]);
read_words(fi, i, nouns);
printf("%zu\n", strlen(nouns[word]));

робити. Так, це буквоїдство.
3. Передавайте у функцію посилання на масив і у ній його змінюйте - як, власне, ви і робите. Так, це не функціональне програмування.
4. Кращий з якого боку? Для початківця - важко щось краще придумати. Для серйозного програмування - динамічне виділення пам'яті, буфери і обробка помилок.

Подякували: Master_Sergius1

3 Востаннє редагувалося Master_Sergius (05.12.2016 12:51:54)

Re: С. Повернути масив масивів символів

А оцей ворнінг то лишається?

sentence.c:14:19: warning: format ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘char (*)[15]’ [-Wformat=]
         fscanf(f, "%s", &words[word]);
Мій блог про ОС сімейства *nix - http://nixtravelling.blogspot.com/
Подякували: koala1

4

Re: С. Повернути масив масивів символів

Упс, вибачте - у себе набрав, а тут просто скопіював...

fscanf(f, "%s", words[word]);

5

Re: С. Повернути масив масивів символів

Тоді таке питання, як воно працює, якщо ми забираємо взяття адреси і тут і при виклику функції?
Адже в такому випадку, хіба не мав би аргумент передатися просто у функцію без side-effects? Тобто, зміна в середині функції не повинна впливати на той параметр, що передався зовні?

Мій блог про ОС сімейства *nix - http://nixtravelling.blogspot.com/

6

Re: С. Повернути масив масивів символів

Аргумент - це назва масиву. Назва масиву в C майже завжди (окрім sizeof) означає адресу нульового елементу. Якщо ви хочете підкреслити, що передаєте посилання - пишіть &words[word][0].

Подякували: Master_Sergius1

7 Востаннє редагувалося #Sparta (05.12.2016 13:12:20)

Re: С. Повернути масив масивів символів

Добрий день :)
1) Спробував в себе в студії - не свариться. Можливо діло в версії (бо інакше я сам не знаю) :)
2) Ну я зрозумів, що тут :

fscanf(f, "%s", &words[word]);

ви маєте тип char ** а передаєте char* чи щось в тому роді. Тому напевне Вам відповісти не можу.
3)Ну якщо ви хочете передати свій масив слів як параметр функції щоб туди записати слова, то я б так зроби би.

void read_words(FILE *f, int wordsCount, char **words)
{
    for (int word = 0; word < wordsCount; word++)
    {
        fscanf(f, "%s", &(*words[word])); 
    }
    //ну і нічого не треба return нити:)
}

4) Краще, мабуть, було б спочатку зчитувати к-сть слів(те число). Потім виділяти динамічно пам'ять під них (char**). Звісно, ви не будете знати довжини наперед, тому я б виділяв фіксовану кількість під слова (ваша змінна WORD_LENGTH.
Ну взагалі кажучи сам нубаська в цих ділах. Тому, можливо, Вам хтось щось краще порадить.

Ахаха:) Пан koala вже все пояснив.

Студент НУ "ЛП".
Подякували: Master_Sergius1

8

Re: С. Повернути масив масивів символів

Ясненько, дякую. Інколи сідаю за С, коли є цікава задачка з великою кількістю циклів, перебір чи ще щось таке. Швидкість порівняно з Python/Ruby просто неймовірна. Але трохи більше траходрому при написанні :)

Мій блог про ОС сімейства *nix - http://nixtravelling.blogspot.com/

9

Re: С. Повернути масив масивів символів

Але як пайтоно-джаваскрипто-рубісту важкувато приходиться у світі С

треба було починати з С і було б легше

- Поганому трояну фаєрвол заважає
- Ніколи не програмуйте та не пийте пиво
Якщо ви з першого разу написали програму, в якій немає жодної помилки, повідомте про це системного програмісту: він виправить помилки в компіляторі

10

Re: С. Повернути масив масивів символів

reverse2500 написав:

Але як пайтоно-джаваскрипто-рубісту важкувато приходиться у світі С

треба було починати з С і було б легше

Треба було починати з проектування простеньких ЕОМ. Тоді перехід спершу на RISC-програмування (в маш. кодах), потім на CISC, далі Асемблер, а тоді і C легко буде вчити.

Подякували: Master_Sergius1

11

Re: С. Повернути масив масивів символів

reverse2500 написав:

Але як пайтоно-джаваскрипто-рубісту важкувато приходиться у світі С

треба було починати з С і було б легше

А життя тра було починати в Швейцарії? :)
Як сталося, так сталося... а починав Я, насправді, ще з Basic ;)

Мій блог про ОС сімейства *nix - http://nixtravelling.blogspot.com/

12

Re: С. Повернути масив масивів символів

Треба було починати з проектування простеньких ЕОМ.

у кожного свій шдях до дзен програмування

- Поганому трояну фаєрвол заважає
- Ніколи не програмуйте та не пийте пиво
Якщо ви з першого разу написали програму, в якій немає жодної помилки, повідомте про це системного програмісту: він виправить помилки в компіляторі
Подякували: #Sparta1