No format string passed to variable argument list function

A

Adam

Hi,

I have a simple printf-like function:

int print(const char *format, ...)
{
char buffer[1024];
va_list argptr;
int i;

va_start(argptr, format);
i = vsnprintf(buffer, sizeof(buffer), format, argptr);
va_end(argptr);

buffer[sizeof(buffer)-1] = '\0';

printf("%s\n",buffer); /* this bit just for the sake of testing */

return i;
}

If I call the function using something like:
char message[50];
strcpy(message, "hi there");
print("%s",message);

everything works, but if I do:
print(message);

it doesn't (program crashes with an abort).

Is there something wrong with what I'm doing, or should I be looking
elsewhere to work out the cause of my crash?

Thanks a lot,
Adam
 
N

Nate Eldredge

Adam said:
Hi,

I have a simple printf-like function:

int print(const char *format, ...)
{
char buffer[1024];
va_list argptr;
int i;

va_start(argptr, format);
i = vsnprintf(buffer, sizeof(buffer), format, argptr);
va_end(argptr);

buffer[sizeof(buffer)-1] = '\0';

printf("%s\n",buffer); /* this bit just for the sake of testing */

return i;
}

If I call the function using something like:
char message[50];
strcpy(message, "hi there");
print("%s",message);

everything works, but if I do:
print(message);

it doesn't (program crashes with an abort).

I don't see a problem myself. I took your function and added a main function:

#include <stdarg.h>
#include <stdio.h>

/* your function */

int main(void) {
print("%s\n", "Hello world!");
print("Goodbye world!\n");
return 0;
}

and it compiles and runs without problems on three different systems.

There could be something that I'm missing, though, which doesn't show up
on those systems. Can you post a complete program that aborts, and tell
us the system and compiler you're using?

It certainly seems like v*printf should be able to handle a va_list from
a function with no extra arguments, though the standard doesn't
explicitly seem to say so. It's conceivable that there's a bug in your
standard library, but hard to say just yet.
 
K

Keith Thompson

Adam said:
I have a simple printf-like function:

int print(const char *format, ...)
{
char buffer[1024];
va_list argptr;
int i;

va_start(argptr, format);
i = vsnprintf(buffer, sizeof(buffer), format, argptr);
va_end(argptr);

buffer[sizeof(buffer)-1] = '\0';

This isn't your problem, but what's the purpose of the above?
vsnprintf() should already write a '\0'-terminated string to buffer;
you're just setting character 1023 (which is likely far beyond the end
of the string) to '\0'.
printf("%s\n",buffer); /* this bit just for the sake of testing */

return i;
}

If I call the function using something like:
char message[50];
strcpy(message, "hi there");
print("%s",message);

everything works, but if I do:
print(message);

it doesn't (program crashes with an abort).

Is there something wrong with what I'm doing, or should I be looking
elsewhere to work out the cause of my crash?

It would help if you could post a small, complete, compilable program
that demonstrates the problem. I don't see anything in what you
posted that explains the symptom you're seeing.
 
P

Peter Nilsson

Adam said:
I have a simple printf-like function:

int print(const char *format, ...)
{
  char buffer[1024];

Maybe make this static. There's no upper or lower limit
on automatic storage, but I prefer not to allocate more
than 256 bytes in a single function.
  va_list argptr;
  int i;

  va_start(argptr, format);
  i = vsnprintf(buffer, sizeof(buffer), format, argptr);

Note that [v]snprintf is new in C99. Although many C90
implementations have it as an extension, they may
differ in semantics and prototype.

Did you include the correct header or prototype?
Is print defined before or after use? If after,
did you prototype it correctly before use?
  va_end(argptr);

  buffer[sizeof(buffer)-1] = '\0';

This shouldn't be necessary.
  printf("%s\n",buffer); /* this bit just for the sake of testing */

puts(buffer) is better since the result string could have
% characters in it that will be subject to conversion.
  return i;
}

If I call the function using something like:
char message[50];
strcpy(message, "hi there");

char message[50] = "hi there";
print("%s",message);

everything works, but if I do:
print(message);

it doesn't (program crashes with an abort).

Post a compilable program that exhibits the problem.

Also try print("%s\n", message);
Is there something wrong with what I'm doing,

Nothing obvious.
or should I be looking elsewhere to work out the
cause of my crash?

If you're using a C90 extension, you should check
the manual on what vsnprintf does.
 
B

Ben Bacarisse

Peter Nilsson said:
Adam <[email protected]> wrote:

puts(buffer) is better since the result string could have
% characters in it that will be subject to conversion.

Did you misread the printf? It looks fine to me (though puts probably
has the edge).
 
K

Kenny McCormack

Adam said:
I have a simple printf-like function:

int print(const char *format, ...)
{
  char buffer[1024];

Maybe make this static. There's no upper or lower limit
on automatic storage, but I prefer not to allocate more
than 256 bytes in a single function.

(CLC pedant mode)

I would imagine that the lower limit is 0 in most cases.

Are you aware of any implementations that require at least one
automatic object?
 
H

Harald van Dijk

(CLC pedant mode)

I would imagine that the lower limit is 0 in most cases.

Are you aware of any implementations that require at least one automatic
object?

(Continued pedant mode)

I think whatever location is used to store the return address qualifies as
an object, and while some may have that location fixed (making it a non-
automatic object), plenty of others don't. I'm not aware of
implementations that don't have a lower limit at all, though.
 
S

s0suk3

Hi,

I have a simple printf-like function:

int print(const char *format, ...)
{
  char buffer[1024];
  va_list argptr;
  int i;

  va_start(argptr, format);
  i = vsnprintf(buffer, sizeof(buffer), format, argptr);
  va_end(argptr);

  buffer[sizeof(buffer)-1] = '\0';

  printf("%s\n",buffer); /* this bit just for the sake of testing */

  return i;

}

You're using [v]snprintf() as a "safer [v]sprintf()", which is already
OK, but you should also take advantage of the fact that [v]snprintf()
returns the required length when you pass it zero as the size (the 2nd
argument). That will ensure that the result won't be truncated (in
this case, to 1024 characters).

int print(const char *format, ...)
{
va_list ap;

va_start(ap, format);
int len = vsnprintf(NULL, 0, format, ap); // figure out the length
va_end(ap);

char buffer[len + 1];
va_start(ap, format);
vsnprintf(buffer, len + 1, format, ap);
va_end(ap);

printf("%s\n", buffer);

return len;
}

Sebastian
 
A

Adam

Adam said:
I have a simple printf-like function:
int print(const char *format, ...)
{
  char buffer[1024];
  va_list argptr;
  int i;
  va_start(argptr, format);
  i = vsnprintf(buffer, sizeof(buffer), format, argptr);
  va_end(argptr);
  buffer[sizeof(buffer)-1] = '\0';
  printf("%s\n",buffer); /* this bit just for the sake of testing */
  return i;
}
If I call the function using something like:
char message[50];
strcpy(message, "hi there");
print("%s",message);
everything works, but if I do:
print(message);
it doesn't (program crashes with an abort).

I don't see a problem myself.  I took your function and added a main function:

#include <stdarg.h>
#include <stdio.h>

/* your function */

int main(void) {
  print("%s\n", "Hello world!");
  print("Goodbye world!\n");
  return 0;

}

and it compiles and runs without problems on three different systems.

Actually, the bit I was wondering about was passing a (char *) to the
print function, rather than a literal string. I did find this page:
http://www.eskimo.com/~scs/cclass/int/sx11c.html
....which seems to suggest (in the first paragraph) this might be
wrong.


There could be something that I'm missing, though, which doesn't show up
on those systems.  Can you post a complete program that aborts, and tell
us the system and compiler you're using?

Well, the compiler is GCC. Trouble is, I can't replicate it in a
simple example (and a complex example would take me well out of
comp.lang.c territory). I though perhaps that some undefined behaviour
was causing problems in one case but not in another, but
I guess I need to look elsewhere for my problem.

This isn't your problem, but what's the purpose of [buffer[sizeof(buffer)-1] = '\0']?
Hmm, well when I originally wrote a function like this I wasn't sure
about the rules for whether it would get terminated or not and it
seemed it didn't if the incoming string was truncated. However, a
similar test now seems to reveal it does work, as you say.


Thanks,
Adam
 
K

Keith Thompson

Adam said:
Keith said:
This isn't your problem, but what's the purpose of [buffer[sizeof(buffer)-1] = '\0']?
Hmm, well when I originally wrote a function like this I wasn't sure
about the rules for whether it would get terminated or not and it
seemed it didn't if the incoming string was truncated. However, a
similar test now seems to reveal it does work, as you say.

Testing doesn't prove anything. The way to confirm that the string
will be properly terminated string is to read the documentation for
the functions you're using.

If you were using a function that *isn't* guaranteed to produce a
properly terminated string, testing will very likely fail to detect
the problem; consider that uninitialized memory is often set to zero
(though that's by no means guaranteed).
 
J

jameskuyper

Adam said:
Adam said:
I have a simple printf-like function:
int print(const char *format, ...)
{
� char buffer[1024];
� va_list argptr;
� int i;
� va_start(argptr, format);
� i = vsnprintf(buffer, sizeof(buffer), format, argptr);
� va_end(argptr);
� buffer[sizeof(buffer)-1] = '\0';
� printf("%s\n",buffer); /* this bit just for the sake of testing */
� return i;
}
If I call the function using something like:
char message[50];
strcpy(message, "hi there");
print("%s",message);
everything works, but if I do:
print(message);
it doesn't (program crashes with an abort).

I don't see a problem myself. �I took your function and added a main function:

#include <stdarg.h>
#include <stdio.h>

/* your function */

int main(void) {
� print("%s\n", "Hello world!");
� print("Goodbye world!\n");
� return 0;

}

and it compiles and runs without problems on three different systems.

Actually, the bit I was wondering about was passing a (char *) to the
print function, rather than a literal string. I did find this page:
http://www.eskimo.com/~scs/cclass/int/sx11c.html
...which seems to suggest (in the first paragraph) this might be
wrong.

In C, the type of a string literal is char*; when used in this
contexts, (and most other contexts) "message" decays to a char* too.
Therefore, whatever issue might arise with a string literal will also
arise with "message".

The first paragraph that you refer to on that web page talks about the
integer promotions, which only apply to integer types like 'char'.
Pointer types like 'char *' remain unaffected by the integer
promotions.
Well, the compiler is GCC. Trouble is, I can't replicate it in a
simple example (and a complex example would take me well out of
comp.lang.c territory).

Start with your complicated example, and start simplifying it by
removing things. Test after each removal. When a removal causes the
problem to disappear, put it back in and remove something else. If you
do this systematically, you will either produce a minimum-size program
that demonstrates the problem which you can post to this group, no
matter how long it is; or you'll realize what the problem is. In my
experience, it's usually the second possibility that actually comes
up.
This isn't your problem, but what's the purpose of [buffer[sizeof(buffer)-1] = '\0']?
Hmm, well when I originally wrote a function like this I wasn't sure
about the rules for whether it would get terminated or not and it
seemed it didn't if the incoming string was truncated. However, a
similar test now seems to reveal it does work, as you say.

Performing a test is not a goodway to figure this out. The test will
tell you what your compiler does; it won't tell you whether this a
special case, or something you can rely on. Reading and understanding
the documentation of the vsnprintf() function is the best way. The
standard's description of vsnprintf() cross-references snprintf(). The
description of snprintf() says "a null character is written at the end
of the characters actually written into the array." The documentation
that comes with your compiler should say roughly the same thing.
 
V

vippstar

In C, the type of a string literal is char*; when used in this
contexts, (and most other contexts) "message" decays to a char* too.
Therefore, whatever issue might arise with a string literal will also
arise with "message".

The type of a string literal is char [n] where n is size of the string
literal.
What are you talking about? I hope I haven't took this out of context.
 
J

jameskuyper

In C, the type of a string literal is char*; when used in this
contexts, (and most other contexts) "message" decays to a char* too.
....
The type of a string literal is char [n] where n is size of the string
literal.
What are you talking about? I hope I haven't took this out of context.

What I should have done is replace the first sentence with:

In C, in this context (and in most other contexts) both string
literals and arrays of char like "message" are converted to char*
pointers.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top