LINUX.ORG.RU

Ошибки при сборке тренировочного задания по C

 , , , ,


1

2

Есть код на C, для выполнения тренировочного задания: напишите программу для вывода всех строк входного потока, имеющих длину более 80 символов. Вот код: http://ix.io/12jI

Собираю с помощью GCC, вот выхлоп "cc -v"

Using built-in specs.
COLLECT_GCC=cc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.9.2-10' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --with-arch-32=i586 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.9.2 (Debian 4.9.2-10)
Получаю ошибки:
hw.c:5:5: error: conflicting types for ‘getline’
 int getline(char line[], int limit);
     ^
In file included from hw.c:1:0:
/usr/include/stdio.h:678:20: note: previous declaration of ‘getline’ was here
 extern _IO_ssize_t getline (char **__restrict __lineptr,
                    ^
hw.c:16:5: error: conflicting types for ‘getline’
 int getline(char line[], int limit) {
     ^
In file included from hw.c:1:0:
/usr/include/stdio.h:678:20: note: previous declaration of ‘getline’ was here
 extern _IO_ssize_t getline (char **__restrict __lineptr,
Не могу понять, что не так. Пожалуйста, помогите разобраться.

★★
Ответ на: комментарий от Norong

Почему нет необходимости указывать длину первого измерения, но необходимо указывать длины остальных?

Для индексации массива необходимо знать размер элемента массива. В одномерном массиве это просто размер типа (int, например), а в многомерном это другие массивы. Отсюда и требование. Компилятору нужен sizeof(*array), а это какой-нибудь int [4].

Это относится только к объявлению параметров при определении функции, или к чему-то ещё (например, к прототипам)?

К прототипам и везде в других местах, где хочется иметь указатель на многомерный массив.

Как многомерный массив представляется в памяти?

Последовательно элементы каждого массива на каждом уровне. Т.е. для int arr[3][4]:

Начало здесь (адреса растут вправо и потом вниз)
arr[0] = arr + 0*sizeof(*arr): arr[0][0] arr[0][1] arr[0][2] arr[0][3]
arr[1] = arr + 1*sizeof(*arr): arr[1][0] arr[1][1] arr[1][2] arr[1][3]
arr[2] = arr + 2*sizeof(*arr): arr[2][0] arr[2][1] arr[2][2] arr[2][3]
arr[3] = arr + 3*sizeof(*arr): arr[3][0] arr[3][1] arr[3][2] arr[3][3]

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Для индексации массива необходимо знать размер элемента массива.

Я правильно понимаю, что общий размер этого массива (как длина области памяти, которую он занимает со всеми своими измерениями) будет и так известен при передаче? По аналогии с тем, что если я объявил переменную short, то известно, что она 16 бит занимает. Объявил массив short array[a][j][k] - занимает 16*a*j*k бит.

Norong ★★
() автор топика
Последнее исправление: Norong (всего исправлений: 3)
Ответ на: комментарий от Norong

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

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Вот как я теперь понял ситуацию: есть int arr[a][j][k]. В определении (или прототипе) функции в объявлении параметров указано: int arr[][j][k] и передаётся в функцию указатель на этот массив. Известен размер int, известно k => известен размер arr[M][N]. Известен размер arr[M][N], известно j => известен размер arr[M], но мне неизвестно, чему может быть равен M <=> неизвестен размер массива. Т.е. я могу любое отведённое мне пространство проиндексировать, но разве мне не нужно знать, где оно кончается?

Norong ★★
() автор топика
Последнее исправление: Norong (всего исправлений: 1)
Ответ на: комментарий от Norong

Нужно, но это оставлено на программиста, который может передать размер отдельно. А так можно передать указатель на подмассив и работать с ним. В общем, могли бы сделать и иначе, но для простоты (чтобы не передавать размер массива неявно), сделали простым указателем.

В C99 на самом деле были добавления по этому поводу, но в C11 их сделали опциональными. Оно довольно запутанно и не очень удобно, решили, что программисты разберутся сами.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

И ещё вопрос. В учебнике есть такое место: https://i.imgur.com/u4kn5Jh.png

Первое, что меня смущает - массив объявлен статическим внутри функции. Его имя же вроде в любом случае не должно быть известно снаружи, если он внутри объявлен? Раньше по тексту говорилось, что если внутреннюю переменную объявить статической, то она просто будет сохраняться между вызовами функции (но видна только из неё). Тут, видимо, другой случай? Кроме того, для меня неочевидно, что происходит со строковыми константами (вроде тех, что там). Как я понимаю, где-то выделяется под них память, а наш массив name содержит указатели на начала этих участков памяти. Эта память, получается, выделяется «глобально» и нужные данные там существуют всё время работы программы, а не только пока выполняется функция? Так получается всегда, или есть варианты в зависимости от того, внутри функции происходит объявление (определение?) этих данных или снаружи, или может static играет какую-то роль?

Norong ★★
() автор топика
Ответ на: комментарий от Norong

Тут, видимо, другой случай?

Да нет, тот же. Просто незачем создавать массив на стеке при каждом вызове, поэтому делают во время компиляции.

Эта память, получается, выделяется «глобально» и нужные данные там существуют всё время работы программы, а не только пока выполняется функция?

Да. Это так же как с глобальными/статическими данными. Место резервируется и инициализируется во время компиляции.

Так получается всегда, или есть варианты в зависимости от того, внутри функции происходит объявление (определение?) этих данных или снаружи, или может static играет какую-то роль?

Всегда. Любой строковой литерал лежит где-то в глобальной памяти.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

И ещё один вопрос :)

В учебнике есть вот такой эпизод: https://i.imgur.com/7fQUXur.png

Я правильно понимаю, что int *b[10] это массив из 10 указателей на int? Тогда b[3] - это четвёртый в массиве указатель на int, т.е. указатель на нулевой элемент какого-то массива int'ов, а b[3][4] - это пятый элемент этого массива. Но сказано, что те 10 указателей b[N] не проинициализированы. Без этого, наверное, в переменных b[N] хранится мусор? Куда они указывают? Могут, наверное, указывать и друг на друга, и на одни и те же области, и вообще неведомо куда. То же самое и с теми массивами, которые начинаются по указателям b[N]. Длина не обозначена, объявлений не было; есть ли у нас гарантии, что для них найдётся место, что они не пересекутся...?

Norong ★★
() автор топика
Ответ на: комментарий от Norong

Да, там мусор со стека. Просто не надо использовать те указатели. Никаких массивов по ним нету. Массив это всего лишь способ интерпретации участка памяти, который подразумевает возможность индексации. Если этот указатель покажет куда-то, то содержимое этого места будет обрабатываться как массив, но этот факт сам тот участок памяти ни к чему не обязывает.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Просто не надо использовать те указатели.

В смысле? Зачем-то же про них рассказывают...

там мусор со стека
Если этот указатель покажет куда-то

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

Norong ★★
() автор топика
Ответ на: комментарий от Norong

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

Может. Его надо направить в нужное место, перед использованием.

В смысле? Зачем-то же про них рассказывают...

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

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

А, догнал! Т.е. я объявляю массив указателей как в тексте, и каждый из них пригоден для следующего использования: указывать на массив int'ов. Дальше, если у меня есть на примете готовый массив int'ов или область памяти, которую я хочу использовать, как массив int'ов, то я указываю указателем туда и использую. Ага?

Norong ★★
() автор топика
Ответ на: комментарий от Norong

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

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Т.е. указатель позволяет выйти за пределы того, на что указывает, но если я так сделаю, то попаду неизвестно куда, да?

Norong ★★
() автор топика
Последнее исправление: Norong (всего исправлений: 2)
Ответ на: комментарий от xaizek

Мне тут в задании предлагают такой вот массив:

static char daytab[2][13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
изобразить с помощью указателей. Тут у меня массив daytab из двух элементов, каждый из которых является массивом из 13 элементов типа char.

Я попробовал сделать так:

static char *daytab[2] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
Я вижу себе это так: массив из двух указателей на char, и я его инициализирую двумя объектами. Каждый - это массив char'ов (под эти массивы глобально выделяется память), он же - указатель на нулевой элемент этого массива, и именно эти указатели записываются в daytab.

Однако при использовании этого у меня segfault. Не подскажешь, что я делаю не так?

Norong ★★
() автор топика
Ответ на: комментарий от Norong

Списки инициализации не превращаются в указатели. Эти массивы надо объявлять отдельно. Там, видимо, 0 и 31 были интерпретированы как два указателя.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Выше по тексту был такой пример:

static char *name[] = {
    "Illegal month",
    "January", "February"... и так далее.
};
Чем он принципиально отличается от того случая, о котором мы говорим? Вроде, например, «January» аналогично { 'J', 'a', 'n'... }

Norong ★★
() автор топика
Ответ на: комментарий от Norong

Строковые литералы на особом счету. Они могут вести себя либо как список инициализации, либо как указатель при одинаковом синтаксисе:

char str[] = "bla";
const char *str = "bla";
Но это не работает со списками инициализации в общем случае.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Понял. Т.е. я отдельно объявляю свои массивы чисел, а потом в массив указателей эти массивы кладу?

И ещё вопрос: вчера ты мне объяснил, что под строковые литералы в глобальной памяти место отводится при компиляции. Так только со строковыми литералами, или любой массив также себя поведёт?

Norong ★★
() автор топика
Ответ на: комментарий от Norong

Т.е. я отдельно объявляю свои массивы чисел, а потом в массив указателей эти массивы кладу?

Да.

Так только со строковыми литералами, или любой массив также себя поведёт?

Со строковыми, которые разлагаются до указателя (иначе в этом нет необходимости). Статические/глобальные массивы тоже конечно статически выделяются. Но локальные инициализируются во время выполнения.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

которые разлагаются до указателя

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

Norong ★★
() автор топика
Ответ на: комментарий от xaizek

Т.е. если я его использую, как указатель, то тогда выделяем глобальную память, иначе - по ситуации (в зависимости от того, внешний/статический или нет)?

И ещё один вопрос (как-то у меня указатели нелегко идут). Я дошёл до параграфа про аргументы командной строки. Сказано, что argv - указатель на массив строк-аргументов. В качестве примера в параграфе приведена программа echo, которая воспринимает argv как массив символьных указателей. В ней argv объявлен так:

char *argv[]

и обращение к аргументам происходит по argv[a], т.е. всё действительно так, как если argv - массив символьных указателей (массив строк). Но раньше было сказано, что он - указатель на такой массив. Я понимаю, если бы мне сказали, что он - указатель на нулевой элемент массива. Тогда указатель на нулевой элемент массива == это и есть само имя массива, но тут ситуация другая, видимо. Указатель на массив - это указатель на указатель на нулевой элемент? Не мог бы ты помочь мне понять, что здесь происходит?

Norong ★★
() автор топика
Последнее исправление: Norong (всего исправлений: 2)
Ответ на: комментарий от Norong

Т.е. если я его использую, как указатель, то тогда выделяем глобальную память, иначе - по ситуации (в зависимости от того, внешний/статический или нет)?

Память где-то выделится в любом случае. Просто это будет либо часть объекта, который инициализируется, либо глобально, если объект берёт только указатель.

Я понимаю, если бы мне сказали, что он - указатель на нулевой элемент массива.

Так и есть. Это же эквивалент записи char **argv. Указатель на нулевой элемент массива и считается указателем на весь массив, так как в обоих случаях это одно и то же место в памяти.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Просто это будет либо часть объекта, который инициализируется, либо глобально, если объект берёт только указатель.

Понял, спасибо!

Указатель на нулевой элемент массива и считается указателем на весь массив

Вот он - ключевой момент, которого мне не хватало :) Спасибо!

Norong ★★
() автор топика
Ответ на: комментарий от xaizek

Просто это будет либо часть объекта, который инициализируется, либо глобально, если объект берёт только указатель.

Пост-фактум у меня возникают небольшие вопросы. Правильно ли я понимаю, что первый случай - это

char string[] = «string»

а второй - это

char *pointer = «string»

? Если да, то с точки зрения использования, вроде, разницы особо нет - по string или pointer я получаю указатель на нулевой элемент, по string[N] или pointer[N] - обращение к N-ому элементу. А с точки размещения в памяти во втором случае, как я понял, указатель в одном месте, а сам массив - в другом; а в первом случае они как-то «вместе»? И, прости что к этому возвращаюсь:

Со строковыми, которые разлагаются до указателя (иначе в этом нет необходимости)

- нет необходимости в том, чтобы хранить отдельно указатель и сам массив?

Norong ★★
() автор топика
Ответ на: комментарий от Norong

Правильно ли я понимаю, что первый случай - это
char string[] = «string»
а второй - это
char *pointer = «string»
?

Да.

Если да, то с точки зрения использования, вроде, разницы особо нет

В массиве будет своя копия литерала. А если сделать так:

char *a = "abc";
char *b = "abc";
То a и b могут указывать на одну и ту же строку.

А с точки размещения в памяти во втором случае, как я понял, указатель в одном месте, а сам массив - в другом; а в первом случае они как-то «вместе»?

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

нет необходимости в том, чтобы хранить отдельно указатель и сам массив?

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

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

У меня тут возникла проблема с кодом. Вот он: https://gist.github.com/anonymous/0bc9e7a7f3850dcf68a0c6d1e07679c9

Вот что он должен делать: если программа запущена без аргументов, то во входном потоке заменять каждый таб необходимым числом пробелов до следующей границы табуляции. Если есть аргументы, и они - числовые, то заменять лишь те табы, которые на позициях, указанных аргументами. Почему-то при вводе таких данных, при которых таб стоит не на позиции, указанной в аргументе (если аргументы есть), программа sefault'ится:

(gdb) run
Starting program: /home/user/C/a.out 2 5 8
aТАБ
a\\\
aesdТАБ
aesd
aesdasdТАБ
aesdasd\
aeТАБ

Program received signal SIGSEGV, Segmentation fault.
0x00000000004006d0 in atoi (s=0x0) at 5.11.1.c:52
52	    sign = (*s == '-') ? -1 : 1;
При вызове без аргументов работает без проблем. Меня очень смущает место сегфолта. Эту функцию (atoi) я не раз копировал из старого кода в новый, она, вроде, работает корректно. Аргумент для неё (ожидается строка) - это аргумент самой программы (строка). Кроме того, конкретно та строка, на которой сегфолт, выглядит вполне невинной.

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

Norong ★★
() автор топика
Ответ на: комментарий от xaizek

Ага, догнал! Там i++ всё портит, надо сделать проверку

i < arrlen

и инкрементировать i внутри while. Ну и можно i = 1 в начале сделать, проверка нулевого аргумента мне не нужна.

Спасибо!

Norong ★★
() автор топика
Ответ на: комментарий от xaizek

У меня тут есть программа для сортировки строк, использующая функцию сравнения строк. Эта функция проходит по символам двух данных строк и выводит разность числовых кодов первых разошедшихся символов. Одно из заданий к этой программе гласит:

Добавьте в программу ключ -d (от directory order - упорядочение каталожного типа), при наличии которого сравнение должно выполняться только по буквам, цифрам и пробелам.

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

Пожалуйста, если тебя не затруднит, подскажи, как мне следует понять задание. Заранее спасибо.

Norong ★★
() автор топика
Ответ на: комментарий от Norong

Мда, формулировка туманная. Как вариант, это может значить, что надо просто игнорировать все символы, которые не попадают в указанное множество. То есть вместо простого перехода к следующему символу, надо идти вперёд пока не будет найден признак конца строки или один из символов того множества. И таким образом сравнивать между собой только символы, которые попадают в множество.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Вот что у меня получилось: https://gist.github.com/anonymous/955adbdfa006e1f6d10bc963be1137e6

Функция начинается с 70 строки, обработка аргументов командной строки - с 25.

dirtype = 1 если нужно выполнить то, что в задании.

ignorecase = 1, если нужно игнорировать регистр.

При запуске проги с -d, вводе данных и завершении ввода функция сегфолтится на

Program received signal SIGSEGV, Segmentation fault.
0x00000000004009da in mystrcmp (s=0x60154a <strings+10> "dir/ctory", t=0x601540 <strings> "directory")
    at 5.14-17.c:84
84	                while (!LETNUMSPACE(s[i]))
Относительно предыдущего варианта (до этого задания) в функцию добавилась директива

#define LETNUMSPACE(a) (((a >= 'a' && a <= 'Z') || (a >= '0' && a <= '9') || a == ' ') ? 1 : 0)

для определения, принадлежит ли символ множеству, проверка

|| dirtype

в for,

if (!equal && dirtype) {

со всем содержимым и переменная j (раньше везде было только i, т.к. по строкам шёл одновременно).

Я рассчитывал на то, что если вдруг символы разошлись и dirtype задан, то если какой-то из символов - не из множества, то идём вперёд по той строке, где этот символ, пока не найдём правильный символ. Потом --j и --i - чтобы выполнить ещё одну проверку после ++i, ++j цикла for. Если вдруг в обеих строках символы не из множества попались, и они разные - то переход вперёд должен за два выполнения произойти.

Кажется, я что-то делаю сильно не так. Если вдруг у тебя в какой-то момент появится желание почитать жутковатый код... :)

Norong ★★
() автор топика
Ответ на: комментарий от Norong

надо идти вперёд пока не будет найден признак конца строки или один из символов того множества

признак конца строки

Мне кажется проблема должна быть в этом. Я не вижу проверки в while циклах, и поэтому они могут уйти далеко за пределы строки.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Ага, уловил. \n там принципиально быть не может (обрубается функцией readlines). Добавил проверку на != '\0' и добавил '\0' в допустимые символы:

    #define LETNUMSPACE(a) (((a >= 'a' && a <= 'Z') || (a >= '0' && a <= '9') || a == ' ' || a == '\0') ? 1 : 0)
    #define CSDIFF ('A' - 'a')

    extern int ignorecase;
    extern int dirtype;
    int equal, i, j;

    for (i = 0, j = 0; (equal = (((CAPITAL(s[i]) && ignorecase) ? (s[i] - CSDIFF) : s[i]) == ((CAPITAL(t[j]) && ignorecase) ? (t[j] - CSDIFF) : t[j]))) || dirtype; i++, j++) {
        if (!equal && dirtype) {
            if (!LETNUMSPACE(s[i])) {
                while (!LETNUMSPACE(s[i]) && s[i] != '\0')
                    ++i;
                --i;
                --j;
            }
            else if (!LETNUMSPACE(t[j])) {
                while (!LETNUMSPACE(t[j]) && t[j] != '\0')
                    ++j;
                --i;
                --j;
            }
        }
Сегфолта больше нет, логики в сортировке вывода не вижу. Входные данные:
directory
dirfctory
dirgctory
dir/ctory
dir/story
direstory
dirfstory
dir/file
dir/gile
Выхлоп:
dir/story
directory
direstory
dirgctory
dirfstory
dirfctory
dir/file
dir/ctory
dir/gile

Norong ★★
() автор топика
Последнее исправление: Norong (всего исправлений: 2)
Ответ на: комментарий от Norong

Мне тоже не понятно, как оно должно сортировать. Может есть какие-то другие трактовки. Фиг с ним, если не смогли написать нормально задание.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Судя по всему, ты правильно истрактовал условие: нужно только по буквам-цифрам и пробелам сравнивать строки. Однако, выхлоп в моём предыдущем комменте как-то всё равно не соответствует. Тебе случайно никакой косяк не бросился в глаза по ходу кода? Если нет, то просто перейду к следующему заданию, фиг с ним, действительно :)

Norong ★★
() автор топика
Ответ на: комментарий от Norong

Norong это мой студент пусть мучается леньтяй. Заходи осенью ко мне с конъячком и шоколадом на пересдачу.

anonymous
()
Ответ на: комментарий от xaizek

Здравствуй! У меня тут снова возникли затруднения с кодом.

Суть его в том, чтобы сгруппировать поступающие слова в группы так, чтобы у слов в каждой группе первые 6 символов были одинаковыми. Затем надо вывести эти слова по группам. Первоначально код выглядел вот так: https://gist.github.com/anonymous/0f6c1848e41584a7308680abaae900a1

Проблема возникала в районе строки 59, и заключалась она в том, что под *pointer - указатель на очередную область в памяти для строки - не было выделено место. Если бы я заранее выделил массив для указателей на char, я был бы ограничен в числе слов. Если бы я выделял место под эти указатели с помощью malloc, потребовалось бы где-то хранить указатели на свежевыделенное место (участки памяти были бы разбросаны случайным образом), и опять ограничение. Если я правильно понял, без выделения места под *pointer (а он там каждый раз новый, по одному на каждое слово) - нельзя, потому что могу попасть в уже выделенную область памяти. В этом варианте код работает логически корректно, но время от времени вместо одного из слов я в выводе получаю «мусор» - думаю, как раз из-за невыделения памяти под *pointer.

Я переписал код, создав фиксированной длины массив под указатели на char, но массив объявил как массив указателей на void. Когда я дохожу до конца массива (заполняя его указателями на char), я malloc-ом выделяю место под ещё один такой массив, указатель размещаю в последний элемент предыдущего массива (именно поэтому у меня указатели на void, чтобы я, как я понял, мог записывать туда указатель на что угодно). Таким образом, у меня происходит динамическое выделение массивов под указатели на char. Вот код: https://gist.github.com/anonymous/9b792c447ec7a6ed021b6c3a169474e2

То, о чём я говорю, происходит в строках [59; 75].

Он не компилируется:

6.2.c: In function ‘addtree’:
6.2.c:62:28: warning: dereferencing ‘void *’ pointer
             t = (void **) **(t + ARRSIZE - 1);
                            ^
6.2.c:62:13: error: void value not ignored as it ought to be
             t = (void **) **(t + ARRSIZE - 1);
             ^
6.2.c:65:14: warning: dereferencing ‘void *’ pointer
             t[p->count] = malloc(sizeof(word));
              ^
6.2.c:65:13: error: invalid use of void expression
             t[p->count] = malloc(sizeof(word));
             ^
6.2.c:66:21: warning: dereferencing ‘void *’ pointer
             strcpy(t[p->count], word);
                     ^
6.2.c:66:13: error: invalid use of void expression
             strcpy(t[p->count], word);
             ^
6.2.c:69:14: warning: dereferencing ‘void *’ pointer
             t[p->count] = malloc(sizeof(p->names));
              ^
6.2.c:69:13: error: invalid use of void expression
             t[p->count] = malloc(sizeof(p->names));
             ^
6.2.c:71:20: warning: dereferencing ‘void *’ pointer
             t = *(t[p->count]);
                    ^
6.2.c:71:13: error: void value not ignored as it ought to be
             t = *(t[p->count]);
             ^
6.2.c:72:13: warning: dereferencing ‘void *’ pointer
             *t = malloc(sizeof(word));
             ^
6.2.c:72:13: error: invalid use of void expression
6.2.c:73:20: warning: dereferencing ‘void *’ pointer
             strcpy(*t, word);
                    ^
6.2.c:73:13: error: invalid use of void expression
             strcpy(*t, word);
             ^
По-видимому, я что-то фундаментально недопонял про указатели на void. Сам я надеялся, что в t, являющийся указателем на void, я смогу записывать любой указатель и работать с ним, как с любым указателем. Пожалуйста, если тебе не трудно, помоги мне разобраться в этой ситуации. Заранее большое спасибо.

Norong ★★
() автор топика
Ответ на: комментарий от Norong

В код полностью не вдавался, но в принципе понятно, что не так.

 *p->names = (char *) malloc(sizeof(word));
sizeof() не считает размер строки, а вернёт размер указателя, в таких случах надо делать
 *p->names = (char *) malloc(strlen(word) + 1);
А можно воспользоваться strdup(), которая делает malloc()+strcpy().

Если нужен динамический массив, то надо делать realloc(), который перевыделяет память с изменением размера (данные копируются). Тогда место под новые значения будут.

А с void* проблема в том, что записать в него можно что угодно, но прежде чем вытащить это обратно надо преобразовать к правильному типу. Т.е. преобразование к void* стирает информацию о типе и её надо вернуть, чтобы компилятор смог что-то с этим указателем делать.

Вторая реализация это уже что-то ближе к спискам, можно и так сделать, только проще завести отдельную структуру с полями char *data (можно массивом указателей сделать, если хочется) и struct list_node *next_node и обойтись без указателей на void.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Теорию, вроде, понял. В первом варианте кода поменял sizeof(word) на strlen(word) + 1

В выводе всё равно иногда появляется мусор. Мне кажется, это из-за того, что в 58 строке делаю pointer указателем на то, что после p->names, не выделив прежде это место.

А вот про void*: я попробовал, например, 62-ую строку второго кода сделать такой:

t = (void **) **((void **) t + ARRSIZE - 1);

В моём представлении, это должно было помочь, но почему-то ошибка никуда не делась:

6.2.c:62:13: error: invalid use of void expression
             t = (void **) **((void **) t + ARRSIZE - 1);
             ^

Norong ★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.