Присвоение строк члену структуры (указателю) в C


Мне нужно заполнить мою структуру my_s1 данными. Я передаю его функции get_data(), которая должна делать всю грязную работу. У меня проблема с элементом b моей структуры, который является указателем. Я понятия не имею, как правильно присвоить значение, указанное (char *) buff, b без ошибок сегментации или ошибок valgrind.
Например:

  1. Почему начальный p1->b="abc"; работает нормально, но если я попытаюсь использовать strcpy() или назначить через "=" оператор массива в p1->b я получаю ошибки?

  2. Выделяет ли s1 my_s1 память для b или я должен как-то сам использовать malloc() p1->b? Но опять же мне нужно освободить() его и присвоить указатель NULL перед возвратом из функции, что противоречит цели (функция присваивает данные структуре), верно?

  3. С текущим кодом, указанным ниже, у меня есть «правильный результат выполнения», но я также получаю следующие ошибки вывода valgrind (насколько я понимаю, поправьте меня, если я ошибаюсь, кажется, что printf() обращается к неправильно выделенной памяти - так что в этом случае это работает, но это мусор):

валгринд:

==1067== Memcheck, a memory error detector
==1067== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==1067== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==1067== Command: ./if
==1067== Parent PID: 1059
==1067==
==1067== Invalid read of size 1
==1067==    at 0x4E7ADF9: vfprintf (in /usr/lib64/libc-2.17.so)
==1067==    by 0x4E83E38: printf (in /usr/lib64/libc-2.17.so)
==1067==    by 0x4005EF: main (iface.c:10)
==1067==  Address 0x51f3040 is 0 bytes inside a block of size 5 free'd
==1067==    at 0x4C294C4: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-
linux.so)
==1067==    by 0x40064D: get_data (ifacelib.c:17)
==1067==    by 0x4005D3: main (iface.c:8)
==1067==
==1067== Invalid read of size 1
==1067==    at 0x4EA9459: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.17.
so)
==1067==    by 0x4E7ADB1: vfprintf (in /usr/lib64/libc-2.17.so)
==1067==    by 0x4E83E38: printf (in /usr/lib64/libc-2.17.so)
==1067==    by 0x4005EF: main (iface.c:10)
==1067==  Address 0x51f3043 is 3 bytes inside a block of size 5 free'd
==1067==    at 0x4C294C4: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-
linux.so)
==1067==    by 0x40064D: get_data (ifacelib.c:17)
==1067==    by 0x4005D3: main (iface.c:8)
==1067==
==1067== Invalid read of size 1
==1067==    at 0x4EA946C: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.17.
so)
==1067==    by 0x4E7ADB1: vfprintf (in /usr/lib64/libc-2.17.so)
==1067==    by 0x4E83E38: printf (in /usr/lib64/libc-2.17.so)
==1067==    by 0x4005EF: main (iface.c:10)
==1067==  Address 0x51f3042 is 2 bytes inside a block of size 5 free'd
==1067==    at 0x4C294C4: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-
linux.so)
==1067==    by 0x40064D: get_data (ifacelib.c:17)
==1067==    by 0x4005D3: main (iface.c:8)
==1067==
==1067== Invalid read of size 4
==1067==    at 0x4EBBDDE: __GI_mempcpy (in /usr/lib64/libc-2.17.so)
==1067==    by 0x4EA939C: _IO_file_xsputn@@GLIBC_2.2.5 (in /usr/lib64/libc-2.17.
so)
==1067==    by 0x4E7ADB1: vfprintf (in /usr/lib64/libc-2.17.so)
==1067==    by 0x4E83E38: printf (in /usr/lib64/libc-2.17.so)
==1067==    by 0x4005EF: main (iface.c:10)
==1067==  Address 0x51f3040 is 0 bytes inside a block of size 5 free'd
==1067==    at 0x4C294C4: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-
linux.so)
==1067==    by 0x40064D: get_data (ifacelib.c:17)
==1067==    by 0x4005D3: main (iface.c:8)
==1067==
==1067==
==1067== HEAP SUMMARY:
==1067==     in use at exit: 0 bytes in 0 blocks
==1067==   total heap usage: 1 allocs, 1 frees, 5 bytes allocated
==1067==
==1067== All heap blocks were freed -- no leaks are possible
==1067==
==1067== For counts of detected and suppressed errors, rerun with: -v
==1067== ERROR SUMMARY: 10 errors from 4 contexts (suppressed: 2 from 2)

Код в 3 файлах.

ifacelib.h:

#ifndef IFACELIB_H
#define IFACELIB_H

typedef struct
{
    int a;
    char * b;
} s1;

int get_data(s1 *);

#endif

ifacelib.c:

#include "ifacelib.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int get_data(s1 *p1)
{
    char *buff;
    p1->a=1;
    p1->b="abc";

    buff = (char *) malloc(strlen("test")*sizeof(char)+1);
    strcpy(buff, "test");

    p1->b = buff;

    free(buff);
    buff = NULL;

    return 0;
}

iface.c:

#include "ifacelib.h"
#include <stdio.h>

int main()
{
    s1 my_s1;

    if ((get_data(&my_s1))==0)
    {
        printf("a= %d\tb= %s\n", my_s1.a, my_s1.b);
    }

    return 0;
}


Любая помощь или просто указание в правильном направлении будут оценены.

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

Это мой первый пост здесь, так что, пожалуйста, потерпите меня, мои ошибки форматирования, стены текста и мое невежество.
Заранее спасибо,
Том


person wymiata3    schedule 09.12.2013    source источник
comment
Я подозреваю, что у вас есть базовое непонимание разницы между указателем и тем, на что указывает указатель. Когда вы назначаете строку в C, вы назначаете только указатель. Фактические данные персонажа не перемещаются.   -  person Hot Licks    schedule 09.12.2013
comment
Почему вы сразу освобождаетесь после malloc? Вы также не можете получить доступ к p1-›b после освобождения буфера! p1->b — это просто указатель на буфер, вы можете освободить буфер только тогда, когда уверены, что он больше не будет использоваться.   -  person moeCake    schedule 09.12.2013
comment
Большое спасибо @moeCake, я больше не освобождаю бафф из функции get_data(), вместо этого в main() в конце я освобождаю (my_s1.b) и не получаю утечек памяти или ошибок valgrind.   -  person wymiata3    schedule 09.12.2013


Ответы (2)


Вы делаете неправильно, и вам просто повезло получить правильный результат, на самом деле вы обращаетесь к памяти, которая только что освободилась.
Это правильно, что вам нужно использовать malloc для char* в структуре (кстати, вы можете использовать strdup) , но вам нужен еще один деструктор, чтобы освободить структуру, когда их работа будет выполнена.
В вашем случае вам нужна такая функция, как free_s1 после printf, а не free в функции конструктора (get_data).

person moeCake    schedule 09.12.2013

Это работает в C, не так хорошо в C++:

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

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

typedef struct
{
    int a;
    char * b;
    char   data[1];    // data goes here. 
                       // structure MUST be malloced at run time WITH
                       //  extra storage for data.
} s1;
    s1   *p1;
    data = "test";
    data_len = strlen(data);       // additional bytes of storage
    p1 = malloc( data_len + sizeof( *p1 ) );  // allocate structure + data
    strcpy(s1->data, data );        // copy data to buffer

    ...
    free(p1);        // free storage
person Gilbert    schedule 09.12.2013