программисты!
Я полностью утонул в двойном указателе (указатель на указатели)... Здесь много вопросов!
Начнем с этой задачи: я пишу свою версию функции calloc, которая должна возвращать указатель на n элементов памяти размера size. Вот что я изобрел:
void **calloc1(int n, int size)
{
int i, j;
char *tmp, **p = NULL;
tmp = (char *) malloc1(n * size);
p = tmp;
for (i = 0; i < n; i++) {
p[i] = tmp;
for (j = 0; j < size; j++)
tmp++;
}
p = &p[0];
return (void **)p;
}
/* I entered n==4, size==3; real pointers are: p[0] == 0x804cfe0; p[1] == 0x804cfe3; p[2] == 0x804cfe6; ... */
Итак, по сути, я выделяю байты n * size, а затем «назначаю» массив указателей равного «размера» соответствующим начальным позициям. Введем n=4 и size=3; это означает, что p[0] указывает на tmp[0], p[1] на tmp[3], p[2] на tmp[6] и так далее. В GDB я отслеживаю значения указателей почти после каждого шага.
Затем в «main» я объявляю двойной указатель и «прикрепляю» его к буферу, полученному от моего «calloc»:
int main (short argc, char **argv)
{
char **space;
space = (char **) calloc1(n, size); /* removing '(char**)' here does not have effect */
/* at this stage pointers seems to be correctly "located": 'space' == 'space[0]' == 0x804cfe0; 'space[1]' == 0x804cfe3; 'space[2]' == 0x804cfe6; ... */
1) Вот уже первый вопрос: как 'main()' (или любая другая функция, которой я передам копию **p) узнает размер арифметики указателя? Например, откуда 'main()' знает, что если я добавлю '1' к 'space' (или просто увеличим его один раз), он должен указывать на свой второй указатель (в 'calloc' это p[1]), который ( в этом конкретном случае) на 3 символа дальше первого указателя (p[0])? Более того, если я создам в 'alloc' массив указателей на строки с "переменной длиной" (например, p[0] указывает на tmp[0], p[1] на tmp[7], p[2] на tmp [11] и т. д.), как любая другая функция узнает, где она должна увеличить «верхний» указатель на 4, а где на 7 «символов»?
Хорошо, идем дальше, я пытаюсь поместить некоторые символы в полученный буфер:
int i = 0, j = 0, n, size;
char nn, ssize, c, temp[3];
printf ("Enter number/size \n");
sc = scanf ("%c/%c", &nn, &ssize);
n = nn - '0'; /* n==4 */
size = ssize - '0'; /* size==3 */
printf ("Enter a 'number' of words\n");
while (j < n) {
for (i = 0; (c = getchar()) != EOF && i < size; i++)
*(*space)++ = c;
(*space)--; /* this line is unneccesary; if I remove it - nothing changes */
++j;
++space;
}
2) И вот доказательство для первого вопроса: на самом деле, когда я увеличиваю пробел здесь, он перемещается не на 3, а на 4 символа (после первого «++» это 0x804cfe4, после второго 0x804cfe8). Почему? Есть ли какая-то связь с размером типа «плавающий»? После первого такого увеличения '*space' указывает на 0x804cfe6... Я не думаю, что это правильно.
Я попробовал другой способ - ссылаясь на «пространство», как не на указатель, а на массив:
....
while (j < n) {
for (i = 0; (c = getchar()) != EOF && i < size; i++)
*space[j]++ = c;
space[j]--;
++j;
}
3) В этом случае указатели кажутся в порядке - например. пробел[1] == 0x804cfe3, пробел[2] == 0x804cfe6. Проблема в том, что пока этот цикл работает с j == 2, значение 'space[0]' каким-то образом меняется с 0x804cfe2 (перемещено дважды - ок) на что-то вроде 0x6a04cfe2 (что выходит за пределы). Что за х..???
4) И вообще, какое-то странное поведение адресов. Я также пытался не записывать символы напрямую в **space, а использовать функцию копирования строки:
char i, temp[3];
...
while (j < n) {
for (i = 0; (c = getchar()) != EOF && i < size; i++)
temp[i] = c;
strncpy1 (space[j],temp,3);
++j;
}
.....
void strncpy1 (char *s, char *t, int k)
{
while (--k > 0) {
*s = *t;
s++; t++;
}
}
Внутри функции копирования копирование и приращение отображаются в GDB правильно. Но после возврата из 'strncpy1' пробел[j] меняется с 0x804cfe0 на что-то вроде 0x804000a. Как возможно, что вызываемая функция может воздействовать на родительский (внешний) указатель?
Итак, наконец, какой тип указателей на символы? Какой у него размер?
void **
вместо того, чтобы возвращатьvoid *
, как это делает обычный calloc? - person hugomg   schedule 08.05.2015calloc
. Он не выделяет массив указателей. Все, что он делает, это вычисляет n*size байтов и очищает эти байты до нуля. В calloc нет указателей на указатели. Если вы пытаетесь написать что-то другое, пожалуйста, объясните, что вы пытаетесь сделать, потому что ваша программа в данный момент не имеет смысла. - person JS1   schedule 08.05.2015void *calloc(int n, int size) { void *ret = malloc(n*size); memset(ret, 0, n*size); return ret; }
Это все, чем являетсяcalloc()
. Я не имел в виду, что вам нужно смотреть исходный код. Я имел в виду, что вы не поняли, что функция возвращает один единственный указатель. - person JS1   schedule 09.05.2015