Here afaik, %s will keep on printing until it gets the termination character i.e. \0.
How does %c knows about termination and end up printing only one character?
String formatting is not a feature of C or the language. Is is a facility provided by the C runtime library.
"%c" doesn't need a terminator because it only processes one character.
"%s" continues to process characters until it encounters an ASCII NUL character which is expressed in C as '\000' (or '\0'). A forward slash in a string or character constant is a printable character without any special meaning.
Additionally, almost every C runtime library provides considerably more control over the %s length:
%.123s processes up to 123 characters if a NUL is not encountered
%.*s needs two matching parameters: the first is an integer which specifies the maximum field width and the second is the char pointer. Example: printf ("x = %.*s\n", 15, ptr);
%c always prints a single character.
char x = 'a';
Here we have a single character a which needs to be printed.
char x[2] = "a";
In the above example we have 2 characters one is 'a' and another is '\0'.
So while printing we need to use %s so that it encounters end of string thorough '\0' and prints the string.
%c means character, range is 0x00 to 0xff (Unsigned) that is 1 byte, a memory location can hold 1 byte of data, %c will print data from one memory location only, so termination character is not required.
%s means stream of characters means it has to get data from stream of memory locations so it requires a termination character, Hence '\0' is required for %s.
%c and %s are part of the printf() functions in the standard library, not part of the language itself.
That being said, printf() uses the character(s) following %, along with the stdarg functions, to determine what type of variable it will read from its arguments and what it will do with it. A highly simplified implementation of printf() might look like the following.
Note in particular that %c and %s do not deal with the same type of arguments! %c deals with a char (that is, a single character), whereas %s deals with a char * (that is, a pointer to an array of characters, hopefully null-terminated).
int printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt); // Initialize stdargs
const char *p = fmt; // Pointer into the format string
while (*p != '\0') {
char ch = *p++; // Get the next character of the format string
if (ch != '%') { // Not a format character? Just print it
putchar(ch);
continue;
}
char fmt = *p++; // Get the character following the %
if (fmt == 'd') {
int n = va_arg(ap, int);
print_number(n);
if (fmt == 'c') {
char c = va_arg(ap, char);
putchar(c);
} else if (fmt == 's') {
char *str = va_arg(ap, char *);
print_string(str);
} else {
// Unrecognized format character
}
}
va_end(ap);
return n;
}
Related
I have read this post.
But when I tried:
printf("before null %c after null\n", 0); // (ASCII=0) != '\0' ??
instead of getting:
before null
I got:
before null after null
So my question is:
Is ASCII value 0 actually equal to '\0'?
Is ASCII value 0 actually equal to \0?
Yes
The differences in how the strings are stored in memory and handled by functions like printf() are important.
"before null %c after null\n"
"before null \0 after null\n"
Both are stored in memory with an implicit \0 terminator at the end. The fact that the second has an explicit \0 character in the middle changes things.
printf() will scan the string until "the end", printing components as it goes... in C "the end" typically means until the first \0 / nul character.
With the first variant, printf() copies characters to the output until it reaches the %c directive, at which point it looks at the arguments that were given to the function... it might find that you gave '\0', or it might find that you gave '+' - either way, it copies this to the output. It'll then continue copying characters to the output, seeking "the end" of the string.
With the second variant, printf() will start copying characters to the output, will find "the end" (denoted by the \0), and stop.
If you were to use snprintf(), then the results / outputs would contain the following: (again, with implicit \0 termination)
"before null \0 after null\n"
"before null "
If you were to subsequently print both of these, they would look the same, but the memory content would be different.
However, the output of printf() is the terminal (or a file)... what happens for \0 depends on your terminal emulator... it might simply not be shown, it might be displayed as a space, or it may have a funny box symbol...
The important thing to note, is that this occurs at run time - not compile time.
That's because printf is NOT actually replacing "Hello %s", "World" with "Hello World" then print. No. It does not concatenate.
Rather, it actually prints each character alone in order, and when it encounters an argument, it starting printing each character from it directly too without concatenating.
If you ever tried to print a single null character using putchar(), You'd notice that it prints a space instead, that's why printf prints a space too based on it. (Note that It'll print nothing on other systems like Linux).
Sample code of how printf actually work.
const char * x;
// while the current char != '\0'
while (*format)
{
// if the current char == '%'
if (*format == '%')
{
// increment the pointer so we can point to the next char and skip printing '%'
switch (*(++format)) // then switch that next char (specifier).
{
case 'c':
putchar(va_arg(args, char)); // if the argument is null, then it's putchar(0);
break;
case 's':
// regular operation of printing a string argument.
x = va_arg(args, const char*);
while (*x) putchar(*x++);
break;
}
// skips the format specifier so we don't print it (e.g 's', 'c'..)
*format++;
}
// else: isn't a format specfier.
else
// print the current char (pointer) of the original string
putchar(*format++); // increments it for the next operation.
}
va_end(args);
So returning to your question, It will print each character and when it comes to the argument 0 which is null, putchar() will either put a space or nothing based on your system.
You can say that printf arguments don't really have any relationship with the original string to terminate it, they don't know each other. Like when you printf("Hello %s, from SO!", "World");, "World" is actually terminated at the end with \0, but it will terminate just itself, not the other , from SO!.
And Yes, 0 is '\0'. they're the same character.
printf will not terminate the printf format-string at that character position for the %c format-specifier when given values 0 or '\0'. Instead, the terminal output for the nul will generally be a placeholder (e.g. a space or the like)
However you can insert a nul into the string and then output the string using the %s format-specifier and see that in fact decimal 0 is actually the ASCII value for the equivalent ASCII character '\0' and will terminate the string at the point of the nul-character (see: www.ASCIItable.com), e.g.
#include <stdio.h>
#define FMT "before null %c after null\n"
int main (void) {
char buf[sizeof FMT * 2];
puts (FMT);
sprintf (buf, FMT, 0);
printf ("using 0 : '%s'", buf);
putchar ('\n');
sprintf (buf, FMT, '\0');
printf ("using \\0: '%s'", buf);
putchar ('\n');
return 0;
}
Example Use/Output
$ ./bin/str_printf_null
before null %c after null
using 0 : 'before null '
using \0: 'before null '
This question already has answers here:
scanning a string to hex char array
(3 answers)
Closed 8 years ago.
Here is my Code :
char a[18], b[18];
char oper, clear;
char *test;
init_8051();
test="0x1234567890123456 + 0x1234567890123456\0";
printf("Please enter an equation: %s \n",test );
sscanf(test,"0x%s %c 0x%s",a,&oper,b);
printf(" a= %s \n" ,a);
printf(" oper= %s \n" ,oper);
printf(" b= %s \n" ,b);
I want to accept to hex numbers with an operation as a string and to be able to seperate those 2 numbers into 2 separate char arrays but it doesnt wanna work, here is the output of the following code :
Please enter an equation: 0x1234567890123456 + 0x1234567890123456
a= 1234567890123456
oper= Ò
b= 1234567890123456
As you can see the operation is not recognized and also i have to use spaces which i wish i didnt have to use i wish it to be in the format of 0x1234567890123456+0x1234567890123456
with no spaces between the plus and the number.
Thanks
From the sscanf manual
s Matches a sequence of non-white-space characters; the next pointer must be a pointer to character array that is long enough to hold the input sequence and the
terminating null byte ('\0'), which is added automatically. The input string stops at white space or at the maximum field width, whichever occurs first.
It means that %s consumes the + and the rest of the characters, leaving b and oper uninitalized, abd overflowing a since it only has space for 18 characters.
So when the input string is lacking the space after the first operand, sscanf will continue reading until it finds a whitespace character. Hence when the string does not contain the separating space between the operands and the operator, sscanf consumes all the input.
I'll append here a different approach to your problems solution
We copy the string, this is required by strtok you can't pass an inmutable string, there are plenty of methods to copy this string, you just have to pick the appropriate one for your case
input = strdup("0x1234567890123456 + 0x1234567890123456\0");
Now, we use strpbrk to find the operator
pointer = strpbrk(input, "+-*/" /* here go the operators */);
if (pointer != NULL)
oper = *pointer; /* this will contain the operator ascii value */
Create a string containing the operator as a delimiter
operstr[0] = oper;
operstr[1] = '\0'; /* strings must be null terminated */
Now, we use strtok to tokenize the string, and find the operands
pointer = strtok(input, operstr);
if (pointer != NULL)
fprintf(stderr, "first operand: %s\n", pointer); /* you can copy this string if you need to */
printf("Operator: %s \n", operstr);
Second call to strtok needs NULL first argument
pointer = strtok(NULL, operstr);
if (pointer != NULL)
fprintf(stderr, "second operand: %s\n", pointer); /* you can copy this string if you need to */
And finally free our copy of the input string.
free(input);
It is better to use strtok_r the reentrant version. But for now you could test my suggestions and may be, it is what you need.
Even though this will work for this particular situation it is not the preferred way of doing this kind of thing, you can try writing a parser and use Reverse Polish Notation, or you can try with a lexical analyzer and a parser generator like flex and bison.
My previous answer was downvoted, and didn't address all of OP's requirements, so I have rewritten this answer.
OP wants flexible input, either spaces or no spaces. I suggest not using sscanf() but the methods below. First the program finds a valid operator by using strcspn(), then breaks the string using strtok() on operators and whitespace. But using strtok() on a string literal is UB so I copy the "equation" to another string first.
I also corrected the printf() field spec for the operator, and made a and b different - it's always a bad idea using the same values for different variables in an example.
#include <stdio.h>
#include <string.h>
#define OPERATORS "+-*/"
#define DELIMS " \t\n" OPERATORS
int parse (char *test)
// return 1 if parsed successfully
{
char a[50], b[50];
char oper;
char *ptr;
int opind;
opind = strcspn (test, OPERATORS); // find operator
if (opind < 1) return 0; // fail
oper = test[opind]; // collect operator
ptr = strtok (test, DELIMS); // find a
if (ptr == NULL) return 0; // fail
strcpy (a, ptr); // collect 1st arg
ptr = strtok (NULL, DELIMS); // find b
if (ptr == NULL) return 0; // fail
strcpy (b, ptr); // collect 2nd arg
printf(" a %s \n" ,a);
printf(" oper %c \n" ,oper); // corrected format
printf(" b %s \n" ,b);
return 1;
}
int main (void)
{
char test[100];
strcpy (test, "0x123456789ABCDEF0+0xFEDCBA9876543210");
if (!parse (test))
printf("Failed\n");
printf("\n");
strcpy (test, "0x123456789ABCDEF0 + 0xFEDCBA9876543210");
if (!parse (test))
printf("Failed\n");
return 0;
}
Program output
a 0x123456789ABCDEF0
oper +
b 0xFEDCBA9876543210
a 0x123456789ABCDEF0
oper +
b 0xFEDCBA9876543210
I am studying pointers right now. So, arrays are simultaneously studied with that. It says that address of first of element of array arr is &arr[0] which can also be written as arr.
But as i know that string is an array of characters so if have a string:
char string[] = "ilovejapan";
and then print it using printf
printf("%s", string);
shouldn't it be just printing the first address? Really confused Now.
Question updated: Now in the example below *W points to word that means it pointers to the first address of the string word right? How does this access complete string word?
int getword(char *word, int lim)
{
int c, getch(void);
void ungetch(int);
char *W = word;
while (isspace(c = getch()))
;
if (c != EOF)
*W++ = c;
if (lisalpha(c)) {
*W = '\0';
return c;
}
for ( ; --lim > 0; W++)
if ( lisalnum(*W = getch())) {
ungetch ( *W) ;
break;
}
*W = '\0';
return word[O];
}
The conversion specifier %s says, "Give me the address of a character. I will print that character, and then look at the next higher address and print that character, and so forth, until the character at the address I'm looking at is zero". So string is indeed the address of a character, but printf knows what to do with it.
char string[0] = "ilovejapan"; isn't a valid declaration. Maybe you meant to leave the 0 out?
Anyway, the %s format specifier is intended to match up with a pointer to a string, which in your case is just fine. It prints characters from that address up until the terminating null character.
When you pass string in printf("%s", string);, you are telling printf you want to print a string and you are telling the function the address of the first character in the string. Using its address, printf can figure out what character is stored at that address, and increments the address of the first character to get the address of the second character, and prints that character, and so on. It stops printing when it finds a character (not that character's address, but the actual character itself) whose value is '\0' (the "zero" character, or the character represented by the number 0). If that makes any sense.
I want to get a string as input by using scanf and if the string is just a space or blank I have to print error message.
This is what I've tried to do:
char string1[20]
scanf("%s",string1)
if(string1=='')
print error message
But that didn't work, actually I didn't expect it to work because string1 is an array of chars.
Any hint how to do it?
You should note that the scanf function will never scan a string with only blanks in it. Instead check the return value of the function, if it's (in your case) less than one it failed to read a string.
You may want to use fgets to read a line, remove the trailing newline, and then check if each character in the string is a space (with the isspace function).
Like this:
char string1[20];
if (fgets(string1, sizeof(string1), stdin) != NULL)
{
/* Remove the trailing newline left by the `fgets` function */
/* This is done by changing the last character (which is the newline)
* to the string terminator character
*/
string1[strlen(string1) - 1] = '\0';
/* Now "remove" leading whitespace */
for (char *ptr = string1; *ptr != '\0' && isspace(*ptr); ++ptr)
;
/* After the above loop, `*ptr` will either be the string terminator,
* in which case the string was all blanks, or else `ptr` will be
* pointing to the actual text
*/
if (*ptr == '\0')
{
/* Error, string was empty */
}
else
{
/* Success, `ptr` points to the input */
/* Note: The string may contain trailing whitespace */
}
}
scanf() does not always skip leading blanks.
Select formats specifies like "%s", "%d", "%f" do skip leading blanks. (whitespace).
Other formats specifies like "%c", "%[]", "%n" do not skip skip leading whitespace.
Scan in line and look for spaces. (string1 may contain whitespace)
char string1[20];
// Scan in up to 19 non-LineFeed chars, then the next char (assumed \n)
int result = scanf("%19[^\n]%*c", string1);
if (result < 0) handle_IOError_or_EOF();
else if (result == 0) handle_nothing_entered();
else {
const char *p = string1;
while (isspace(*p)) p++;
if (*p == '\0')
print error message
}
First, scanf will skip any blank spaces if you put a space (or other white space characters like '\n' or '\t') before the format specifier, like scanf(" %s", &str)
Second, if(string1=='') will compare the char pointer string1 with the blank char '' which will never be true because an existing variable's address will be non-NULL. That said, there's no "blank" char like that '' in C. You need to get the line input and parse whether it is a blank line or contains only spaces
I have a string I composed using memcpy() that (when expanded) looks like this:
char* str = "AAAA\x00\x00\x00...\x11\x11\x11\x11\x00\x00...";
I would like to print every character in the string, and if the character is null, print out "(null)" as a substitute for '\0'.
If I use a function like puts() or printf() it will just end at the first null and print out
AAAA
So how can I get it to print out the actual word "(null)" without it interpreting it as the end of the string?
You have to do that mapping yourself. If you want to, that is. In C, strings are null-terminated. So, if you use a formatted output function such as printf or puts and ask it to print a string (via the format specifier %s) it'd stop printing str as soon as it hits the first null. There is no null word in C. If you know exactly how many characters you have in str you might as well loop over them and print the characters out individually, substituting the 0 by your chosen mnemonic.
The draft says 7.21.6.1/8:
p The argument shall be a pointer to void. The value of the pointer is
converted to a sequence of printing characters, in an
implementation-defined manner.
However, the following:
$ cat null.c
#include <stdio.h>
int main() {
printf("%p\n", (void *)0);
}
produces:
00000000
on both gcc 4.6 and clang 3.2.
However, on digging deeper:
$ cat null.c
#include <stdio.h>
int main() {
printf("%s\n", (void *)0);
}
does indeed produce the desired output:
(null)
on both gcc and clang.
Note that the standard does not mandate this:
s If no l length modifier is present, the argument shall be a pointer
to the initial element of an array of character type.280) Characters
from the array are written up to (but not including) the terminating
null character. If the precision is specified, no more than that many
bytes are written. If the precision is not specified or is greater
than the size of the array, the array shall contain a null character.
Relying on this behavior may lead to surprises!
Instead of printing the string with %s , you will have to come up with a for loop that checks a condition whther a given char in your char array is a \0 and then print the NULL
From C++ Reference on puts() (emphasis mine):
Writes the C string pointed by str to stdout and appends a newline
character ('\n'). The function begins copying from the address
specified (str) until it reaches the terminating null character
('\0'). This final null-character is not copied to stdout.
To process data such as you have, you'll need to know the length. From there, you can simply loop across the characters:
/* ugly example */
char* str = "AAAA\x00\x00\x00...\x11\x11\x11\x11\x00\x00...";
int len = ...; /* get the len somehow or know ahead of time */
for(int i = 0; i < len; ++i) {
if('\0' == str[i]) {
printf(" (null) ");
} else {
printf(" %c ", str[i]);
}
}
One of the key cornerstones of C is strings are terminated by '\0'. Everyone lives by that rule. so I suggest you not think of your string as a string but as an array of characters.
If you traverse the array and test for '\0', you can print "(null)" out in place of the character. Here is an example. Please note, your char * str was created either as a char array or on the stack using malloc. This code needs to know the actual buffer size.
char* str = "AAAA\x00\x00\x00...\x11\x11\x11\x11\x00\x00...";
int iStrSz = <str's actual buffer size>
int idx;
for(idx=0; idx<iStrSz; idx++)
{
if('\0' == *(str + idx)
{
sprintf("%s", "(null)");
}
else
{
putchar(*(str + idx));
}
}
printf("%s", "\n");