Explanation of C buffer overflow - c

I try to understand buffer overflows. This is my code:
#include <stdio.h>
int main()
{
char buf[5] = { 0 };
char x = 'u';
printf("Please enter your name: ");
gets(buf);
printf("Hello %s!", buf);
return 0;
}
The buf array is of size five and initialized with 0es. So (with null termination) I have space for four characters. If I enter five characters (stack for example), I overwrite the null termination character and printf should print "Hello stacku!" because of the succeeding variable x. But this isn't the case. It simply prints "stack". Could someone please explain why?

The short explanation is, just because you declared 'x' on the source line after 'buf', that doesn't mean the compiler put them next to each other on the stack. With the code shown, 'x' isn't used at all, so it probably didn't get put anywhere. Even if you did use 'x' somehow (and it would have to be a way that prevents it being stuffed into a register), there's a good chance the compiler will sort it below 'buf' precisely so that it does not get overwritten by code overflowing 'buf'.
You can force this program to overwrite 'x' with a struct construct, e.g.
#include <stdio.h>
int main()
{
struct {
char buf[5];
char x[2];
} S = { { 0 }, { 'u' } };
printf("Please enter your name: ");
gets(S.buf);
printf("Hello %s!\n", S.buf);
printf("S.x[0] = %02x\n", S.x[0]);
return 0;
}
because the fields of a struct are always laid out in memory in the order they appear in the source code.1 In principle there could be padding between S.buf and S.x, but char must have an alignment requirement of 1, so the ABI probably doesn't require that.
But even if you do that, it won't print 'Hello stacku!', because gets always writes a terminating NUL. Watch:
$ ./a.out
Please enter your name: stac
Hello stac!
S.x[0] = 75
$ ./a.out
Please enter your name: stack
Hello stack!
S.x[0] = 00
$ ./a.out
Please enter your name: stacks
Hello stacks!
S.x[0] = 73
See how it always prints the thing you typed, but x[0] does get overwritten, first with a NUL, and then with an 's'?
(Have you already read Smashing the Stack for Fun and Profit? You should.)
1 Footnote for pedants: if bit-fields are involved, the order of fields in memory becomes partially implementation-defined. But that's not important for purposes of this question.

As the other answer pointed out, it's not at all guaranteed that x will sit immediately after buf in memory. But even if it did: gets is going to overwrite it. Remember: gets has no way of knowing how big the destination buffer is. (That's its fatal flaw.) It always writes the entire string it reads, plus the terminating \0. So if x happens to sit immediately after buf, then if you type a five-character string, printf is likely to print it correctly (as you saw), and if you were to inspect x's value afterwards:
printf("x = %d = %c\n", x, x);
it would likely show you that x was 0 now, not 'U'.
Here's how the memory might look initially:
+---+---+---+---+---+
buf: | | | | | |
+---+---+---+---+---+
+---+
x: | U |
+---+
So after you type "stack", it looks like this:
+---+---+---+---+---+
buf: | s | t | a | c | k |
+---+---+---+---+---+
+---+
x: |\0 |
+---+
And if you were to type "elephant" it would look like this:
+---+---+---+---+---+
buf: | e | l | e | p | h |
+---+---+---+---+---+
+---+
x: | a | n t \0
+---+
Needless to say, those three characters n, t, and \0 are likely to cause even more problems.
This is why people say not to use gets, ever. It cannot be used safely.

Local variables are generally created on the stack. In most implementations, stacks grow downward, not upward, as memory is allocated. So, it is likely that buf is at a higher address than x. That's why, when buf overflows, it does not overwrite x.
You might be able to confirm this by writing buf[-1]='v';printf("%c\n",x); although that might be affected by padding. It may also be instructive to compare the addresses with printf("%i\n",buf - &x); -- if the result is positive, then buf is at a higher address than x.
But this is all highly implementation dependent, and can change based on various compiler options. As others have said, you shouldn't rely on any of this.

Related

Can anyone tell me how this piece of code works?

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void print_reverse(char *s)
{
size_t len=strlen(s);
char *t=s+len-1;
printf("%s %s\n",t,s);
while(t>=s){
printf("%c",*t);
t=t-1;
}
puts("");
}
int main(){
print_reverse("Hello");
}
Can anyone tell how char *t=s+len-1; and while(t>=s) works. I cant understand how a number can be added to pointer and how the pointers are compared in while loop. This program is for reversing a string in c.
Lets do this line by line:
print_reverse("Hello");
void print_reverse(char *s)
Now s points to a string that contains:
- - ----+----+----+----+----+----+----+---- - -
| H | e | l | l | o | \0 |
- - ----+----+----+----+----+----+----+---- - -
^
s
That last character is called the string "NUL" terminator because "NUL" is the name of the character with ASCII value zero (all ASCII values that are not printable have three letter names).
size_t len=strlen(s);
Now len has a value of five. Notice it does not include the "NUL" terminator so even though the string takes 6 bytes the length is five.
char *t=s+len-1;
Now t has a value of s+4. If you count the memory locations this is what you get:
- - ----+----+----+----+----+----+----+---- - -
| H | e | l | l | o | \0 |
- - ----+----+----+----+----+----+----+---- - -
^ ^
s t
Note that s+strlen(s) would point to the "NUL" terminator.
printf("%s %s\n",t,s);
That printf should print Hello o
while(t>=s)
This while loop will continue as long as t>=s which means it will do the body of the loop for every character, including the one where s is pointing.
printf("%c",*t);
This prints the contents of the memory that t is pointing at. It starts with the o and continues backwards towards the H.
t=t-1;
That the part that moves t backwards. Eventually t will be past s and then the loop will end. When the loop finishes it will look like this:
- - ----+----+----+----+----+----+----+---- - -
| H | e | l | l | o | \0 |
- - ----+----+----+----+----+----+----+---- - -
^ ^
t s
Then there is this one final line:
puts("");
That prints an empty string and a final linefeed - there wasn't a linefeed in the string but we needed one so this is a way to do that.
Pointer Arithmetic
When a pointer points into an array, adding integers to the pointer or subtracting integers from the pointer moves the pointer back and forth within the array.
This function should be passed a char *s that points to a string, which is an array of characters ending in a null character ('\0'). Then size_t len = strlen(s); sets len to the size of this string, and char *t = s+len-1; sets t to point to the last character before the null character.
Then, in the loop t=t-1; moves t backward.
Unfortunately, this loop uses t>=s as its control condition. This is intended to stop when t has been moved to the character before s, meaning it has gone back before the start point. However, the C standard only defines pointer arithmetic for elements within the array plus a special position at the end of the array. If this function is passed an s that points to the beginning of an array, then the loop will eventually make t point before the array, and the C standard does not define the resulting behavior.
Other Things to Know About Pointer Arithmetic
Any object may be treated as an array of one element. If you have some type T and some object T x;, you may set a pointer T *p = &x;, and then it is allowed to advance the pointer by one element, p = p+1;. Dereferencing that pointer with *p is not defined, but you can compare it, as in &x == p, or you can subtract one from it.
If print_reverse were passed a pointer into an array beyond the beginning, then its loop would be okay. However, that is now how it is used in the example code; print_reverse("Hello"); is not good code.
Any object may be treated as an array of characters. You can convert a pointer to any object to a pointer to unsigned char and then examine the bytes that make up an object. This is used for special purposes. You should not use it in general code while you are learning C, but you should be aware it exists.

Issue With Comparing Strings In C

I'm trying to compare a string with another string and if they match I want the text "That is correct" to output but I can't seem to get it working.
Here is the code:
int main ()
{
char * password = "Torroc";
char * userInput;
printf("Please enter your password: ");
scanf("%s", userInput);
if (strcmp(password, userInput) == 0) {
printf("That is correct!");
}
}
In your code, userInput pointer does not have provision to hold the string that you are about to pass using the scanf call. You need to allocate space for the cstring userInput in your stack, before you try to save/assign any string to it. So...
You need to change the following code:
char * userInput;
to:
char userInput[200];
Here, 200 is just an arbitrary value. In your case, please select the max. length of the string + 1 for the (\0).
When you enter characters you need to store the characters somewhere.
char* userInput;
is an uninitialized pointer.
So first you declare an array for your input
char userInput[128];
Now when reading from the keyboard you need to make sure the user does not enter more characters than 127 + one for \0 because it would overwrite the stack so best way to read from the keyboard is to use fgets, it also good to check the return value, if the user simply pressed ENTER without writing anything fgets returns NULL.
if (fgets(userInput, sizeof(userInput), stdin) != NULL) {
Now you have the string that the user entered plus the end of line character. To remove it you can do something like
char* p = strchr(userInput,'\n');
if ( p != NULL ) *p = '\0';
Now you can compare the strings
if (strcmp(password, userInput) == 0) {
puts("That is correct!");
}
When you think about "a string" in C, you should see it as an array of char's.
Let me use the identifier s instead of userInput for brevity:
0 1 2 3 4 5 9
+---+---+---+---+---+---+-- --+---+
s -> | p | i | p | p | o | \0| ... | |
+---+---+---+---+---+---+-- --+---+
is what
char s[10] = "pippo";
would create.
In other words, it's a block of memory where the first 6 bytes have been initialized as shown. There is no s variable anywhere.
Instead, declaring a char * like in
char *s;
would create a variable that can hold a pointer to char:
+------------+
s| 0xCF024408 | <-- represent an address
+------------+
If you think this way, you notice immediately that doing:
scanf("%s",s);
only make sense in the first case, where there is (hopefully) enough memory to hold the string.
In the second case, the variable s points to some random address and you will end up writing something into an unknown memory area.
For completeness, in cases like:
char *s = "pippo";
you have the following situation in memory:
0 1 2 3 4 5
+---+---+---+---+---+---+ Somewhere in the
0x0B320080 | p | i | p | p | o | \0| <-- readonly portion
+---+---+---+---+---+---+ of memory
+------------+ a variable pointing
s| 0x0B320080 | <-- to the address where
+------------+ the string is
You can make s pointing somewhere else but you can't change the content of the string pointed by s.

Reading file in C

I'm reading a file in my C program and comparing every word in it with my word, which is entered via command line argument. But I get crashes, and I can't understand what's wrong. How do I track such errors? What is wrong in my case?
My compiler is clang. The code compiles fine. When running it says 'segmentation fault'.
Here is the code.
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[])
{
char* temp = argv[1];
char* word = strcat(temp, "\n");
char* c = "abc";
FILE *input = fopen("/usr/share/dict/words", "r");
while (strcmp(word, c))
{
char* duh = fgets(c, 20, input);
printf("%s", duh);
}
if (!strcmp (word, c))
{
printf("FOUND IT!\n");
printf("%s\n%s", word, c);
}
fclose(input);
}
The issue here is that you are trying to treat strings in C as you might in another language (like C++ or Java), in which they are resizable vectors that you can easily append or read an arbitrary amount of data into.
C strings are much lower level. They are simply an array of characters (or a pointer to such an array; arrays can be treated like pointers to their first element in C anyhow), and the string is treated as all of the characters within that array up to the first null character. These arrays are fixed size; if you want a string of an arbitrary size, you need to allocate it yourself using malloc(), or allocate it on the stack with the size that you would like.
One thing here that is a little confusing is you are using a non-standard type string. Given the context, I'm assuming that's coming from your cs50.h, and is just a typedef to char *. It will probably reduce confusion if you actually use char * instead of string; using a typedef obscures what's really going on.
Let's start with the first problem.
string word = strcat(argv[1], "\n");
strcat() appends the second string onto the first; it starts from the null terminator of the first string, and replaces that with the first character of the second string, and so on, until it reaches a null in the second string. In order for this to work, the buffer containing the first string needs to have enough room to fit the second one. If it does not, you may overwrite arbitrary other memory, which could cause your program to crash or have all kinds of other unexpected behavior.
Here's an illustration. Let's say that argv[1] contains the word hello, and the buffer has exactly as much space as it needs for this. After it is some other data; I've filled in other for the sake of example, though it won't actually be that, it could be anything, and it may or may not be important:
+---+---+---+---+---+---+---+---+---+---+---+---+
| h | e | l | l | o | \0| o | t | h | e | r | \0|
+---+---+---+---+---+---+---+---+---+---+---+---+
Now if you use strcat() to append "\n", you will get:
+---+---+---+---+---+---+---+---+---+---+---+---+
| h | e | l | l | o | \n| \0| t | h | e | r | \0|
+---+---+---+---+---+---+---+---+---+---+---+---+
You can see that we've overwritten the other data that was after hello. This may cause all kinds of problems. To fix this, you need to copy your argv[1] into a new string, that has enough room for it plus one more character (and don't forget the trailing null). You can call strlen() to get the length of the string, then add 1 for the \n, and one for the trailing null, to get the length that you need.
Actually, instead of trying to add a \n to the word you get in from the command line, I would recommend stripping off the \n from your input words, or using strncmp() to compare all but the last character (the \n). In general, it's best in C to avoid appending strings, as appending strings means you need to allocate memory and copy things around, and it can be easy to make mistakes doing so, as well as being inefficient. Higher level languages usually take care of the details for you, making it easier to append strings, though still just as inefficient.
After your edit, you changed this to:
char* temp = argv[1];
char* word = strcat(temp, "\n");
However, this has the same problem. A char * is a pointer to a character array. Your temp variable is just copying the pointer, not the actual value; it is still pointing to the same buffer. Here's an illustration; I'm making up addresses for the purposes of demonstration, in the real machine there will be more objects in between these things, but this should suffice for the purpose of demonstration.
+------------+---------+-------+
| name | address | value |
+------------+---------+-------+
| argv | 1000 | 1004 |-------+
| argv[0] | 1004 | 1008 | --+ <-+
| argv[1] | 1006 | 1016 | --|---+
| argv[0][0] | 1008 | 'm' | <-+ |
| argv[0][1] | 1009 | 'y' | |
| argv[0][2] | 1010 | 'p' | |
| argv[0][3] | 1011 | 'r' | |
| argv[0][4] | 1012 | 'o' | |
| argv[0][5] | 1013 | 'g' | |
| argv[0][6] | 1014 | 0 | |
| argv[1][0] | 1016 | 'w' | <-+ <-+
| argv[1][1] | 1017 | 'o' | |
| argv[1][2] | 1018 | 'r' | |
| argv[1][3] | 1019 | 'd' | |
| argv[1][4] | 1020 | 0 | |
+------------+---------+-------+ |
Now when you create your temp variable, all you are doing is copying argv[1] into a new char *:
+------------+---------+-------+ |
| name | address | value | |
+------------+---------+-------+ |
| temp | 1024 | 1016 | --+
+------------+---------+-------+
As a side note, you also shouldn't ever try to access argv[1] without checking that argc is greater than 1. If someone doesn't pass any arguments in, then argv[1] itself is invalid to access.
I'll move on to the next problem.
string c = "abc";
// ...
char* duh = fgets(c, 20, input);
Here, you are referring to the static string "abc". A string that appears literally in the source, like "abc", goes into a special, read-only part of the memory of the program. Remember what I said; string here is just a way of saying char *. So c is actually just a pointer into this read-only section of memory; and it has only enough room to store the characters that you provided in the text (4, for abc and the null character terminating the string). fgets() takes as its first argument a place to store the string that it is reading, and its second the amount of space that it has. So you are trying to read up to 20 bytes, into a read-only buffer that only has room for 4.
You need to either allocate space for reading on the stack, using, for example:
char c[20];
Or dynamically, using malloc():
char *c = malloc(20);
First problem I see is this:
string word = strcat(argv[1], "\n");
You are adding characters to the end of a buffer here.
A buffer allocated for you by the runtime enviroment, that you should consider read only.
EDIT
I'm afraid your change to the code still has the same effect.
char* temp = argv[1];
Has temp pointing to the same buffer as argv[1].
You need to allocate a buffer the proper size, and use it.
char* temp = (char*)malloc(sizeof(char) * (strlen(argv[1]) + 2));
The +2 is for the adding \n and \0 at the end.
Than you do this:
strcpy(temp, argv[1]);
strcat(temp,"\n");
The code is rather flawed. Another one:
char* duh = fgets(c, 20, input);
Here you define a pointer to char, do not initialize it (hence it contains a random value) and then you write up to 20 bytes to the address pointed to by the random data. If you're lucky you just get a cash. If not, you overwrite some other important data. Fortunately most of the systems in use today won't let you access address space of another program, so the code wreaks havoc only on itself.
The line in question could look like:
#define BUFFERSIZE 1024
...
while (reasonable condition) {
char *duh = malloc(BUFERSIZE);
if (NULL == duh) { /* not enough memory - handle error, and exit */
}
duh = fgets(duh, BUFFERSIZE, input);
if (NULL == duh) { /* handle error or EOF condition */
} else { /* check that the line is read completely,
i.e. including end-of-line mark,
then do your stuff with the data */
}
free (duh);
}
Of course, you can allocate the buffer only once (outside of the loop) and reuse it. The #define makes it easy to adjust the maximum buffer size.
Alternatively, on recent systems, you can use getline(), which is able to allocate a buffer of appropriate size for you. That you must free() at the end of the loop.
If you are on Linux/BSD, use man (e.g. man fgets) to get information on the functions, otherwise resort to internet or a decent book on C for documentation.
First, My C knowledge is old, so I'm not sure what a string is. Either way, it's helpful, but not absolutely required to have a nice pre-zeroed buffer in which to read contents of the file. So whether you zero word or do something like the following, zero the input first.
#define IN_BUF_LEN 120
char in_buf[IN_BUF_LEN] = {0};
120 characters is a safe size, assuming most of your text lines are around 80 characters or less long.
Second, you're basing your loop of the value of a strcmp rather than actually reading the file. It might accomplish the same thing, but I'd base my while on reaching end of file.
Finally, you've declared duh a pointer, not a place to store what fgets returns. That's a problem, too. So, duh should be declared similarly to in_buf above.
Finally, you're assigning the value of argv[1] at compile time, not run-time. I can't see where that's getting you what you want. If you declare temp as a pointer and then assign argv[1] to it, you'll just have another pointer to argv[1], but not actually have copied the value of argv[1] to a local variable. Why not just use argv[1]?

String replace in C?

Write a program that takes nouns and
forms their plurals on the basis of
these rules: a. If noun ends in “y”
remove the “y” and add “ies” b. If
noun ends in “s” , “ch”, or “sh”, add
“es” c. In all other cases, just add
“s” Print each noun and its plural.
Try the following data: chair
dairy boss circus fly dog
church clue dish
This is what I've got so far but it just isn't quite functioning like it's supposed to:
#include<stdlib.h>
#include <Windows.h>
#include <stdio.h>
#include <string.h>
#define SIZE 8
char *replace_str(char *str, char *orig, char *rep)
{
static char buffer[4096];
char *p;
if(!(p = strstr(str, orig)))
return str;
strncpy(buffer, str, p-str);
buffer[p-str] = '\0';
sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig));
return buffer;
}
int main(void)
{
char plural[SIZE];
printf("Enter a noun: ");
scanf("%c",&plural);
bool noreplace = false;
puts(replace_str(plural, "s","es"));
puts(replace_str(plural, "sh","es"));
puts(replace_str(plural, "ch","es"));
puts(replace_str(plural, "y", "ies"));
if(noreplace) {
puts(replace_str(plural, "","s"));
}
system("pause");
return 0;
}
I haven't taken a C class in a while can anyone help me out?
Thanks.
For a start, scanf("%c") gets a single character, not a string. You should use fgets for that, along the lines of:
fgets (buffer, SIZE, stdin);
// Remove newline if there.
size_t sz = strlen(buffer);
if (sz > 0 && buffer[sz-1] == '\n') buffer[sz-1] = '\0';
Once you've fixed that, we can turn to the function which pluralises the words along with a decent test harness. Make sure you keep your own main (with a fixed input method) since there's a couple of things in this harness which will probably make your educator suspect it's not your code. I'm just including it for our testing purposes here.
Start with something like:
#include <stdio.h>
#include <string.h>
char *pluralise(char *str) {
static char buffer[4096];
strcpy (buffer, str);
return buffer;
}
int main(void) {
char *test[] = {
"chair", "dairy", "boss", "circus", "fly",
"dog", "church", "clue", "dish"
};
for (size_t i = 0; i < sizeof(test)/sizeof(*test); i++)
printf ("%-8s -> %s\n", test[i], pluralise(test[i]));
return 0;
}
This basically just gives you back exactly what you passed in but it's a good start:
chair -> chair
dairy -> dairy
boss -> boss
circus -> circus
fly -> fly
dog -> dog
church -> church
clue -> clue
dish -> dish
The next step is to understand how to detect a specific ending and how to copy and modify the string to suit. The string is an array of characters of the form:
0 1 2 3 4 5
+---+---+---+---+---+---+
| c | h | a | i | r | $ |
+---+---+---+---+---+---+
where $ represents the null terminator \0. The numbers above give the offset from the start or the index that you can use to get a character from a particular position in that array. So str[3] will give you i.
Using that and the length of the string (strlen(str) will give you 5), you can check specific characters. You can also copy the characters to your target buffer and use a similar method to modify the end.
Like any good drug pusher, I'm going to give you the first hit for free :-)
char *pluralise(char *str) {
static char buffer[4096]; // Risky, see below.
size_t sz = strlen(str); // Get length.
if (sz >= 1 && str[sz-1] == 'y') { // Ends with 'y'?
strcpy(buffer, str); // Yes, copy whole buffer,
strcpy(&(buffer[sz-1]), "ies"); // overwrite final bit,
return buffer; // and return it.
}
strcpy(buffer, str); // If no rules matched,
strcat(buffer, "s"); // just add "s",
return buffer; // and return it.
}
Of particular interest there is the sequence:
strcpy(buffer, str);
strcpy(&(buffer[sz-1]), "ies");
The first line makes an exact copy of the string like:
0 1 2 3 4 5
+---+---+---+---+---+---+
| d | a | i | r | y | $ |
+---+---+---+---+---+---+
The second line copies the "ies" string into the memory location of buffer[sz-1]. Since sz is 5, that would be offset 4, resulting in the following change:
0 1 2 3 4 5
+---+---+---+---+---+---+
| d | a | i | r | y | $ |
+---+---+---+---+---+---+---+---+
| i | e | s | $ |
+---+---+---+---+
so that you end up with dairies.
From that, you should be able to use the same methods to detect the other string endings, and do similar copy/modify operations to correctly pluralise the strings.
Keep in mind that this is basic code meant to illustrate the concept, not necessarily hardened code that I would use in a production environment. For example, the declaration static char buffer[4096] has at least two problems that will occur under certain circumstances:
If your words are longer than about 4K in length, you'll get buffer overflow. However, even German, with its penchant for stringing basic words together in long sequences(a), doesn't have this problem :-) Still, it's something that should be catered for, if only to handle "invalid" input data.
Being static, the buffer will be shared amongst all threads calling this function if used in a multi-threaded environment. That's unlikely to end well as threads may corrupt the data of each other.
A relatively easy fix would be for the caller to also provide a buffer for the result, at least long enough to handle the largest possible expansion of a word to its plural form. But I've left that as a separate exercise since it's not really relevant to the question.
(a) Such as Donaudampfschiffahrtselektrizitätenhauptbetriebswerkbauunterbeamtengesellschaft :-)
You are reading a word as:
scanf("%c",&plural);
which is incorrect as it only reads one character.
Change it to:
scanf("%s",plural);
or even better use fgets as:
fgets (plural,SIZE, stdin);
But note that fgets might add a newline at the end of the string. If it does you need to remove it before you do the replacement as your replacement depends on the last character in the word.
Also your replacement part is incorrect. You are replacing any s with es (same with other replacements). You need to replace only the last s.
puts(replace_str(plural, "ch","es"));
Consider the input: church
strstr(3) will find the first ch, not the last ch. Ooops.
Furthermore, once you modify replace_str() to find the the last ch, you're still ripping it off and not putting it back on: chures. (Assuming your replace_str() functions as I think it does; that's some hairy code. :) So add the ch back on:
puts(replace_str(plural, "ch","ches"));
first of all u need to find last position of occurance and then call replace_str() function
and secondly scanf("%s",&plural);or use fgets()
Maybe this might help you:
str_replace
it's nicely done!

Reason for the Output

#include<stdio.h>
int main(void)
{
int a=5;
printf("%d"+1,a);
}
Output: d.
I didn't get how the output is coming: d ?
You passed as first argument of printf "%d"+1; "%d" is actually seen as a const char * that points to a memory location where %d is stored. As with any pointer, if you increment it by one, the result will point to the following element, which, in this case, will be d.
a is not used, but this should not be a problem since in general (I don't know if it's standard-mandated Edit: yes it is, see bottom) the stack cleanup responsibility for variadic functions is up to the caller (at least, cdecl does it that way, this however may or may not be UB, I don't know*).
You can see it easier this way:
#include<stdio.h>
int main(void)
{
int a=5;
const char * str="%d";
printf(str + 1, a);
}
str ---------+
|
V
+----+----+----+
| % | d | \0 |
+----+----+----+
str + 1 ----------+
|
V
+----+----+----+
| % | d | \0 |
+----+----+----+
Thus, ("%d"+1) (which is "d") is interpreted as the format string, and printf, not finding any %, will simply print it as it is. If you wanted instead to print the value of a plus 1, you should have done
printf("%d", a+1);
Edit:
* ok, it's not UB, at least for the C99 standard (§7.19.6.1.2) it's ok to have unused parameters in fprintf:
If the format is exhausted while arguments remain, the excess arguments are
evaluated (as always) but are otherwise ignored.
and printf is defined to have the same behavior at §7.19.6.3.2
The printf function is equivalent to fprintf with the argument stdout interposed
before the arguments to printf.
String literals are pointers. Advancing the pointer to "%d" by 1 results in "d". The argument is discarded.
You should do printf("%d", a+1). "%d" + 1 is a pointer to "d" inside an array of char ({'%','d','\0'}).
Because of +1. If you want to increment a do: printf("%d", a + 1); instead.
Suppose you had:
char x[] = "%d";
What do you expect
printf(x + 1, a);
to print?
Hint: t.c:5: warning: too many arguments for format
"%d" is String constant, it will be stored in the char[] in the memory. During runtime, "%d" returns the starting location of the char[]. Increasing character array pointer by one, will point to the next character. Hence "d" alone is passed to the printf function. so the output is "d"

Resources