Указатели на строку в функциях - PullRequest
5 голосов
/ 07 января 2011

кажется, я застрял с некоторыми основами. Может кто-нибудь объяснить мне, почему следующий код:

#include <stdlib.h>

void Test1(char *t)
{
    t = (char *)malloc(11);
    strcpy(t, "1234567890");
}

void Test2(char **t)
{
    *t = (char *)malloc(11);
    strcpy(*t, "1234567890");
}

void Test3(char *t)
{
    strcpy(t, "1234567890");
}

char * Test4(char *t)
{
    t = (char *)malloc(11);
    strcpy(t, "1234567890");
    return t;
}

int main()
{
    char *t1 = NULL;
    Test1(t1);
    printf("\nTest1: %s\n", t1);

    char *t2 = NULL;
    Test2(&t2);
    printf("\nTest2: %s\n", t2);

    char *t3 = (char *)malloc(11);
    Test3(t3);
    printf("\nTest3: %s\n", t3);

    char *t4 = NULL;
    t4 = Test4(t4);
    printf("\nTest4: %s\n", t4);

    return 0;
}

дает этот вывод:

Test1: (null)

Test2: 1234567890

Test3: 1234567890

Test4: 1234567890

Что не так с функцией Test1? И почему Test4, который почти похож на Test1, работает? Более общий вопрос: как правильно создать строку в функции и вернуть указатель на нее?

Ответы [ 10 ]

7 голосов
/ 07 января 2011

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

Чтобы исправить это, либо передайте указатель на указатель (char ** t), либо передайте указатель по ссылке (char & *) и измените код функции на соответствующий.

4 голосов
/ 07 января 2011

вы определили t1 как char* t1 = NULL; и вызвали функцию как Test1(t1);, передав переменную-указатель t1 (не ее адрес).

Функция Test1 ожидает символ * void Test1(char *t)

здесь t - переменная, локальная только для функции Test1.Любые изменения, которые вы выполняете внутри функции, не будут видны вне функции, потому что вы фактически не изменяете переменную main функции t1, а локальную переменную t.

3 голосов
/ 07 января 2011

В Test1 вы передаете переменную t вашей функции, которая является указателем. Параметры, переданные функциям, живут в стеке, и когда функция завершается, стек теряется. Причина, по которой t в вашем main () равен NULL, заключается в том, что вы сохранили результат malloc в стеке, стек больше не существует.

3 голосов
/ 07 января 2011

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

3 голосов
/ 07 января 2011

Рассмотрим функцию:

void Test1(char *t)
{
    t = (char *)malloc(11);
    strcpy(t, "1234567890");
}

Теперь t является локальной переменной внутри функции. Что это содержит? Значение указателя. Первоначально это значение указателя указывает на NULL, потому что вы называете его как Test1( NULL );.

Однако первая строка, t = (char *)malloc(11), переназначает локальную переменную t для нового фрагмента памяти malloc.

Когда функция возвращает переменную main(), t1 все еще указывает на NULL, потому что, как я уже говорил, функция Test1 копирует значение указателя. Ни в коем случае переменная t1 никогда не модифицируется как Test1.

Если, однако, вы закодировали функцию как:

void Test1(char **t) {
    *t = (char *)malloc(11);
    strcpy( *t, "1234567890" );
}

int main( void ) {
    ...
    Test1( &t1 );
    ...
}

.. все было бы иначе.

2 голосов
/ 07 января 2011

В Test1 строка

t = (char *)malloc(11);

присваивается переменной t, которая является локальной в функции Test1.Переменная t1 в main () не изменяется, поэтому указатель NULL передается в printf.Test4 работает, потому что вы меняете t4 в main ().

«Правильный» способ создания строки в функции - это либо Test4 (но вам не нужно указывать t в качестве параметра), либо Test2 (если вы предпочитаетеили нужен выходной параметр).В обоих случаях вызывающая сторона должна освободить строку после этого.Test3 также работает, но вызывающий должен убедиться, что буфер достаточно большой - чтобы предотвратить неопределенное поведение и утечки памяти, размер буфера должен быть передан в качестве параметра в Test3.Преимущество использования Test3 заключается в том, что буфер может быть выделен в стеке, что исключает риск утечки памяти.

2 голосов
/ 07 января 2011

Поскольку вы пишете это:

void Test1(char *t)

Измените это на:

 void Test1(char* &t)

Будет работать только в C ++.Демонстрация здесь: http://www.ideone.com/OYNQo

1 голос
/ 30 декабря 2018

Во-первых, в c вы передаете параметры только по значению, что означает

char *t1 = NULL;
Test1(t1);

вы передаете копию указателя t1 в функцию Test1, поэтому изменение этой копии не повлияет на исходную. Поэтому, когда вы попытаетесь напечатать строку t1 points, это будет значение NULL, которое вы инициализировали ранее.

Теперь вот этот:

char *t4 = NULL;
t4 = Test4(t4);

хотя вы также передаете копию из t4, функция Test4 фактически выделяет вам память и заставляет эту копию из t4 указать эту память, а затем вы получаете эта копия, вы получите ту память, выделенную для вас:)

На самом деле для Test4 вам не нужно передавать аргумент, просто создайте указатель в Test4 и верните его, вы получите копию указателя, которая указывает на нужную вам память. Как это:

   char * Test4()
   {
       char *t = (char *)malloc(11 * sizeof(char));
       strcpy(t, "1234567890");
       return t;
   }

Надеюсь, это поможет.

0 голосов
/ 07 января 2011

Предположим, что ваш Test1 выполняет следующее поведение:

char * x1 = NULL;
Test1 (x1);

Test1 выполняется в следующем:

void test1 (char * t) / / t -> x1 -> null
{
    t = (char *) malloc (11);
    // t now points a different place (the memory allocated), and the reference of x1 continues pointing to null
    strcpy (t, "1234567890"); // t value is "1234567890" but x1 continues pointing to null
    // Error, the memory that is pointed by t is never released
}
printf ("\nTest1:%s \n", t1); / / Print the value of x1 (null) and the reference of t is lost
0 голосов
/ 07 января 2011
void Test1( char*& t ) ; // This is a possible method signature.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...