String reverse in C - c

I have code for reversing a string. Let's say I type 'ABC', the output will be 'CBA'. However, there are some code lines I quite don't understand.
1 #include <stdio.h>
2 #include <string.h>
3
4 void print_reverse(char *s) {
5 size_t len = strlen(s);
6
7 char *t = s + len-1;
8 while(t >= s) {
9 printf("%c", *t);
10 t = t-1;
11 }
12 puts("");
13 }
14
15 int main()
16 {
17 char charinput[100];
18 printf("Enter character you want to reverse:");
19 fgets(charinput, 100, stdin);
20 print_reverse(charinput);
21 getchar();
22 }
What does line 7 and 8 do? What would be the output for the pointer t?

The posted code uses the following algorithm:
Line 7: set the pointer t to the last character in the string (note: it will be a newline character if the user entered a string fewer than 99 characters). The -1 is to move one character back from the terminating nil-char
Lines 8-10: This is the core of the reversal reporting loop. The pointer t is repeatedly tested against the address at the beginning of the string. The condition clause checks to see if the t value (an address) is greater-or-equal to the beginning address of the string. So long as it is, the loop-body is entered and the character currently residing at the address held in t is sent to stdout via printf(). The address in t is then decremented by one type-width (one-byte on most-all systems with a single-byte char) and the loop repeats. Only when t contains an address before s does the loop break (and note: this is not within the standard; see below for why).
Something you should know about this loop (and should point out to the author if it isn't you). The final pointer comparison is not standard-compliant. The standard specifies comparison between non-null, like-type, pointers is valid from the base address of a valid sequence (charinput in this code, the address parameterized through s) up to and including one type-element past the allocated region of memory. This code compares t against s, breaking the loop only when t is "less". But as soon as t is less-than-s its value is no longer legally range-comparable against s. In accordance with the standard, this is so because t no longer contains a valid address that falls in the range from charinput through 1-past the size of the charinput memory block.
One way to do this correctly is the following:
t = s + len;
while (t-- > s)
printf("%c", *t);
Edit: after a journey into the standard after prodding from Paul Hankin the prior code has been rewritten to account for an unnoticed UB condition. The updated code is documented below:
t = s + len;
while (t != s)
printf("%c", *--t);
This will also work for zero-length strings. How it works is as follows:
t is set to the address of the terminating nulchar of the string.
Enter the loop, the condition being continue so long as the address in t is not equivalent to the base address of s.
Decrement t, then dereference the resulting address to obtain the current character, sending the result to printf.
Loop around for next iteration.

Let's understand it step by step:
len = strlen(s) will assign size of string s pointing to in bytes to the len (say this len is 10).
s is pointing to the first character of the string. Let's assume the address of first element of this string is 100, then s contains 100.
Adding len-1 to s will give 109.
Now, the line 7
char *t = s + len-1;
tells the compiler that t is pointing to the element at address 109, i.e, last element of string.
Line 8
while(t >= s) {
tells the compiler that loop will continue until t points to something before the first element of the string.

line 7: pointer t is pointing to the last character (s+len-1).
line 8: repeat the step when the address of the t equals or greater than the address of the s. suppose if s pointing to address of the first input string is 1101, the address of the next character is 1101+1=1102 and third is 1102+1=1103 and so on. so t point to 1101 + len-1 in line 7 would be 1101+10-1 (1110) if you input has 10 characters long.
line 9:print the character hold by address pointing by t.
line 10: t is decremented by 1 and now point to the immediate left character.
9 and 10 repeated while the address is greater or equal (1110 in my illustration)

t starts to point at the last character of the string s and in the following loop is decreased until it points to the first character. For each loop iteration the character is printed.

Line 7 sets the pointer t to point to the end of the string s. Line 8 is a while loop (which will go backward through the string, until the beginning). The pointer t is the current position in the string and is output on line 9.

char *t = s + len-1; : To point to the last character of string s
while(t >= s) : To scan all the characters of string s in reverse order (as s points to first character and we have made t point to last character in line 7).
Hope this helps.

Related

Unable to understand this C code snippet from KN King book

#include<stdio.h>
int main(void)
{
char s[] = "Hsjodi", *p;
for(p=s; *p; p++)
--*p;
puts(s);
return 0;
}
This is a code snippet given in chapter 13 exercise of
K N King
C Programming: A Modern Approach, 2nd Edn 2008. The question asks for the output.
I was not able to understand and depict the output visually so I coded it in editor and executed it.
This code gives Grinch as output. Can anyone explain why? I'm really having hard time understanding pointers and Strings in C.
No worries, with pointers you just need to remember that *p means whatever is inside p
and p alone means the memory address
so with that in mind,
Remember the ASCII TABLE, this program is operating chars, and because a string is a group of chars, every char has an address memory which happens to be next from the other one.
Have in mind that every char has a number asigned, using -- means to rest -1 to the number asigned of that char.
So with the string or char group Hsjodi the for just rests -1 to every character
Let's go through...
H in ASCII means 48, now do --*p which is -1 so now it's 47 the char G
s in ASCII means 115, now do --*p which is -1 so now it's 114 the char r
j in ASCII means 106, now do --*p which is -1 so now it's 105 the char i
o in ASCII means 111, now do --*p which is -1 so now it's 110 the char n
d in ASCII means 100, now do --*p which is -1 so now it's 99 the char c
i in ASCII means 108, now do --*p which is -1 so now it's 107 the char h
which ends up like this Grinch
for(p=s; // P is a pointer to the first character in S ('H')
*p; // Keep the loop going as long as the character at P is not zero (which, in C, marks the end of the string)
p++) // Each pass, move P to the next character s... j... o... etc
--*p; // Take the value at pointer P, and decrease it by one.
// (H ==> G, s ==> r, the previous letter alphabetically)
The --*p means that the pointer p will first be dereferenced and then decremented by 1. At the beginning of the for loop, the pointer p is assigned to the start of the char string s. Dereferencing p will give you H, decrementing that by 1 will give you G.
The loop does this for all the characters in the string, so the decrementing all the characters in the loop by 1 will give you "Grinch" as the final output.
First, s is a char array and p is a char pointer.
In the for loop, the value of s, which is the address of the first element of the array, is assigned to p.
The judgment condition of the loop is the dereference of p. It is worth noting that the s array has 7 elements, including 6 letters and a null character \0. When the loop reaches the end, a null character in p dereference will cause the loop to abort.
After each round of the loop, p will increase automatically, that is, point to the next character.
The statements in the loop body need to be aware that the dereference symbol has a higher priority than the decrement symbol.
In other words, first dereference p as a character in the string, and then decrement it. The object of subtraction is the ascii code of the character, and the corresponding letter is the previous letter. So the output is Grinch.

printing int array as string

I am trying to print int array with %s. But it is not working. Any ideas why?
#include<stdio.h>
main() {
int a[8];
a[0]='a';
a[1]='r';
a[2]='i';
a[3]='g';
a[4]='a';
a[5]='t';
a[6]='o';
a[7] = '\0';
printf("%s", a);
}
It prints just a.
I tried with short as well, but it also does not work.
This is because you are trying to print a int array, where each element has a size of 4 byte (4 chars, on 32bit machines at least). printf() interprets it as char array so the first element looks like:
'a' \0 \0 \0
to printf(). As printf() stops at the first \0 it finds, it only prints the 'a'.
Use a char array instead.
Think about the way integers are represented - use a debugger if you must. Looking at the memory you will see plenty of 0 bytes, and %s stops when it reaches a 0 byte.
It prints just a.
That's why it prints just a. Afterwards it encounters a 0 byte and it stops.
Because you declared a as an integer, so those signle characters you initialized would result in an error. You must change it to a char variable. However to save time, just make the variable a pointer using the asterisk character, which then allows you to make a single string using double quotes.
int a[8] means array of 8 ints or 8*(4 bytes) - Say 32 bit architecture
a[0] = 'a' stores in the first int index as 'a''\0''\0''\0'
a[1] = 'r' as 'r''\0''\0''\0' and so on . . .
%s represents any C-style string ie. any string followed by a '\0' character
So
printf("%s", a);
searches for trailing '\0' character and just prints "a" assuming it is the entire string

How does this loop end?

code example like this:
#include<stdio.h>
void main()
{
char *s={"abcd"};
do {
printf("%d\n",++s);
} while(*s);
}
Where does the pointer s point when the loop do ends?How does it work?
In C, zero is equivalent to false. So when *s points to the terminator character in the string (a zero), the loop stops.
"abcd" is stored in memory in 5 consecutive bytes: 'a' 'b' 'c' 'd' '\0'.
The last byte, zero, terminates the loop since zero is false in C.
First of all, you should not use %d for formatting a pointer, it is for integers, not for pointers. Use %p instead.
This line-
char *s={"abcd"};
initializes the string with '\0' as the last character.
Your programs loops through each character (from 2nd to last) of the string an prints the address of where they are stored in the memory. Since it is a do-while loop, the condition checking is done after the execution of the body of the loop.
NOTE: It does NOT print the address of the first character because-
printf("%d\n",++s);
this line (due to prefix increment) increments the pointer to the next character before passing its value to printf. So when the body of the loop is executed for the first time, the address of the second character(b) is printed.
Now the condition part of the loop checks whether character at pointed by s (the character can be referred to by *s) is non-zero.
As the string has '\0' as the last character (which has an integer value of 0), the loop terminates when it reaches the last character.
The output of your program (with %d changed to %p) will be something like but NOT exactly same as-
0x40061d
0x40061e
0x40061f
0x400620
Note that only 4 addresses are printed (from 'b' to '\0', the address of 'a' is not printed). To do that you should try this-
#include<stdio.h>
main()
{
char *s={"abcd"};
do {
printf("%p\n",s++);
} while(*s);
printf("%p\n", s); // to print the address of the '\0' character.
}

Reseting a char pointer to the top of an array

I am writing a function and I need to count the length of an array:
while(*substring){
substring++;
length++;
}
Now when I exit the loop. Will that pointer still point to the start of the array? For example:
If the array is "Hello"
when I exit the loop with the pointer be pointed at:
H or the NULL?
If it is pointing at NULL how do I make it point at H?
Strings in C are stored with a null character (denoted \0) at the end.
Thus, one might declare a string as follows.
char *str="Hello!";
In memory, this will look like Hello!0 (or rather, a string of numbers corresponding to each letter followed by a zero).
Your code looks like this:
substring=str;
length=0;
while(*substring){
substring++;
length++;
}
When you reach the end of this loop, *substring will be equal to 0 and substring will contain the address of the 0 character mentioned above. The value of substring will not change unless you explicitly do so.
To make it point at the beginning of the string you could use substring-length, since pointers are integers and may be manipulated as such. Alternatively, you could memorize the location before you begin:
beginning=str;
substring=str;
length=0;
while(*substring){
substring++;
length++;
}
substring=beginning;
It's pointing at the NULL-terminator of the array. Just remember the position in another variable, or subtract length from the pointer.
Pointer once moved will not automatically move to any another location. So once the while loop gets over the pointer would be pointing to NULL or precisely '\0' which is a termination sequence for the string.
In order to move back to the length of string just calculate the string length, which you already are doing by incrementing the length variable.
Sample code:
#include<stdio.h>
int main()
{
char name1[10] = "test program";
char *name = '\0';
name = name1;
int len = strlen(name);
while(*name)
{
name++;
}
name=name-len;
printf("\n%s\n",name);
}
Hope this helps...
At the end of the loop, *substring will be 0. That's the condition for the loop to end:
while(*substring)
So while( (the value pointed to by substring) is not equal to 0), do stuff
But then *substring becomes 0 (i.e. end of string), so *substring will point to NULL.
If you want to bring it back to H, do substring - length
However, the function you are writing already exists. It's in string.h and it's size_t strlen(const char*) size_t is an integer the size of a pointer (i.e. 32 bits on 32 bit OS and 64 bits on 64 bit OS).

Working with atoi

I have been attacking atoi from several different angles trying to extract ints from a string 1 digit at a time.
Problem 1 - Sizing the array
Should this array of 50 chars be of size 50 or 51 (to account for null terminator)?
char fiftyNumbersOne[51] = "37107287533902102798797998220837590246510135740250";
Problem 2 - atoi output
What am I doing wrong here?
char fiftyNumbersOne[51] = "37107287533902102798797998220837590246510135740250";
int one = 0;
char aChar = fiftyNumbersOne[48];
printf("%c\n",aChar);//outputs 5 (second to last #)
one = atoi(&aChar);
printf("%d\n",one);//outputs what appears to be INT_MAX...I want 5
Problem 1
The array should be length 51. But you can avoid having to manually figure that out by simply doing char fiftyNumbersOne[] = "blahblahblah";.
Problem 2
aChar is not a pointer to the original string; it's just an isolated char floating about in memory somewhere. But atoi(&aChar) is treating it as if it were a pointer to a null-terminated string. It's simply walking through memory until it happens to find a 0 somewhere, and then interpreting everything it's found as a string.
You probably want:
one = aChar - '0';
This relies on the fact that the character values for 0 to 9 are guaranteed to be contiguous.
51.
That's because aChar is not null-terminated. If you just want to get the integer value of a char, simply use
one = aChar - '0';
Problem 1 - Sizing the array Should
this array of 50 chars be of size 50
or 51 (to account for null
terminator)?
You always want an array one bigger than what you need to store in it (to account for the null terminator). So your 50 chars should be stored in an array of size 51.
What am I doing wrong here?
Try null terminating your input string to atoi. Documentation says atoi is supposed to be given the pointer to a string - which is different than a non-terminated single character. Your results with the current code you posted vary on different platforms (I get -1 on unbuntu/gcc) .
char fiftyNumbersOne[51] = "37107287533902102798797998220837590246510135740250";
int one = 0;
char aChar = fiftyNumbersOne[48];
char intChar[2];
printf("%c\n",aChar);//outputs 5 (second to last #)
sprintf(intChar, "%c", aChar); //print the char to a null terminated string
one = atoi(&intChar);
printf("%d\n",one);//outputs what appears to be INT_MAX...I want 5
Should this array of 50 chars be of size 50 or 51 (to account for null terminator)?
51, but you can also declare it without size.
char foo[] = "foo";
What am I doing wrong here?
Not reading the documentation for atoi I guess. aChar is a char, so you're passing the right type to atoi, but atoi is expecting this type to represent a string of characters, normally terminated by the character '\0'. Your "string" isn't terminated.
One solution to this is
char aString[2];
aString[0] = fiftyNumbersOne[48];
aString[1] = '\0';
atoi(aString);
Another is doing fiftyNumbersOne[48] - '0' instead of calling atoi, since in ASCII the decimal codes are consecutive and increasing from 0 to 9.

Resources