Playing with pointers in C is fun (not really).
I have several arrays of strings I want to declare in an easy way, preferably something like:
arrayOfStrings1 = {"word1", "word2", etc. };
arrayOfStrings2 = {"anotherword1", "anotherword2", etc. };
arrayOfStrings3 = etc.
etc.
Something similar to a translation array (but not quite), so I want to be able to swap between these during runtime. For that I want a pointer pointerToArrayOfStrings that I can swap like:
pointerToArrayOfStrings = arrayOfStrings1;
doStuff();
pointerToArrayOfStrings = arrayOfStrings2;
doSomeOtherStuff();
In my naive understanding of arrays of strings and pointers to these, this is what I tried:
// Danish transforms
const unsigned char* da_DK[] = {"b","bb","c","c","cc","d","dd","e","f","ff","g","gg","h","hh","j","j","jj","k","k","kk","l","l","l","l","ll","m","mm","n","n","nn","p","pp","r","r","r","rr","s","s","s","ss","t","t","tt","v","v","vv","æ"};
// British english transforms
const unsigned char* en_GB[] = {"a","a","a","a","a","a","a","a","a","a","a","a","a","age","ai","aj","ay","b","cial","cian","cian","dj","dsj","ea","ee","ege","ei","ei","eigh","eigh","f","f","f","g","g","gs","i","i","i","j","j","k","ks","kw","l","m","n","n","o","r","s","s","sd","sdr","sion","sion","sj","sj","tial","tion","tion","tj","u","u","u","u","w","ye","ye","z"};
// More languages....
const unsigned char** laguageStrings;
// Assign language
if (streq(language, "da-DK")){
laguageStrings= da_DK;
}
else if (streq(language, "en-GB")){
laguageStrings= en_GB;
}
else
return 0;
}
Language is a char * containing the language "en-GB", "da-DK" etc., streq() is just a home brewed (somewhat faster than strcmp()) string comparison function.
Long story short, depending on compiler this approach may work, report compiler warnings or compile, but give unexpected results.
What would be the correct way to solve this problem?
There are two way of working with array of characters (strings) in C. They are as follows:
char a[ROW][COL];
char *b[ROW];
Pictorial representation is available as an inline comment in the code.
Based on how you want to represent the array of characters (strings), you can define pointer to that as follows
char (*ptr1)[COL] = a;
char **ptr2 = b;
They are fundamentally different types (in a subtle way) and so the pointers to them is also slightly different.
The following example demonstrates the different ways of working with strings in C and I hope it helps you in better understanding of array of characters (strings) in C.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define ROW 5
#define COL 10
int main(void)
{
int i, j;
char a[ROW][COL] = {"string1", "string2", "string3", "string4", "string5"};
char *b[ROW];
/*
a[][]
0 1 2 3 4 5 6 7 8 9
+---+---+---+---+---+---+---+------+---+---+
| s | t | r | i | n | g | 1 | '\0' | | |
+---+---+---+---+---+---+---+------+---+---+
| s | t | r | i | n | g | 2 | '\0' | | |
+---+---+---+---+---+---+---+------+---+---+
| s | t | r | i | n | g | 3 | '\0' | | |
+---+---+---+---+---+---+---+------+---+---+
| s | t | r | i | n | g | 4 | '\0' | | |
+---+---+---+---+---+---+---+------+---+---+
| s | t | r | i | n | g | 5 | '\0' | | |
+---+---+---+---+---+---+---+------+---+---+
*/
/* Now, lets work on b */
for (i=0 ; i<5; i++) {
if ((b[i] = malloc(sizeof(char) * COL)) == NULL) {
printf("unable to allocate memory \n");
return -1;
}
}
strcpy(b[0], "string1");
strcpy(b[1], "string2");
strcpy(b[2], "string3");
strcpy(b[3], "string4");
strcpy(b[4], "string5");
/*
b[] 0 1 2 3 4 5 6 7 8 9
+--------+ +---+---+---+---+---+---+---+------+---+---+
| --|------->| s | t | r | i | n | g | 1 | '\0' | | |
+--------+ +---+---+---+---+---+---+---+------+---+---+
| --|------->| s | t | r | i | n | g | 2 | '\0' | | |
+--------+ +---+---+---+---+---+---+---+------+---+---+
| --|------->| s | t | r | i | n | g | 3 | '\0' | | |
+--------+ +---+---+---+---+---+---+---+------+---+---+
| --|------->| s | t | r | i | n | g | 4 | '\0' | | |
+--------+ +---+---+---+---+---+---+---+------+---+---+
| --|------->| s | t | r | i | n | g | 5 | '\0' | | |
+--------+ +---+---+---+---+---+---+---+------+---+---+
*/
char (*ptr1)[COL] = a;
printf("Contents of first array \n");
for (i=0; i<ROW; i++)
printf("%s \n", *ptr1++);
char **ptr2 = b;
printf("Contents of second array \n");
for (i=0; i<ROW; i++)
printf("%s \n", ptr2[i]);
/* b should be free'd */
for (i=0 ; i<5; i++)
free(b[i]);
return 0;
}
What would be the correct way to solve this problem?
Well, the correct way would be to use a library specifically designed for dealing with multilanguage interfaces - for instance gettext.
Another way, though patchier, would be to use a hash table (also known as "dictionary" or "hash map" or "associative map" in other languages/technologies): Looking for a good hash table implementation in C
It's probably not the answer you were looking for, but you've asked the wrong question to the right problem.
Related
I'm trying to read a text file called "olaola.dict", some sort of a dictionary, that currently holds 10 words each with 5 letters and store the words into an array of strings. I'm using a char** pointer that points to an array of pointers in which each pointer points to a word from the dictionary.
So far, I've developed this code. It is printing the last 7 words correctly, but not the first three. The code is the following:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define ROW 10 /* 10 words */
#define COL 6 /* 5 letters in each word + Null terminator*/
int main(void) {
int i, j;
FILE *fp = NULL;
char **ptr = NULL;
ptr = (char**)malloc(ROW * sizeof(char*));
fp = fopen("olaola.dict","r");
for (i=0 ; i < 15; i++) {
if ((ptr[i] = malloc(sizeof(char) * COL)) == NULL) {
printf("unable to allocate memory \n");
return -1;
}
fscanf(fp, "%s", ptr[i]);
}
for(i = 0; i < ROW; i++){
printf("Word %d:%s\n", i+1,ptr[i]);
}
for (i=0 ; i < ROW; i++)
free(ptr[i]);
free(ptr);
fclose(fp);
return 0;
}
/*
ptr[] 0 1 2 3 4 5 6
+--------+ +---+---+---+---+---+---+---+
| --|------->| w | o | r | d | 0 | 0 |\0 |
+--------+ +---+---+---+---+---+---+---+
| --|------->| w | o | r | d | 0 | 1 |\0 |
+--------+ +---+---+---+---+---+---+---+
| --|------->| w | o | r | d | 0 | 2 |\0 |
+--------+ +---+---+---+---+---+---+---+
| --|------->| w | o | r | d | 0 | 3 |\0 |
+--------+ +---+---+---+---+---+---+---+
| --|------->| w | o | r | d | 0 | 4 |\0 |
+--------+ +---+---+---+---+---+---+---+
*/
The output is:
Word 1:[p:�
Word 2:0[p:�
Word 3:P[p:�
Word 4:Carlo
Word 5:Andre
Word 6:MESSI
Word 7:Arroz
Word 8:Doces
Word 9:Carro
Word 10:Tevez
Here is also the text file olaola.dict
lista
Sabes ontem Carlo
Andre
MESSI
Arroz Doces Carro Tevez
As you can see, there is no specific position for the words to be.
What would be the correct way to solve this problem?
(In another note, would it be wiser and more efficient to use a 2D array, even though the dictionary can have like 5k words ?)
I noticed that the first for loop goes from zero to 14 instead of from zero to 9. This means that the code writes beyond the boundaries of the array. I assume that this writes to the memory being allocated for the first 3 words and this is the reason why you see the wrong values for these words. Correct the for loop and see if this solves the issue.
you problem is in this line
for (i=0 ; i < 15; i++) {
it's supposed to be:
for (i=0 ; i < ROW; i++)
because you should allocate till ptr[9], but you are allocating till ptr[14] which will produce undefined behavior as the size of the array = 10
The code ran differently than I predicted, I think *(score+i*n+j) is problematic, it could also be a problem elsewhere, I'm not quite sure, but I don't know how to modify it.
#include <stdio.h>
#define STUD 30 // Maximum number of students possible
#define COURSE 5 // The maximum number of possible exam subjects
void Total(int *score, int sum[], float aver[], int m, int n);
void Print(int *score, int sum[], float aver[], int m, int n);
int main(void)
{
int i, j, m, n, score[STUD][COURSE], sum[STUD];
float aver[STUD];
printf("Enter the total number of students and courses:\n");
scanf("%d %d",&m,&n);
printf("Enter score:\n");
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
scanf("%d", &score[i][j]);
}
}
Total(*score, sum, aver, m, n);
Print(*score, sum, aver, m, n);
return 0;
}
void Total(int *score, int sum[], float aver[], int m, int n)
{
int i, j;
for (i = 0; i < m; i++)
{
sum[i] = 0;
for (j = 0; j < n; j++)
{
sum[i] = sum[i] + *(score + i * n + j);
}
aver[i] = (float) sum[i] / n;
}
}
void Print(int *score, int sum[], float aver[], int m, int n)
{
int i, j;
printf("Result:\n");
for (i = 0; i < m; i++)
{
for (j = 0; j < n; j++)
{
printf("%4d\t", *(score + i * n + j));
}
printf("%5d\t%6.1f\n", sum[i], aver[i]);
}
}
Example of a program running:
Enter the total number of students and courses:
2 3↙
Enter score:
90↙
95↙
97↙
82↙
73↙
69↙
Result:
90 95 97 282 94.0
82 73 69 224 74.7
Compiling your program yields no warnings or errors. Running it with the sample input you've provided yields:
Enter the total number of students and courses:
2 3
Enter score:
90
95
97
82
73
69
Result:
90 95 97 282 94.0
404780 0 82 404862 134954.0
This is correct for the first set of scores, but not the second. As you intuited, this means your math for accessing the array via pointer math is probably wrong.
Consider what your array actually looks like in memory. You've allocated on the stack an array that looks like:
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
| | | | | |
+---+---+---+---+---+
Your example input has filled it like this:
+---+---+---+---+---+
| 90| 95| 97| | |
+---+---+---+---+---+
| 82| 73| 69| | |
+---+---+---+---+---+
...
If you want to access the first element of the second row, you need your offset to be i * 5 rather than i * 3 which is what happens when you use i * n. This 5 we can get from your constant COURSE.
*(score + i * COURSE + j)
When you use a different offset you get data which has not been initialized, which is why you see garbage values. If you initialize all of the values in your array to 0, but leave your code otherwise unchanged, you can see this in action.
int i, j, m, n, score[STUD][COURSE] = {0}, sum[STUD];
Enter the total number of students and courses:
2 3
Enter score:
90
95
97
82
73
69
Result:
90 95 97 282 94.0
0 0 82 82 27.3
As you note, the problem is your array accesses -- you use score[i][j] in main to fill the array and then *(score + i * n + j) in your Total and Print functions to try to access it, and these are different and incompatible. The easiest fix is probably just to fix the declarations of Total and Print to match the score you are using:
void Total(int score[][COURSE], int sum[], float aver[], int m, int n);
void Print(int score[][COURSE], int sum[], float aver[], int m, int n);
Then you can just use score[i][j] in them and everything should work. You would also pass score as just score instead of *score.
Alternately, change the declaration of score to score[STUD*COURSE] and use *(score + i * n + j) (or score[i*n + j]) in main to access it like you do in Total and Print.
OP is unclear why the code uses #defines to define values for rows and columns of the array score, then goes on to use scanf() to enter new values that may or may not conflict with the #defines, or even overflow the array memory. Either method works, but using both together confuse things. Pick one or the other.
Aside: If a dynamic sized array is necessary, then it can be created as a pointer, or pointers to allocated memory, or by use of a VLA
eg: A short example of using user input with dynamic memory allocation to create the array sized to the need of the moment:
Note: following method allows you to use plain array notation to assign values:
score[i][j] = someValue://easy to use and readable
//as opposed to
*(score + i*n + j) = someValue;// cumbersome to use and read
Example:
int student, course;//using descriptive variables
printf("Enter the total number of students and courses:\n");
scanf("%d %d",&student,&course);
int (*score)[course] = create_arr_2d (student, course);
if(score)
{ //use score
...
free(score);
}
Where create_arr_2d(...) is defined:
void * create_arr_2d (size_t x, size_t y)
{
int (*ptr_2)[x] = malloc( sizeof (int *[y]) ); //allocate a true 2D array
if(ptr_2)
{
memset(ptr_2, 0, sizeof **ptr_2);
}
return ptr_2;
}
(credit for method)
Addressing your code as is. First, the following creates variables, but does not initialize any of them:
int i, j, m, n, score[STUD][COURSE], sum[STUD];
float aver[STUD];
To eliminate some of the issues you may be seeing, initialize:
int i=0, j=0, m=0, n=0, score[STUD][COURSE]={{0}}, sum[STUD]={0};
float aver[STUD]={0};
In the function prototype in your given code:
Total(int *score, int sum[], float aver[], int m, int n)
int *score
suggests a pointer to a single dimensional array is being passed, but if it is being used to represent score[STUD][COURSE], then it should be passed as `score[m][n], with the prototype changed as:
Total(int m, int n, int score[m][n], int sum[m], float aver[m]);
Then called as:
Total(STUD, COURSE, score[STUD][COURSE], sum[STUD], aver[STUD]){...}
Note, this arrangement makes use of VLA type function arguemnts
Note also, an array such as: (shortened from your values for easier viewing)
int m = 5;
int n = 4
int array[m][n] = {{0}}
creates a single contiguous block of memory, conceivably looking like this in memory:
`|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|...`
0 5 10 15 20
Where all of the elements can be accessed in a for loop like this:
for(int i=0; i<m; i++
for(int j=0;j<n;j++)
*(array + i*n + j);
...
...
I am trying to write a program which finds all anagrams (words that are remade by rearranging their letters) in the dictionary (/usr/share/dict/words file in Linux). The dictionary file contains a lot of words that ends with " 's " which I want to exclude from checking. Here is what I wrote to do that, but unfortunately the result file contains lines with just one letter "s" I have no idea where it comes from.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define TRUE 1
#define FALSE 0
int wordContainsNonAlpha(char *word);
int main(int argc, const char *argv[]){
FILE *fp_read = fopen("/usr/share/dict/words", "r");
char *word = malloc(sizeof(word));
FILE *fp_write = fopen("words.txt","w");
while (fgets(word,sizeof(word), fp_read) != NULL){
if (wordContainsNonAlpha(word)){
fprintf(fp_write,"%s","\n");
}
else{
// fputs(word, stdout);
fprintf(fp_write,"%s",word);
}
}
fclose(fp_read);
fclose(fp_write);
return 0;
}
int wordContainsNonAlpha(char *word){
int currentLetter = 0;
int wordLenght = strlen(word);
int result = FALSE;
char ch;
while ( (currentLetter < wordLenght) && (result == FALSE) ){
ch = word[currentLetter];
if (ch == '\''){
// if (!isalpha(ch)){
result = TRUE;
break;
}
currentLetter++;
}
return result;
}
The result is:
$ sdiff words.txt /usr/share/dict/words | more
A A
| A's
| AA's
| AB's
| ABM's
| AC's
| ACTH's
| AI's
| AIDS's
| AM's
AOL AOL
| AOL's
| ASCII's
| ASL's
| ATM's
| ATP's
| AWOL's
| AZ's
| AZT's
<
Aachen Aachen
Aaliyah Aaliyah
Aaliyah | Aaliyah's
Aaron Aaron
Abbas Abbas
Abbasid Abbasid
Abbott Abbott
| Abbott's
s <
Abby Abby
| Abby's
Abdul Abdul
| Abdul's
<
Abe Abe
| Abe's
Abel Abel
........
If I try to use function isalpha the result is even worse, as it seems it is looking for a words with specific lenght and does not work correct at all:
sdiff words.txt /usr/share/dict/words | more
| A
| A's
| AA's
| AB's
| ABM's
| AC's
| ACTH's
| AI's
| AIDS's
| AM's
| AOL
| AOL's
| ASCII's
| ASL's
| ATM's
| ATP's
| AWOL's
| AZ's
| AZT's
| Aachen
<
Aaliyah Aaliyah
Aaliyah | Aaliyah's
| Aaron
| Abbas
Abbasid Abbasid
| Abbott
| Abbott's
| Abby
| Abby's
| Abdul
| Abdul's
| Abe
| Abe's
| Abel
| Abel's
<
<
Abelard Abelard
Abelson Abelson
Abelson | Abelson's
Aberdee | Aberdeen
Aberdee | Aberdeen's
Abernat | Abernathy
Abernat | Abernathy's
Abidjan <
Abidjan Abidjan
Could you please help!
Your issue comes from your calls to malloc():
char *word = malloc(sizeof(word));
// Here, you allocate sizeof(char*) bytes which is only the size of a pointer and not the size of a dictionary word
while (fgets(word,sizeof(word), fp_read) != NULL){
// In this case, fgets does not stop when you expect it
To solve this issue, you can simply use, for your allocations, a constant which is the length of the longest word in the dictionary or an arbitrary value (I tested quickly with 64)
Concerning your issue with isalpha(), it's because fgets() stores the '\n' in your word.
From man fgets:
If a newline is read, it is stored into the buffer
So, you can use:
if (ch != '\n' && !isalpha(ch)){
char command [][12] = {
{"Attention!!"},
{"About Turn!"},
{"Left Turn!"},
{"right Turn!"},
{"Dismiss!"}
};
int i;
for (i=1; i<5; i++)
{
strcat (command [0], command [i]);
}
printf ("length of command[0]:%ld\nNew string:%s\n", strlen(command[0]), command[0]);
In the above block of code, do I need to check destination string command[0] to see if it has enough space for the other 4 strings?
I didn't do that but it turns out the compiler didn' t report any error and printed out the concatenated string.
Is it ok to do it like this with a for loop?
Yes, you do need to consider the space available in the target string
when using strcat(). In fact, you need to consider the available
space for all string manipulation operations. If you don't know that
there is enough space, you are not safe.
Consider the array:
char command[][12] =
{
{"Attention!!"},
{"About Turn!"},
{"Left Turn!"},
{"Right Turn!"},
{"Dismiss!"}
};
The memory for the array looks like this, using $ to represent '\0'
+---+---+---+---+---+---+---+---+---+---+---+---+
| A | t | t | e | n | t | i | o | n | ! | ! | $ |
+---+---+---+---+---+---+---+---+---+---+---+---+
| A | b | o | u | t | | T | u | r | n | ! | $ |
+---+---+---+---+---+---+---+---+---+---+---+---+
| L | e | f | t | | T | u | r | n | ! | $ | $ |
+---+---+---+---+---+---+---+---+---+---+---+---+
| R | i | g | h | t | | T | u | r | n | ! | $ |
+---+---+---+---+---+---+---+---+---+---+---+---+
| D | i | s | m | i | s | s | ! | $ | $ | $ | $ |
+---+---+---+---+---+---+---+---+---+---+---+---+
The specification of strcat() says:
The strcat function appends a copy of the string pointed to by s2
(including the terminating null character) to the end of the string
pointed to by s1. The initial character of s2 overwrites the null
character at the end of s1. If copying takes place between objects
that overlap, the behavior is undefined.
Your processing loop is:
for (int i = 1; i < 5; i++)
{
strcat(command[0], command[i]);
}
It is fairly clear that you are invoking undefined behaviour because the
extended material in command[0] runs over the content of command[1].
Nevertheless, some common implementations will actually behave sensibly,
but there is no guarantee that every implementation will.
Indeed, when run on macOS Sierra 10.12.2 with GCC 6.3.0, the first
iteration of the loop generates an Abort trap: 6 error. This is a
perfectly legitimate result of 'undefined behaviour'.
However, if we 'reimplement' strcat() to behave as you might expect,
what you end up with is:
+---+---+---+---+---+---+---+---+---+---+---+---+
| A | t | t | e | n | t | i | o | n | ! | ! | A |
+---+---+---+---+---+---+---+---+---+---+---+---+
| b | o | u | t | | T | u | r | n | ! | L | e |
+---+---+---+---+---+---+---+---+---+---+---+---+
| f | t | | T | u | r | n | ! | R | i | g | h |
+---+---+---+---+---+---+---+---+---+---+---+---+
| t | | T | u | r | n | ! | D | i | s | m | i |
+---+---+---+---+---+---+---+---+---+---+---+---+
| s | s | ! | $ | i | s | s | ! | $ | $ | $ | $ |
+---+---+---+---+---+---+---+---+---+---+---+---+
Demonstration code
#include <ctype.h>
#include <stdio.h>
#include <string.h>
static inline void n_strcat(char *s1, char *s2)
{
memmove(s1 + strlen(s1), s2, strlen(s2) + 1);
}
#ifndef USE_REAL_STRCAT
#undef strcat
#define strcat(s1, s2) n_strcat(s1, s2)
#endif /* USE_REAL_STRCAT */
static void print_bar(int cols)
{
printf(" +");
for (int i = 0; i < cols; i++)
printf("---+");
putchar('\n');
}
static void print_cmd(int rows, int cols, char cmd[rows][cols])
{
print_bar(cols);
for (int i = 0; i < rows; i++)
{
printf(" |");
for (int j = 0; j < cols; j++)
{
unsigned char u = cmd[i][j];
if (!isprint(u))
u = '$';
printf(" %c |", u);
}
putchar('\n');
print_bar(cols);
}
}
int main(void)
{
char command[][12] =
{
{ "Attention!!" },
{ "About Turn!" },
{ "Left Turn!" },
{ "Right Turn!" },
{ "Dismiss!" },
};
enum { CMD_ROWS = sizeof(command) / sizeof(command[0]) };
enum { CMD_COLS = sizeof(command[0]) / sizeof(command[0][0]) };
print_cmd(CMD_ROWS, CMD_COLS, command);
for (int i = 1; i < 5; i++)
strcat(command[0], command[i]);
printf("\nLength of command[0]: %zu\nNew string: [%s]\n\n", strlen(command[0]), command[0]);
print_cmd(CMD_ROWS, CMD_COLS, command);
return 0;
}
The output from the program is the two tables shown above, with this information in between:
Length of command[0]: 51
New string: [Attention!!About Turn!Left Turn!Right Turn!Dismiss!]
I'm going through a programming book and I'm wondering what a line of code does. There are no comments in the book or explanations about what it's supposed to do.
Below is a function that takes a character array and prints it backwards.
void print_reverse (char *s)
{
size_t len = strlen(s);
char *t = s + len -1; // I don't understand what this line is doing
while (t >= s) {
printf("%c", *t);
t = t - 1;
}
puts("");
}
Let's say s is the string "PaxDiablo", stored in memory at location 1 thus:
s
|
V
+---+---+---+---+---+---+---+---+---+----+
| P | a | x | D | i | a | b | l | o | \0 |
+---+---+---+---+---+---+---+---+---+----+
Address: 1 2 3 4 5 6 7 8 9 10
The expression t = s + len - 1 (where len is 9 in this case) sets t to eight characters past s.
s t
| |
V V
+---+---+---+---+---+---+---+---+---+----+
| P | a | x | D | i | a | b | l | o | \0 |
+---+---+---+---+---+---+---+---+---+----+
Address: 1 2 3 4 5 6 7 8 9 10
In other words, it gives you the address of the last character in the string.
The rest of the code then iterates over the string in a backwards direction, by decrementing t until it passes s.
Technically, this is undefined behaviour since you're only every supposed to compare pointers where they point to the same array on one character beyond (here we are comparing t where it's one character before the array), but you'd struggle to find a system on which this didn't work.
As Potatoswatter (I absolutely love some of the names people choose here on SO) points out in a comment, you can avoid that comparison by using the do {} while construct rather than while {}:
#include <stdio.h>
#include <string.h>
void printReverse (char *str) {
size_t len = strlen (str);
if (len != 0) {
char *pStr = str + len;
do {
putchar (*(--pStr));
} while (pStr > str);
}
putchar ('\n');
}
Let's say s points to the string "Hello". In memory, it looks like:
s --> [ H | e | l | l | o | \0 ]
Now, char *t declares a new pointer variable to a char value, that is initialized with s + len - 1, which is:
s ------ ------- s + 5 // (len is 5 since strlen("Hello") is 5)
| |
[ H | e | l | l | o | \0 ]
|
------------ finally, t = s + len - 1