behaviour of pointer ; char ** argv - c

when I was testing double pointer behaviour I got a result that I don't understand well.
==> code 1 :
int main (int argc , char **argv)
{
if(*argv+1 ==NULL)
{
printf("NULL pointer \n");
exit(0) ;
}
else
{
printf("test double pointer[] : %s \n ",*argv+1);
}
return(0);
}
====> result 1
root#root:/home/aa/test# ./geip 1255
test double pointer[] : /geip
root#root:/home/aa/test#
===> code 2 :
int main (int argc , char **argv)
{
if(*argv+9 ==NULL)
{
printf("NULL pointer \n");
exit(0) ;
}
else
{
printf("test double pointer[] : %s \n ",*argv+9);
}
return(0);
}
==> result 2 :
root#root:/home/aa/test# ./geip 1255
test double pointer[] : 55
root#root:/home/aa/test#
==> result 3 :
root#root:/home/aa/test# ./geip
test double pointer[] : ELL=/bin/bash
root#root:/home/aa/test#
it seems that printf display from n th word (1 and 9)
how we can explain this behaviour of pointer ?

You're using it wrong.
*argv+1 will be interpreted as (argv[0])+1 and since argv[0] is "./geip" you get "/geip".
*argv+9 will be interpreted as (argv[0])+9 but since argv[0] only has length 6 the outcome is undefined.
In your case, argv is probably stored as :
. / g e i p \0 1 2 5 5 \0
0 1 2 3 4 5 6 7 8 9 10 11
Which explains why +9 gets you "55"
But you should really forget that because it's never going to be useful! This is undefined behavior and should not ever be used.

char **argv is a pointer to a char * (sometimes referred to more simply as a string). You dereference this pointer when you do *argv. The result of this dereference is a char * or in other words it is the address of a char. When you do addition with the result, your code is computing a new address. So, for example while *argv would be the address of the first character in your string, *argv+1 is the address of the second character in your string.
When you add in a number that is longer than the length of your string you are stepping out of "safety". Remember that C will let you do pointer arithmetic that takes you past the end of your string. In your second example you are asking printf to go 9 bytes past the start of *argv and print characters from there to the next \0 (or NULL) byte. You are effectively reading arbitrary memory from your program's process space, which explains what is being printed.

There are actually more than one problem.
please, please, do not work as a root. Just don't.
your syntax (*argv + 9) means literally: "defeference argv and move pointer 9 chars", and really, if you move 9 characters from ./geip 1255 you'll arrive to 55. So either use argv[i] (i = 1..N denotes argument index) or if you want to do it the hard way, you must add parentheses: *(argv + i).
try to format your code better - it will be more readable not only for stackoverflow guys, but also for you.
For example when you run ./geip a b c 123:
argv[0] is string holding program name - ./geip
argv[1] is string holding the first argument - a
argv[2] is string holding the second argument - b
argv[3] is string holding the third argument - c
argv[4] is string holding the fourth argument - 123
argv[5] is NULL as argc will bw 5 (see comments)
argv[>5] is not a good idea, because there are no more arguments. So you better check argc to see how many arguments there are.

You just do a pointer arithmetics:
**argv is a pointer to list of pointers
*argv is a head of the list
//char **argv is given from outthere
char *p;
p = *argv; // the same as "p = *argv[0]"
for (int i = 0; i < 100) {
printf("Next: %s\n", p+i);
}
Try to run it and see the dump of memory, from head of the list to next 100 bytes.

Related

comparison between the string and pointer [duplicate]

This question already has answers here:
How do I properly compare strings in C?
(10 answers)
Closed 1 year ago.
I'm trying the following codes, but got unexpected result.
//the first element in argv[] is a
int main(int argc, char *argv[]) {
char a;
if (*argv == "a")
{
printf("a");
}
}
I got nothing after excution, so that means the condition *argv++ == "a" is false. So why?
In this if statement
if (*argv == "a")
there are compared two pointers. The first one is the pointer to the first character of the string pointed to by the pointer *argv and the second one is the pointer to the first character of the string literal "a".
As these two strings occupy different extents of memory then the comparison of the pointers always evaluates to logical false.
If you want to compare pointed strings then you need to use the standard C string function strcmp. For example
if ( strcmp( *argv, "a" ) == 0 )
If you want to compare first characters of the strings then you should write
if ( **argv == *"a" )
or just use the character 'a' instead of the string literal that is more natural
if ( **argv == 'a' )
Pay attention to that if *argv is not equal to NULL then this pointer points to the string that denotes the running program.
Maybe you actually mean the following if statement
if ( argc > 1 && strcmp( argv[1], "a" ) == 0 )
if you want to check whether the first command line argument is equal tp the string literal "a".
char *argv[] is an array of pointers to char.
// myapp.c - program to print out all command line parameters
#include <stdio.h>
int main (int argc, char *argv[]) {
int i ;
for (i = 0; i < argc; i++) {
printf ("arg %d = %s\n", i + 1, argv[i]) ;
}
return 0 ;
}
If one were to compile this code and run it at the command line like this:
myapp This That another
It would print out
arg 1 = myapp
arg 2 = This
arg 3 = That
arg 4 = another
So, let's say *argv points to 0x0800001000 (in RAM) and "a" is held at address 0x090000000 (in RAM). What you are doing is like saying:
if (0x0800001000 == 0x090000000) {
...
}
which is always going to be false (unless something really weird happens)
I'm not entirely sure what you want to do, but there are two possibilities
You want to compare the first string passed in on the command line to "a"
// do it like this
if (strcmp(argv[1],"a") == 0) {
...
}
You want to compare the first character of the first string passed in on the command line to 'a'
// do it like this
if (argv[1][0] == 'a') {
...
}
We use argv[1] because argv[0] is just the name of the executable

How to check arguments passed in command line in C?

I am writing a program in C for a basic calculator. I am trying to do this using what I have learned so far: printf() and scanf() functions. I am passing arguments into my program through the command line. I am assuming three arguments will be passed at a time which includes: first int, an operator, and the second int. I want to check if the second arg passed is an operator and then check if it's +,-,*... so on. Here is what I came up with:
int main(int argc, char **argv) {
scanf("%d %c %d", &a, &oper, &b);
if (oper != 43) {
printf("Error: Operator is not a +");
return(1);
}
}
So obviously, I have omitted a lot of the code and kept the relevant part. Here I am just checking if the oper is a +. The ASCII key is 43 so I thought this would work but no luck! Any ideas? (I would like to see if I can do this just with printf and scanf if possible)
EDIT: For example if 12 b 13 was entered, it should return the error above. Same goes for '10 +a 10' or '10 ++ 10'.
Firstly I would highly recommend looking at the man-pages for any C library function you come across, they have a lot of useful information. It seems like you are using scanf() improperly as it is not made to be used with command line arguments.
You can check for matches for a single character by comparing the argument like this:
if(argv[2][0] == '+') ...
(argv[0] is the program's file name).
If would would like to compare string you can use strcmp(). But for the operator example you can get away with just checking the first and second characters in the argument like this:
if(argv[2][0] == '+' && argv[2][0] == '\0') ...
What this does is compare the first two characters of the argument. It first checks for the '+' and then checks if that is the end of the string with by checking for the null terminator '\0'.
We can make the assumption that any argument has at least two characters, the visible character and a null terminator. Performing this on other strings has no guarantee of this however.
The other characters, specifically the numbers need to be converted from their respective ASCII values to integers. You can use atoi or strtol to do this, although atoi will most likely be easier for you.
As David C. Rankin pointed out, **argv is a double pointer which at a high level and in most cases you can treat as a double array. In C a string is actually just an array of type char, so what argv[2] is doing above is first accessing the third index of **argv, this is now de-referenced to a type char * where the string (char array) is located. This can then further be de-referenced by the [0] in argv[2][0] to look at the first char of the string.
Code example:
char **my_arrays = argv; // a array of arrays
char *array = *argv; // de-references to index 0 in argv
char *array = *(argv + 1); // de-references to index 1 in argv
char *array = argv[0]; // de-references to index 0 in argv
char *array = argv[1]; // de-references to index 1 in argv
char first_char = *(*argv) // the first char of the first array of argv
char first_char = *(argv[0]) // the same as above
char first_char = argv[0][0] // the same as above
A side note. All strings in C should end in a null terminator which can be represented by NULL, 0, or '\0' values. This will represent the end of the string and many C functions rely on this to know when to stop.
Also NULL is technically a C macro, but you don't need to treat it any differently than 0 because it literally just expands to 0.
It's char **argv. As Some programmer dude said, you should reread your book/tutorial.
scanf doesn't read arguments. It reads from stdin.
Arguments are of type char* and are stored in argv. To convert these arguments to integers, use atoi or strtol (preferably strtol). See this for more info.
If you want to read from stdin using scanf, that is fine, and what you have will work as long as you instead input the data into stdin, and not as command line arguments.

How do you convert parameters from char to int in the main function for C?

I have this code:
int main(int argc, char *argv[]) {
int num = *argv[1];
When I run the function in terminal with a parameter: for example, if I were to call ./main 17, I want num = 17. However, with this code, num = 49 (ASCII value for 1 because argv is an array of characters). How would I get it to read num = 17 as an int? Playing around with the code, I can get it to convert the parameter into an int, but it will still only read/convert the first value (1 instead of 17).
I'm new to C and the concept of pointers/pointers to arrays is still confusing to me. Shouldn't *argv[1] return the value of the second char in the array? Why does it read the first value of the second char in the array instead?
Thanks for help!
How do you convert parameters from char to int?
Can be done by a simple cast (promotion), but this isn't your case.
In your case *argv[] is array of pointer to char (You can use this for breaking down complex C declarations), meaning that argv[1] is the 2nd element in the array, i.e. the 2nd char* in the array, meaning *argv[1] is the first char in the 2nd char* in the array.
To show it more clearly, assume argv holds 2 string {"good", "day"}. argv[1] is "day" and *argv[1] is 'd' (note the difference in types - char vs char*!)
Now, you are left with the 1st char in your input string i.e. '1'. Its ascii is indeed 49 as, so in order to get it's "int" value you should use atoi like this:
int i = atoi("17");
BUT atoi gets const char * so providing it with 17 is a good idea while sending it a char would not. This means atoi should get argv[1] instead of *argv[1]
int main(int argc, char *argv[]) {
int num = atoi(argv[1]);
// not : int num = *argv[1]; --> simple promotion that would take the ascii value of '1' :(
// and not: int num = atoi(*argv[1]); --> argument is char
note: atoi is considered obsolete so you may want to use long int strtol(const char *str, char **endptr, int base) but for a simple example I preferred using atoi
Shouldn't *argv[1] return the value of the second char in the array?
Look at the signature:
int main(int argc, char *argv[])
Here, argv is an array ([]) of pointers (*) to char. So argv[1] is the second pointer in this array. It points to the first argument given at the command line. argv[0] is reserved for the name of the program itself. Although this can also be any string, the name of the program is put there by convention (shells do this).
If you just dereference a pointer, you get the value it points to, so *argv[1] will give you the first character of the first argument. You could write it as argv[1][0], they're equivalent. To get the second character of the first argument, you'd write argv[1][1].
An important thing to note here is that you can never pass an array to a function in C. The signature above shows an array type, but C automatically adjusts array types to pointer types in function declarations. This results in the following declaration:
int main(int argc, char **argv)
The indexing operator ([]) in C works in terms of pointer arithmetics: a[x] is equivalent to *(a+x). The identifier of an array is evaluated as a pointer to the first array element in most contexts (exceptions include the sizeof operator). Therefore indexing works the same, no matter whether a is an array or a pointer. That's why you can treat argv very similar to an array.
Addressing your "core" problem: You will always have strings in argv and you want numeric input, this means you have to convert a string to a number. There are already functions doing this. A very simple one is atoi(), you can use it like this:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
if (argc != 2)
{
// use program name in argv[0] for error message:
fprintf(stderr, "Usage: %s [number]\n", argv[0]);
return EXIT_FAILURE;
}
int i = atoi(argv[1]);
printf("Argument is %d.\n", i);
return EXIT_SUCCESS;
}
This will give you 0 if the argument couldn't be parsed as a number and some indeterminate value if it overflows your int. In cases where you have to make sure the argument is a valid integer, you could use strtol() instead (note it converts to long, not int, and it can handle different bases, so we have to pass 10 for decimal):
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(int argc, char **argv)
{
if (argc != 2)
{
// use program name in argv[0] for error message:
fprintf(stderr, "Usage: %s [number]\n", argv[0]);
return EXIT_FAILURE;
}
errno = 0; // reset error number
char *endptr; // this will point to the first character not read by strtol
long i = strtol(argv[1], &endptr, 10);
if (errno == ERANGE)
{
fprintf(stderr, "This number is too small or too large.\n");
return EXIT_FAILURE;
}
else if (endptr == argv[1])
{
// no character was converted. This also catches the case of an empty argument
fprintf(stderr, "The argument was not a number.\n");
return EXIT_FAILURE;
}
else if (*endptr)
{
// endptr doesn't point to NUL, so there were characters not converted
fprintf(stderr, "Unexpected characters in number.\n");
return EXIT_FAILURE;
}
printf("You entered %ld.\n", i);
return EXIT_SUCCESS;
}
I'm new to C and the concept of pointers/pointers to arrays is still confusing to me.
In C strings are represented by null terminated ('\0') character arrays. Let's consider the following example:
char str[] = "Hello world!"
The characters would lie contiguous in memory and the usage of str would decay to a character pointer (char*) that points to the first element of the string. The address of (&) the first element taken by &str[0] would also point to that address:
| . | . | . | H | e | l | l | o | | W | o | r | l | d | ! | \0 | . | . | . |
^ ^
str null terminator
Shouldn't *argv[1] return the value of the second char in the array?
First of all in the argv is an array of character pointer char* argv[], so that it could be interpreted like an array of strings.
The first string argv[0] is the program name of the program itself and after that the arguments that are passed are coming:
argv[0] contains a pointer to the string: "program name"
argv[1] contains a pointer to the argument: "17"
If you dereference argv[1] with the use of * you get the first character at that address, here 1 which is 49 decimal in the Ascii code. Example:
p r ("program name")
^ ^
argv[0] (argv[0] + 1)
--------------------------------------------
1 7 ("17")
^ ^
argv[1] (argv[1] + 1)
How would I get it to read num = 17 as an int?
Check the number of passed arguments with argc which contains also the program name as one (read here more about argc and argv). If there are 2 you can use strtol() to convert argv[1] to the an integer. Use strtol() over atoi() because atoi() is considered to be deprecated because there is no error checking available. If atoi() fails it simply returns 0 as integer instead of strtol() that is setting the second argument and the global errno variable to a specific value.
The followig code will use the pointer that strtol() set the second argument to, to check for conversion errors. There are also overflow and underflow errors to check like it's described here on SO. Moreover you have to check if the returned long value would fit into an int variable if you want to store it into an int variable. But for simplicity I've left that out:
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char* argv[])
{
/* At least 1 argument passed? */
if (argc >= 2)
{
char* endptr;
long num = strtol(argv[1], &endptr, 10);
/* Were characters consumed? */
if (argv[1] != endptr)
{
printf("Entered number: %ld\n", num);
}
else
{
printf("Entered argument was not a number!\n");
}
}
else
{
printf("Usage: %s [number]!\n", argv[0]);
}
return 0;
}
Here's what you want to do:
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv []) {
int num = atoi (argv[1]);
printf ("Here's what you gave me: %d", num);
return 0;
}
Here's the documentation for atoi ().
argv is an array of strings, so argv[x] points to a string. atoi () accepts an ASCII string as input and returns an int.
Bonus: This is still a bit unsafe. Try running this program without passing it a parameter and see what happens.
Also, you must take a look at the documentation for strtol (), which is a safe way of doing this.

Understanding dereference operator and pointers in C, and logic of a small program

I got this code snippet from here:
int main(int argc, char *argv[])
{
for (int i = 1; i < argc; ++i) {
char *pos = argv[i];
while (*pos != '\0') {
printf("%c\n", *(pos++));
}
printf("\n");
}
}
I have two questions:
Why are we starting the iterations of for loop at i=1, why not
start it at i=0, especially when we are ending the iterations at
i<argc and Not at i<=argc?
The second, third and fourth last lines of code! In char *pos =
argv[i];, we declare a pointer type variable and assign it a
pointer to a commandline parameter passed when running the program.
Then in while (*pos != '\0'), *pos dereferences the pointer
stored in pos, so *pos contains the actual value pointed by the
pointer stored in pos.
Then in printf("%c\n", *(pos++));, we have *(pos++), and
that is the actual question: (a) Why did he increment pos, and
(b) what is the meaning of dereferencing (pos++) with the
dereference operator *?
Why are we starting the iterations of for loop at i=1, why not start it at i=0, especially when we are ending the iterations at
i
We start at 1 because argv[0] holds the name of the program itself which we don't care about. Ignoring the first element of an array does not move the index of the last array.
We have argc elements stored in argv[]. Therefore we mustn't run until i==argc but need to stop one element earlier, just as with every other array.
The second, third and fourth last lines of code! In char *pos = argv[i];, we declare a pointer type variable and assign it a pointer
to a commandline parameter passed when running the program.
Correct. pos is a pointer and points to the first string passed via command line.
Then in while (*pos != '\0'), *pos dereferences the pointer stored in
pos, so *pos contains the actual value pointed by the pointer stored
in pos.
*pos contains the first character of the string we are currently inspecting.
Then in printf("%c\n", *(pos++));, we have *(pos++), and that is the
actual question: (a) Why did he increment pos, and (b) what is the
meaning of dereferencing (pos++) with the dereference operator *?
You have 2 things here:
1. (pos++): pos is a pointer to char and with ++ increment the pointer to point to the next element, i.e. to the next char after taking its value.
2. The value of pos (before the post-increment) is taken and dereferenced to read the char at that position.
As a result the while loop will read all characters, while the for loop handles all strings.
For the first part,
Why are we starting the iterations of for loop at i=1, why not start it at i=0, especially when we are ending the iterations at i<argc and Not at i<=argc?
Because, for hosted environment, argv[0] represents the executable name. Here, we're only interested in supplied command line arguments other than the executable name itself.
Quoting C11, chapter ยง5.1.2.2.1
If the value of argc is greater than zero, the string pointed to by argv[0]
represents the program name; [....] If the value of argc is
greater than one, the strings pointed to by argv[1] through argv[argc-1]
represent the program parameters.
Point to note: using i<=argc as the loop condition would be wrong, as C arrays use 0-based indexing.
For the second part,
(a) Why did he increment pos, and (b) what is the meaning of dereferencing (pos++) with the dereference operator *?
*(pos++), can also be read as *pos; pos++; which, reads the current value from the memory location pointed to by pos and then advances pos by one element.
To elaborate, at the beginning of each iteration of the for loop, by saying
char *pos = argv[i];
pos holds the pointer to the starting of the string which holds the supplied program parameter and by continuous increment (upto NULL), we're basically traversing the string and by dereferencing, we're reading the value at those locations.
Just for the sake of completeness, let me state, that the whole for loop body
char *pos = argv[i];
while (*pos != '\0') {
printf("%c\n", *(pos++));
can be substituted using
puts(argv[i]);
Why are we starting the iterations of for loop at i=1, why not start
it at i=0, especially when we are ending the iterations at i
Because first parameter is name of the program - and it seems author is not interested in it.
Second case is basically similar to the following (argv[i] basically being a char *):
So you have something like this:
char * c = "Hello"
and then char * p = c;
Now you have
+------------------+
| H e l l o /0 |
| ^ |
| | |
+------------------+
|
|
+
p
When you do p++ you have
+------------------+
| H e l l o /0 |
| ^ |
| | |
+------------------+
|
+-+
+
p
If you do now *p - the value you get is 'e'.
*(p++) is basically same as above two steps, just due to post increment, first the value where p points will be retrieved (before increment), and then p will advance.
Then in printf("%c\n", *(pos++));, we have *(pos++), and that is the
actual question: (a) Why did he increment pos, and (b) what is the
meaning of dereferencing (pos++) with the dereference operator *?
So in the while loop the author is traversing through the whole string until he meets null terminator '\0' and printing each character.
This on the other hand is repeated for each parameter in argv using the for loop.
Why are we starting the iterations of for loop at i=1, why not start
it at i=0, especially when we are ending the iterations at i < argc and
Not at i <= argc?
Note that the argc contains the name of the program being executed too which will be the first (given the number zero) thing to be counted. So the actual arguments starts from 1 and ends at total - 1
A note here, the command line arguments are stored in array of pointer to char, ie
argv[0] -> "YourFirstArguement"
argv[1] -> "YourSecondArguement"
.
.
argv[argc-1] -> "YourLastArguement" //Remember argc-1 is the last argument
and so. Note that each of the argument is a null terminated string
So in
char *pos = argv[i]; // Create another pointer to each string
while (*pos != '\0') {
printf("%c\n", *(pos++)); // Note %c, you're printing char by char.
}
You're just printing character by character using the format specifier %c in printf. So you need to dereference character by character in the while loop and this answers
a) Why did he increment pos, and
b) what is the meaning of dereferencing (pos++) with the dereference operator *?

Can not explain output of simple string operation in C

This is the code:
#include<stdio.h>
#include<string.h>
int main()
{
char *s = "name";
int n = strlen(s);
int i;
s = &s[n+1];
for(i=0; i<=n; i++)
{
printf("%d %c",i,*s);
s++;
}
return 0;
}
Output:
0 %1 d2 3 %4 c
I am unable to understand the output. Why its printing % although there's no escape sequence.
This line s = &s[n+1]; is causing your pointer to point off into the middle of nowhere. After that you start reading random garbage from it. Apparently that random garbage includes some % characters.
First assign s = &s[n+1]; then access out of bound memory in printf using *s. code is runing under Undefined behavior according to C standards.
Maximum index to s[] can be length of string that contains \0. Remember index value start from 0 to size of (array/string)-1
Your string is stored in memory something like:
s 23 24 25 26 27 28
+----+ +----+----+----+----+----+----+
| 23 | | n | a | m | e | \0 | ? |
+----+ +----+----+----+----+----+----+
0 1 2 3 4 5
s points to string "name"
string length of "name" is 4
length("name") + 1 = 5
? means garbage values
In expression s = &s[n+1];, n + 1 is five 5 that pointing to a location outside allocated memory for "name" string And in printf statement you access memory using * Dereference operator cause invalid memory access and behavior of this code at run time is Undefined. That is the reason you code behaving differently at different execution.
Your code compiles correctly because syntactically it correct, But at runtime access of unallocated memory can be detected by OS kernel. This may causes OS kernel send a signal core dump to the your process which caused the exception. (interesting to note: as OS detects memory right violation by a process -- An invalid access to valid memory gives: SIGSEGV And access to an invalid address gives: SIGBUS). In worth case your program may execute without any failure it produces garbage results.
s = &s[n+1];
Makes s to point unknown memory. Referring s thereafter invokes undefined behavior and anything may happen when you access it in printf.
Undefined behaviour because whenever you do s[n+1] where n is the length of the string. Also you are assigning this new address into s again. Accessing every index starting from this location will result in undifined behaviour, because you have no idea what lies at those locations, and you have access to it or not.
You may try defining another string immediately after the one you defined.
char *s = "name";
char *d = "hello test";
In that case you might end up printing the characters from the string "hello test", if the compiler happens to store the string immediately after the string "name" in the read only area. This is not guranteed.
The bottom line is that, the piece of code is not correct and results in undefined behaviour.
You are changing the pointer of s to the end of your screen, that's why you have some random garbage.
If for exemple you change your main to
void foo(char *str)
{}
int main()
{
char *s = "name";
int n = strlen(s);
int i;
s = &s[n+1];
foo("Test");
for(i=0; i<=n; i++)
{
printf("%d %c\n",i,*s);
s++;
}
return 0;
}
I think it will display test
But you should not do such thing.
s = &s[n+1]; is assignment from out of bound. value of s[n] is '\0' and after this which is s[n+1] will have some garbage value.
Assignment shown above is assigning base address of s[n+1] to s and later you are trying to print values from this new s so all values will be garbage.Accessing out of bound is Undefined behaviour.
in your program:
n = 4;
s = &s[n + 1] = &s[5];
pointer s points to a memory not uninitialized, so the output should be uncertain!
You asked:
Why its printing % although there's no escape sequence.
The escape sequence to print a % only applies when you are trying to print the % from within the format string itself. That is:
printf("%% %d\n", 1);
/* output:
% 1
*/
There is no need to escape it when it is being provided as the argument for the format conversion:
printf("%s %c\n", "%d", '%');
/* output:
%d %
*/
Your program invokes undefined behavior, since you are making s point one past the last valid object to which it is pointing to (which is allowed), and then you are reading from it (and beyond) during the printing loop (which is not allowed). Since it is undefined behavior, it could do nothing, it could crash, or it could create the output you are seeing.
The output you are getting can be obtained from the following program:
#include <stdio.h>
int main () {
const char *s = "%d %c";
int i;
for (i = 0; i < 5; ++i) {
printf("%d %c", i, *s);
s++;
}
puts("");
return 0;
}
/* output is:
0 %1 d2 3 %4 c
*/
This output would be less strange if there was a delimiter between each call to printf. If we add a newline at the end of the output after each call to printf, the output becomes:
0 %
1 d
2
3 %
4 c
As you can see, it is simply outputting the string pointed to by s, where each character it prints is preceded by the index position of that character.
As many people have pointed out, you have moved the pointer s past the end of the static string.
As the printf format string is also a static string there is a chance that the memory next to the static string "name" is the printf format string. This however is not guaranteed, you could just as well be print garbage memory.

Resources