c char * вопрос - PullRequest
       22

c char * вопрос

4 голосов
/ 15 марта 2011

Это абсолютный нубский вопрос, но я, кажется, нигде не нашел подходящего ответа, так что вот так, учитывая этот код:

#include <stdio.h>
#include <stdlib.h>

void initialize( char * buffer)
{
    buffer = malloc(1);
    *buffer='a';
}


int main(void) {
    char * buff;
    buff = malloc(sizeof(char));
    *buff = 'b';
    initialize(buff);
    puts("Contents of buffer are: ");
    puts(buff);
    return 0;
}

Вывод main всегда равен 'b',что действительно странно для меня.Я думал, что, передав указатель для инициализации, я мог бы изменить значение указателя, объявленного в main, но по какой-то причине он, похоже, передает переменную по значению, и при возврате у меня есть значения, указанные в main, таким образом, 'b',

Я хотел бы знать, почему это ... я должен вместо этого передать ссылку на указатель?что-то вроде char * & ??

С уважением, Алекс

Ответы [ 6 ]

5 голосов
/ 15 марта 2011

Первое, что вы делаете в своей функции, это переназначаете другое место в памяти на buffer с новым malloc.Затем вы изменяете эту новую ячейку памяти, но buff в основной функции не изменяется, поскольку она по-прежнему указывает на старое местоположение.

Если вы удалите malloc, он будет работать:

void initialize( char * buffer) {
    *buffer='a';
}

Теперь мы модифицируем начальную ячейку памяти.

1 голос
/ 15 марта 2011

Если предпочтителен фрагмент без ошибок без утечек памяти, вот некоторые альтернативы уже опубликованным, более применимые к программированию в реальном мире.

Обратите внимание, что тот же «модуль кода»что мальлоки тоже должны быть свободными. Не используйте malloc в одном модуле и бесплатно в другом, что является очень плохой практикой!

Версия 1:

#include <stdio.h>
#include <stdlib.h>

void set (char* buffer)
{
    *buffer='a';
}


int main (void)
{
    char* buff;

    buff = malloc(sizeof(char));  /* main allocates */
    *buff = 'b';

    set (buff);

    puts("Contents of buffer are: ");
    printf("%c", *buff);  /* don't use puts on single characters! */

    free(buff); /* main cleans up its own mess */
    return 0;
}

Версия 2:

/* main.c */
#include <stdio.h>
#include <stdlib.h>
#include "my_functions.h"


int main (void)
{
    char * buff;

    buff = malloc(sizeof(char));
    *buff = 'b';

    alloc_and_set (&buff);

    puts("Contents of buffer are: ");
    printf("%c", *buff);

    cleanup();
    return 0;
}

my_functions.h

#ifndef MY_FUNCTIONS_H
#define MY_FUNCTIONS_H

void alloc_and_set (char** buffer);
void cleanup (void);

#endif /* MY_FUNCTIONS_H */

my_functions.c

#include "my_functions.h"

static char* internal_buffer = NULL;  /* used to keep track of allocated data */

void alloc_and_set (char** buffer)
{
  if(internal_buffer == NULL)  /* make function memory leak proof */
  {
    internal_buffer = malloc(sizeof(char));
  }

  *internal_buffer = 'a';
  *buffer = internal_buffer;
}

void cleanup (void)
{
    free(internal_buffer);   /* my_functions module cleans up its own mess */
    internal_buffer = NULL;
}
1 голос
/ 15 марта 2011

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

void initialize(char **buffer)
{
    *buffer = malloc(sizeof(char));
    **buffer='a';
}

int main(void) {
    char *buff;
    buff = malloc(sizeof(char));
    *buff = 'b';
    initialize(&buff);
    puts("Contents of buffer are: ");
    printf("%c\n", *buff);
    return 0;
}

Ответы на комментарии ниже:

Я не понимаю, почему указатель на указатель все еще нужен ... это как ссылка в C ++?

Когда я читал ваш код, я подумал, что вы хотите изменить новый буфер, на который указывает переменная buffmain()). Я подумал об этом, когда увидел, что вы выделяете новый буфер в вашей функции initialize(). Чтобы иметь возможность изменить значение переменной из другого места (или функции), вам потребуется адрес этой переменной. Поскольку изначально это был символьный указатель char *, вам понадобился бы указатель на этот символьный указатель char **. И да, это похоже на ссылку в C ++, только более многословно.

Чтобы я мог изменить значение переданной переменной?

Если вы намеревались просто изменить содержимое буфера, который был передан, Krtek покрывает это. Все, что вам нужно было сделать, это исключить новое выделение буфера и изменить то, на что указывал буфер.

void initialize(char *buffer)
{
    /* *buffer = malloc(sizeof(char)); */ /* do not need this */
    /* modify the buffer that was passed in */
    *buffer='a';
}

Относительно проблемы утечки памяти

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

char *buff = malloc(sizeof(char)); /* allocate some memory */
buff = malloc(sizeof(char));       /* oops, memory leak */

Вы имеете в виду использование put с ненулевой строкой? Поэтому вы используете printf ("% c \ n", * buff)? принудительное завершение строки \ n?

Это была другая проблема здесь. puts() принимает строку с нулевым символом в конце и выводит ее на стандартный вывод, за которым следует новая строка. Это не будет утечка памяти для печати потенциально мусорного текста, это просто ... что-то еще. Ваш буфер содержит пространство только для одного символа (без нулевого терминатора). Поэтому не следует использовать puts() для печати содержимого буфера. Вы должны печатать только этот единственный символ. Использование printf() с '\n' должно было привести к тому же выходному поведению, что и puts().

0 голосов
/ 15 марта 2011
void initialize( char * buffer)
{

    // buffer is a local pointer to this function which will have 
    // same value as buff in main AT THIS POINT.

    // now you call malloc to allocate memory and make buffer
    // point to that memory. So the value of buffer got changed now.
    // buff in main remains unchanges.
    buffer = malloc(1);

    // now you write to the memory allocated.
    *buffer='a';

    // key point is buff in main is still pointing where it was.
}


int main(void) {

    // buff is now a wild pointer.
    char * buff;

    // now you allocate memory using malloc and make buff point to that memory.
    buff = malloc(sizeof(char));

    // you write to the memory pointed to by buff.
    *buff = 'b';

    // you pass buff BY VALUE to the function initilize.
    initialize(buff);

    // since a copy of buff (buffer) was changed, buff still points
    // to a memory that has b in it.
    puts("Contents of buffer are: ");
    puts(buff);
    return 0;
}

Если вы хотите, чтобы вызываемая функция влияла на значение buff, вам нужно передать ее адрес:

void initialize( char ** buffer)
{
    *buffer = malloc(1);
    **buffer='a';
}

и вызвать его как:

initialize(&buff);

но учтите, что это приведет к утечке памяти, выделенной в main.Поскольку вы уже выделили память в main, вы можете просто удалить malloc:

void initialize( char * buffer)
{
    *buffer='a';
}
0 голосов
/ 15 марта 2011

Когда вы делаете это

buffer = malloc(1);

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

Если вы напишите это:

void initialize( char **buffer)
{
    *buffer = malloc(1);
    **buffer='a';
}

Тогда вы увидите изменение.

Edit1: Вам нужно понять * оператор.буфер является указателем сам по себе.* На самом деле буфер - это местоположение, на которое указывает буфер, и его изменение не влияет на буфер.

0 голосов
/ 15 марта 2011

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

void initialize( char ** buffer)
{
    *buffer = malloc(1);
    **buffer='a';
}

, затем позвоните initialize(&buff);

...