C: Отсутствует некоторая логика с указателями - PullRequest
0 голосов
/ 29 января 2010

Я пишу свою собственную функцию копирования строк. Следующие работы:

char *src, *dest;
src = (char *) malloc(BUFFSIZE);
//Do something to fill the src
dest = (char *) malloc(strlen(src) + 1);
mystringcpy(src, dest);

void mystringcopy(char *src, char *dest) {
   for(; (*dest = *src) != '\0'; ++src, +dest);
}

Но это не работает:

char *src, *dest;
src = (char *) malloc(BUFFSIZE);
//Do something to fill the src
mystringcpy(src, strlen(src), dest);

void mystringcopy(char *src, size_t length, char *dest) {
   dest = (char *)malloc(length + 1);
   for(; (*dest = *src) != '\0'; ++src, +dest);
}

и я не могу понять, почему ... Распределение памяти внутри вызываемой функции - ошибка?

Ответы [ 5 ]

2 голосов
/ 29 января 2010

Параметры в C передаются по значению, поэтому ваша функция получает копию указателя dest, перезаписывает его с помощью malloc и затем отбрасывает. Попробуйте вместо этого:

void mystringcopy(char *src, size_t length, char **dest) {
   *dest = (char *)malloc(length + 1);
   char *p=*dest;
   for(; (*p = *src) != '\0'; ++src, ++p);
}

Теперь вы передаете указатель на указатель на вашу строку, чтобы вы могли перезаписать его в основной процедуре. Вы бы использовали это как:

char *src, *dest;
*src = (char *) malloc(BUFFSIZE);
//Do something to fill the src
mystringcpy(src, strlen(src), &dest);
// now in dest you have your copy
2 голосов
/ 29 января 2010

Нет проблем с размещением внутри функции.

Проблема в том, что в Си аргументы передаются по значению. Поэтому, когда вы присваиваете значение dest, это только модифицирует dest локально для функции.

У вас есть два варианта. Вы можете вернуть указатель dest:

char *alloc_and_copy(const char *src, size_t length)
{
    char *dest = malloc(length + 1);
    ... do your copying
    return dest;
}

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

void alloc_and_copy(const char *src, size_t length, char **dest)
{
    char *local_dest = malloc(length + 1);
    ... do your copying using local_dest

    *dest = local_dest;
}

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

2 голосов
/ 29 января 2010

Выполнение malloc внутри функции - это нормально, но вы не передаете указатель обратно из функции. Либо верните указатель:

char * mystringcopy(char *src)

или передать указатель на указатель:

void mystringcopy(char *src, char **dest)
2 голосов
/ 29 января 2010

Вы на самом деле не сказали, что означает "работает", но я предполагаю, что вы запутались, почему dest не изменяется на новую память в вызывающей функции.

Причина в том, что в вашей функции mystringcopy параметр dest является копией указателя dest в вызывающей функции.

Затем вы назначаете эту копию новому буферу, делаете копию, и затем копия уходит. Оригинал без изменений. Вам нужно передать dest как указатель (на указатель).

Кроме того, я предполагаю, что вы написали то, что сделали из памяти, поскольку она не должна компилироваться как есть (плохая разыменование в вызывающей функции). Вот фиксированный код:

char *src, *dest;
src = (char *)malloc(BUFFSIZE); // no dereference on src, it's a pointer

//Do something to fill the src
mystringcpy(src, strlen(src), &dest); // pass the address of dest

// take a pointer to a char*
void mystringcopy(char *src, size_t length, char **dest) {
    // now you should dereference dest, to assign to
    // the char* that was passed in
    *dest = (char *)malloc(length + 1);

    // for simplicity, make an auxiliary dest
    char* destAux = *dest;

    // and now the code is the same
    for(; (*destAux = *src) != '\0'; ++src, ++destAux);
}

Другой метод - вернуть указатель dest:

char *src, *dest;
src = (char *)malloc(BUFFSIZE);

//Do something to fill the src
dest = mystringcpy(src, strlen(src)); // assign dest

char* mystringcopy(char *src, size_t length) {
    char* dest = (char *)malloc(length + 1);

    // for simplicity, make an auxiliary dest
    char* destAux = dest;

    for(; (*destAux = *src) != '\0'; ++src, ++destAux);

    return dest; // give it back
}

Имейте в виду, что если длина меньше реальной длины исходного буфера, вы переполните целевой буфер. См. Комментарии для решения, хотя это остается за вами.

1 голос
/ 29 января 2010

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

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

Или измените параметр char *dest на char **dest. Затем вызовите функцию как: mystringcopy(src, strlen(src), *dest). А в функции он возвращает указатель на: *dest = (char *)malloc(length + 1);. Не очень.

...