LINUX.ORG.RU

C, Странная проблема с указателями в функции


0

0

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

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

void try_realloc( char* str )
{
  const char *addstr  = " more string ";
  size_t len    = strlen( str );
  size_t addlen = strlen( addstr );

  str = realloc( str, ( len + addlen + 1 ) * sizeof( char ) );
  strncat( str, addstr, addlen+1 );
}

int main()
{
  char *str = calloc( 1, sizeof(char) );
  
  try_realloc( str );
  printf( "%s\n", str );

  try_realloc( str );
  printf( "%s\n", str );

  free( str );
  return 0;
}

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

В этом коде valgrind орет о куче ошибок. (текст не привожу - вы
сами можете его получить).

Теперь рассмотрим аналогичный код. Разница минимальна, но
ошибок не происходит и валгринд тоже говорит, что все тип-топ.

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

char* try_realloc( char* str )
{
  const char *addstr  = " more string ";
  size_t len    = strlen( str );
  size_t addlen = strlen( addstr );

  str = realloc( str, ( len + addlen + 1 ) * sizeof( char ) );
  strncat( str, addstr, addlen+1 );

  return str;
}

int main()
{
  char *str = calloc( 1, sizeof(char) );
  
  str = try_realloc( str );
  printf( "%s\n", str );

  str = try_realloc( str );
  printf( "%s\n", str );

  free( str );
  return 0;
}

То есть разница лишь в том, что функция возвращает указатель
на перераспределенную память. И все работает!!! В ЧЕМ ПРИКОЛ?

Пробовал компилить с gcc и PGI компиляторами - один хрен.
★★★

realloc() возвращает указатель на новый участок памяти, выровненный так, что его можно использовать для переменных любого типа, _причем этот новый указатель может отличаться от ptr_. Если запрос выполнить не удается или новый размер равен нулю, возвращается NULL. Если вызов realloc() завершился неудачно, то старый блок памяти остается нетронутым: он не освобождается и не перемещается.

(c) man

vasily_pupkin ★★★★★
()

[alex:alex tmp]$  cat 39.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void try_realloc( char** str )
{
  const char *addstr  = " more string ";
  size_t len    = strlen( *str );
  size_t addlen = strlen( addstr );

  *str = realloc( *str, ( len + addlen + 1 ) * sizeof( char ) );
  strncat( *str, addstr, addlen+1 );
}

int main(int argc, char **argv)
{
  char *str = calloc( 1, sizeof(char) );

  try_realloc( &str );
  printf( "%s\n", str );

  try_realloc( &str );
  printf( "%s\n", str );

  free( str );
  return 0;
}

[alex:alex tmp]$ gcc -g -pedantic -o 39 39.c
[alex:alex tmp]$ valgrind --leak-check=full ./39
==11762== Memcheck, a memory error detector.
==11762== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==11762== Using LibVEX rev 1732, a library for dynamic binary translation.
==11762== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==11762== Using valgrind-3.2.3-Debian, a dynamic binary instrumentation framework.
==11762== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==11762== For more details, rerun with: -v
==11762==
 more string
 more string  more string
==11762==
==11762== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 10 from 1)
==11762== malloc/free: in use at exit: 0 bytes in 0 blocks.
==11762== malloc/free: 3 allocs, 3 frees, 42 bytes allocated.
==11762== For counts of detected errors, rerun with: -v
==11762== All heap blocks were freed -- no leaks are possible.

alex_custov ★★★★★
()
Ответ на: комментарий от vasily_pupkin

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

atoku ★★★
() автор топика
Ответ на: комментарий от alex_custov

А вот что у меня:

gcc -g -pedantic ./cmemo.c
$ valgrind ./a.out 
==1665== Memcheck, a memory error detector.
==1665== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==1665== Using LibVEX rev 1732, a library for dynamic binary translation.
==1665== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==1665== Using valgrind-3.2.3, a dynamic binary instrumentation framework.
==1665== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==1665== For more details, rerun with: -v
==1665== 
==1665== Invalid read of size 1
==1665==    at 0x4C20772: strlen (mc_replace_strmem.c:246)
==1665==    by 0x4E8451A: puts (in /lib64/libc-2.6.so)
==1665==    by 0x40067A: main (cmemo.c:20)
==1665==  Address 0x4057030 is 0 bytes inside a block of size 1 free'd
==1665==    at 0x4C1FA97: realloc (vg_replace_malloc.c:306)
==1665==    by 0x400632: try_realloc (cmemo.c:11)
==1665==    by 0x400671: main (cmemo.c:19)

==1665== 
==1665== Invalid read of size 1
==1665==    at 0x4005E5: try_realloc (cmemo.c:8)
==1665==    by 0x400683: main (cmemo.c:22)
==1665==  Address 0x4057030 is 0 bytes inside a block of size 1 free'd
==1665==    at 0x4C1FA97: realloc (vg_replace_malloc.c:306)
==1665==    by 0x400632: try_realloc (cmemo.c:11)
==1665==    by 0x400671: main (cmemo.c:19)
==1665== 
==1665== Invalid free() / delete / delete[]
==1665==    at 0x4C1FA97: realloc (vg_replace_malloc.c:306)
==1665==    by 0x400632: try_realloc (cmemo.c:11)
==1665==    by 0x400683: main (cmemo.c:22)
==1665==  Address 0x4057030 is 0 bytes inside a block of size 1 free'd
==1665==    at 0x4C1FA97: realloc (vg_replace_malloc.c:306)
==1665==    by 0x400632: try_realloc (cmemo.c:11)
==1665==    by 0x400671: main (cmemo.c:19)
==1665== 
==1665== Invalid read of size 1
==1665==    at 0x4C210E1: strncat (mc_replace_strmem.c:218)
==1665==    by 0x40064B: try_realloc (cmemo.c:12)
==1665==    by 0x400683: main (cmemo.c:22)
==1665==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==1665== 
==1665== Process terminating with default action of signal 11 (SIGSEGV)
==1665==  Access not within mapped region at address 0x0
==1665==    at 0x4C210E1: strncat (mc_replace_strmem.c:218)
==1665==    by 0x40064B: try_realloc (cmemo.c:12)
==1665==    by 0x400683: main (cmemo.c:22)
==1665== 
==1665== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 5 from 1)
==1665== malloc/free: in use at exit: 14 bytes in 1 blocks.
==1665== malloc/free: 3 allocs, 2 frees, 29 bytes allocated.
==1665== For counts of detected errors, rerun with: -v
==1665== searching for pointers to 1 not-freed blocks.
==1665== checked 67,656 bytes.
==1665== 
==1665== LEAK SUMMARY:
==1665==    definitely lost: 14 bytes in 1 blocks.
==1665==      possibly lost: 0 bytes in 0 blocks.
==1665==    still reachable: 0 bytes in 0 blocks.
==1665==         suppressed: 0 bytes in 0 blocks.
==1665== Rerun with --leak-check=full to see details of leaked memory.
segmentation fault

А во втором случае - все хорошо!

atoku ★★★
() автор топика
Ответ на: комментарий от atoku

Вы после и до реаллока сделайте printf адреса. У вашей ф-ии try_realloc нет телепатического интерфейса. Основной код не узнает о смене адреса.

vasily_pupkin ★★★★★
()

Думаю, твоя задумка в первом варианте реализуется таким патчем в пятой строчке:

-void try_realloc( char* str )
+void try_realloc( char* &str )

alexsaa
()
Ответ на: комментарий от vasily_pupkin

vasily_pupkin> У вашей ф-ии try_realloc нет телепатического интерфейса. Основной код не узнает о смене адреса.

Вот оно в чем дело! Всем спасибо! Теперь дошло.

А я думал, что есть телепатический интерфейс. Просто не знал, что после аллокации функция держит адрес указателя локально, чтобы потом освободить его по free. Мне казалось, что все это отслеживается глобально.

Вообще такой прикол, убил у меня полдня на отладку. Век живи...

atoku ★★★
() автор топика
Ответ на: комментарий от alex_custov

> ты сначала посмотри, что я компилировал ;)

Извини. Если бы пригляделся все бы понял сразу.

atoku ★★★
() автор топика
Ответ на: комментарий от alex_custov

> ты сначала посмотри, что я компилировал ;)

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

Еще раз всем спасибо. Пойду пить водку с горя, что оказался болваном. :)

atoku ★★★
() автор топика
Ответ на: комментарий от atoku

Это нормально... у самого такие "залипы" случаются. В таких случаях я всегда прошу кого-нибудь посмотреть код, поскольку свежий взгляд более цепкий, а твой замыливается на второй тысяче пересмотра этого кода :)

Deleted
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.