aarelovich
aarelovich

Reputation: 5576

C: Output difference between printf and vfprintf when using the exact same inputs

I'm trying to write a logger function that logs stuff to a file in a printf manner but, if enabled, it will also log to the console. I'm trying it out with a custom function that uses a custom string struct representing a number and transforms it into an actual number.

The main function:

#define MSG "0xab45cdef"
int main(){

   String s;
   stringInit(&s);
   stringSetString(&s,MSG,sizeof(MSG));
   stringPrint(&s);
   logOut("\nTransforming to value\n");
   int64_t v = parseValue(s);
   logOut("\n");
   logOut("\nResult %li \n", v);
}

My output log function

void logOut(const char *control_string, ...){
   FILE *fp;
   fp = fopen(LOG_OUTPUT,"ab+");

   va_list argptr;
   va_start(argptr,control_string);
   vfprintf(fp,control_string,argptr);
#ifdef LOG_CONSOLE
   printf(control_string,argptr);
#endif
   va_end(argptr);
   fclose(fp);
}

My String related functions

typedef struct {
   char *s;
   unsigned int  size; 
} String;

void stringInit(String *s){
   s->s = NULL;
   s->size = 0;
}

void stringAddChar(String *s, char c){
   if (s->size > 0){
      // Adding one more char. 
      s->s = (char *) realloc (s->s, (s->size + 1)*sizeof(char));       
   }
   else{
     // First char.
     s->s = (char *) malloc(sizeof(char));
   }
   s->size++;      
   s->s[s->size-1] = c;
}


void stringFree(String *s){
   if (s->size == 0) return;
   free(s->s);
   s->s = NULL;
   s->size = 0;
}

void stringSetString(String *s, char *str, uint32_t nsize){
   // Clearing the previous string. 
   stringFree(s);
   for (uint32_t i = 0;  i < nsize; i++){
      // This avoids the extra char in a null terminated string. 
      if ((i == nsize-1) && (str[i] == 0)) break;
      stringAddChar(s,str[i]);
   }
}

void stringPrint(String *s){
   for (uint32_t i = 0;  i < s->size; i++){
      logOut("%c",s->s[i]);
   }
}

And finally the parseValue function

int64_t power(int64_t base, int64_t exp){
   int64_t ans = 1;
   for (int i = 0; i < exp; i++){
      ans = ans * base;
   }
   return ans;
}

int64_t parseValue(String input){

   int64_t  base = 10;
   int64_t  res = 0;
   int64_t  maxpow = input.size-1;
   uint32_t start = 0;

   if (input.size > 0){
      // Must check if it is hex or not.       
      if (input.s[0] == '0' && input.s[1] == 'x'){
         base = 16;
         start = 2;
         maxpow = input.size-3;
      }
   }


   for (int i = start; i < input.size; i++){

      int64_t p = maxpow;
      maxpow--;


      char c = toupper(input.s[i]);  

      // printf("Char %d is %d\n",i,c);

      int64_t v = c - 48;

      if ((v >= 0) && (v <= 9)){
         res = res + v*power(base,p);
      }
      else if ((c >= 65) && (c <= 70)){
         if (base == 16){
            v = c - 55;
            res = res + v*power(base,p);
         }
         else{
            logOut("Invalid char %c in a decimal number\n",c);
            return -1;
         }
      }
      else{
         logOut("Invalid digit %d\n",c);
         return -1;       
      }
   }

   return res;
}

Now when I run the main the console outputs:

pppppppppp
Transforming to value


Result 140726926743712 

While my log.txt file has this

0xab45cdef
Transforming to value


Result 2873478639 

The content of the log.txt file is correct. So why is the console output different?

Upvotes: 1

Views: 271

Answers (1)

KamilCuk
KamilCuk

Reputation: 141768

To summarize, your logOut function has some mistakes. You can't "re-use" va_list after it has been used in another function. The function could look like this:

void logOut(const char *control_string, ...){
   FILE *fp;
   fp = fopen(LOG_OUTPUT,"ab+");
   if (fp == NULL) {
       abort();
   }

   va_list argptr;
   va_start(argptr, control_string);
   if (vfprintf(fp, control_string, argptr) < 0) {
      // handle error
   }
   va_end(argptr);

   fclose(fp);

#ifdef LOG_CONSOLE
   va_start(argptr, control_string); // do not re-use va_list
   vprintf(control_string, argptr);    
// ^ you pass va_list
   va_end(argptr);
#endif
}

Notes:

  • realloc(NULL, ...) is equal to malloc(...). So there is no need to if (s->size > 0){ inside stringAddChar - just call realloc(s->s, and make sure that s->s is NULL when size is zero.
  • Your code misses a lot of error handling. A proper way to handle realloc is to use a temporary pointer so that original memory will not leak: void *p = realloc(s->s, ...); if (p == NULL) { free(s->s); /* handle errors */ abort(); } s->s = p;.
  • Try not to use magic numbers in code. c - 48; is better written as c - '0';.

Upvotes: 2

Related Questions