LINUX.ORG.RU

Разное поведение vsnprintf в linux и msys

 


0

1

Приветствую. Потребовалась функция на Си, которая создает форматную строку нужной длины. Первоначальный вариант без проблем компилировался и работал на винде через связку msys+mingw-w64

typedef struct cl_String_s{
  char *str;
  size_t length;
} cl_String;


cl_String *StringCreateF(const char *format, ...){
  va_list argptr;
  cl_String *result = malloc(sizeof(cl_String));
  va_start(argptr, format);
  int res = vsnprintf(NULL, 0, format, argptr);
  if(res<0){
    perror("StringCreateF ERROR");
    errno = 0;
    free(result);
    return NULL; 
  }
  result->length = res;
  result->str = malloc(result->length + 1);
  vsnprintf(result->str, result->length + 1, format, argptr);
  va_end(argptr);
  return result;
}

А вот после компиляции в Linux он сегфолтится на втором vsnprintf. Работает нормально только если заново инициировать argptr.

  ...

  va_end(argptr);
  va_start(argptr, format);
  vsnprintf(result->str, result->length + 1, format, argptr);
  va_end(argptr);
  return result;
}

В чем может быть причина?

man 3 printf

The functions vprintf(), vfprintf(), vdprintf(), vsprintf(), vsnprintf() are equivalent to the functions printf(), fprintf(), dprintf(), sprintf(), snprintf(), respectively, except that they are called with a va_list instead of a variable number of arguments. These functions do not call the va_end macro. Because they invoke the va_arg macro, the value of ap is undefined after the call. See stdarg(3).

mironov_ivan ★★★★★ ()
Последнее исправление: mironov_ivan (всего исправлений: 1)

Почитай man по stdarg, там написано:

If ap is passed to a function that uses va_arg(ap,type), then the value of ap is undefined after the return of that function.

Поэтому советую скопировать свой va_list с помощью va_copy.

anonymous ()

В итоге сделал так.

#define _GNU_SOURCE
#include <stdio.h>

...

cl_String *StringCreateF(const char *format, ...){
  va_list argptr1;
  cl_String *result = malloc(sizeof(cl_String));
  va_start(argptr1, format);
#ifdef __GNUC__
  result->length = vasprintf(&result->str, format, argptr1);
  if(result->length < 0){
    free(result);
    va_end(argptr1);
    return NULL;
  }
#else
  va_list argptr2;
  va_copy(argptr2, argptr1);
  result->length = vsnprintf(NULL, 0, format, argptr1);
  if(result->length < 0){
    perror("StringCreateF ERROR");
    errno = 0;
    free(result);
    va_end(argptr1);
    va_end(argptr2);
    return NULL; 
  }
  result->str = malloc(result->length + 1);
  vsnprintf(result->str, result->length + 1, format, argptr2);
  va_end(argptr2);
#endif
  va_end(argptr1);
  return result;
}

Archer73 ()
Последнее исправление: Archer73 (всего исправлений: 1)
Ответ на: комментарий от Archer73

кстати, а зачем вы держите поле length в структуре?

в поиске/сравнении asciiz/utf8 оно только мешает, а в остальных случаях размер выделенной памяти можно спросить у аллокатора.

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

размер выделенной памяти можно спросить у аллокатора.

Чем ты обдалбываешься, это нестандартные методы если они вообще есть. И нужно же знать не размер чанка, а сколько было запрошено.

в поиске/сравнении asciiz/utf8 оно только мешает

Чем ты ... ах, да.

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

Давным давно приходилось программировать на delphi 7. Так там у динамически подключаемой библиотеки и у вызывающего приложения были разные аллокаторы. Даже костыль для обхода этого был ShareMem. Причем ошибки очень вредные были, приложение падало в совершенно левых местах. С тех пор стараюсь не общаться с библиотечным аллокатором из приложения.

Кстати у меня косяк в коде, я забыл что result->length у меня беззнаковый.

Archer73 ()