2

I created a program in which I noticed two things

  1. I used the address of pointer to print the whole word and It works but when I replaced s with *s it did not work (why is this happened?) (I used address in printf not *s the content)
  2. When I used pointer to pointer to print the character I could not print anything (I mean when I replaced %s with %c

My code :

#include<stdio.h>


int main ()
{
    char str[10]="PinkFloyd";
    char *s;
    char **s1;
    
     s=&str[0];
     
     s1=&s;
     
     printf("the word is using pointer to pointer %s",*s1); //why if I used %c does not print the first character 
     printf("\n");
     printf("the word is using s pointer %s",s); // why if I had replaced with *s does not print anything
    
    
    
    return 0;
}
Jarvis__-_-__
  • 287
  • 1
  • 13
vgag1997
  • 31
  • 1
  • `*s` has the type `char` - so, you could use `printf("%c", *s);` to print that _one_ `char` that `s` is pointing at. – Ted Lyngmo Jun 15 '21 at 15:28
  • You need to look into the manual for `printf`. The correct type of parameter depends on the format specifier. For `%s` it is `printf` doing the dereferencing for you. – Gerhardh Jun 15 '21 at 15:29
  • @TedLyngmo Ok but why when I used the address it prints the whole word? I expect to have an udefined behavior but it works – vgag1997 Jun 15 '21 at 15:30
  • Because `%s` prints one char at a time by doing something like `while(*s != '\0') { putchar(*s); ++s; }` (where `s` is the `char*` you supplied to `printf`) – Ted Lyngmo Jun 15 '21 at 15:31

5 Answers5

5

You don't need * to print a string because, in effect, printf does it for you.

When you use %s with printf, it turns out you do not actually pass your string to printf. You pass a pointer to the string's first character, and printf takes care of stepping along the string and printing all its characters.

This is no different, though, from the way strings are handled everywhere in C. Strings are arrays, but arrays are "second class citizens" in C, which basically means you never pass them around in their entirety. Pretty much every library function in C that works with strings, works with them by using pointers to their first characters.

If you call

printf("%s", *s);      /* WRONG */

it doesn't work. *s gives you the contents of the pointed-to pointer, which is the character 'P'. But %s wants a pointer, not a character.

You might also find this question and its answers interesting.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
3

The %s format specifier, prints a null terminated string pointed to by the parameter. That means that it starts from the address in the parameter, and prints one character at a time until it reaches a 0 (not the character but the actual numeric value 0).

In your case, s1 is a pointer to a pointer to a char the first character of the string, so the dereferenced *s1 is a pointer to a null terminated string. The variable s is a pointer to a char, so the dereferenced *s is a character.

If you used the format specifier for printing a null terminated string (%s) and passed a regular character, you would pass a numeric value (all characters are numerical value) to the printf method and it would try to read it, as if it was a memory address where it should read the string from. %c on the other hand expects the number to be a single character, and not a memory address.

If you want to print the first character (%c) then for s1 you would have to use **s1 and for s you would have to use *s. If you want to print the entire string (%s), then you would have to point to the first character of the string you want to print, so for s1 that would *s1 and for s you would have to use s (it is already a pointer to char).

// Print the entire string
const char *s = "Some String";
printf("%s", s);
// Prints:
// Some String

// Move the pointer and start inside the string
printf("%s", s + 5);
// Prints:
// String

// Print a single character
printf("%c", *s);
// Prints:
// S

// Create a pointer to a pointer to s
const char **ps = &s;
printf("%s", *ps);
// Prints:
// Some String

// Print the first character
printf("%c", **ps);
// Prints:
// S

In the above example s == *ps so when you dereference a point to a pointer to something you end up with a pointer to something. You can read more about the different format specifiers for the printf-like functions here: https://en.cppreference.com/w/c/io/fprintf#Parameters

Tommy Andersen
  • 7,165
  • 1
  • 31
  • 50
  • `" *s1 is a pointer to a null terminated string"` So when I use a pointer to pointer this points to the whole string so when I dereference I will take the whole string and when I decay a pointer to a string and I dereference it I wll print the first character? – vgag1997 Jun 15 '21 at 15:48
  • 1
    @vgag1997 when you create a pointer to a pointer, that pointer points to the second pointer. When you dereference the pointer, you are left with the other pointer. That pointer points to the first character of the string. – Tommy Andersen Jun 15 '21 at 15:52
  • @ Tommy Andersen [link] (https://imgur.com/a/iP5CS97) This is how I think this but if this happened how `s1` will store the whole word? because the s1 points to s which points to the first character not in the whole word this is the point that I confused Excuse me if i insist – vgag1997 Jun 15 '21 at 16:07
  • 1
    You are correct, but `s1` does not store the whole word, it only stores the memory of address of `s` and `s` stores the memory address of the first character, not the whole word. – Tommy Andersen Jun 15 '21 at 17:23
1

Why if I used %c does not print the first character?

printf("the word is using pointer to pointer %s", *s1);

%c expects to find an int (representing a char) where your *s1 argument is. That int holds the unsigned char value of the character (after conversion) that printf("%c",...) would print. *s1 is a char* and holds an address - possibly wider (sizeof(char*)) than an int (sizeof(int)) and printf may therefore leave values on the stack and then you have Undefined behavior.

Why if I had replaced with *s does not print anything?

printf("the word is using pointer to pointer %s", *s1);

Here you have a similar situation. %s expects a char* where you have *s1. It will use that char* to dereference the pointer and move it forward until it finds a '\0' character:

for(; *s1 != '\0'; ++s1) putchar(*s1);

By supplying *s instead, you supply the char value at the address pointed at by s. That's usually a value in the range [-128, 127] and is very unlikely a valid address. printf will nevertheless pick a char* value from the stack (possibly messing the stack up because it picks too many bytes) and beginnig the loop above, reading from the wrong memory addresses. Again Undefined behavior.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
0

#include <stdio.h>

int main ()
{
    char str[10]="PinkFloyd";
    char *s;
    char **s1;
    
     s=&str[0];
     
     s1=&s;
     
     printf("the word is using pointer to pointer %c",*s1[0]); //why if I used %c does not print the first character 
     printf("\n");
     printf("the word is using s pointer %s",s); // why if I had replaced with *s does not print anything
    
    
    
    return 0;
}
0

The conversion specifier %s of the function printf expects an argument of the type char * that points to first character of a string. In such a case the function outputs all characters of the string until the terminating zero character '\0' is encountered. For example

printf("the word is using s pointer %s",s);

If you will write

printf("the word is using pointer to pointer %s",*s);

then the expression *s has the type char instead of the type char *. So this call is incorrect. The function will try to interpret the internal representation of the character 'P' as a pointer of the type char *

Instead you could write

printf("the word is using pointer to pointer %c",*s);

using the conversion specifier %c. In this case the pointed single character will be outputted.

A similar problem exists with this call

printf("the word is using pointer to pointer %c",*s1);

the expression *s1 has the type char * but the conversion specifier %c expects an object of the type char.

So you need to supply an object of the type char as for example

printf("the word is using pointer to pointer %c", **s1);

or

printf("the word is using pointer to pointer %c",( *s1 )[0] );

or

printf("the word is using pointer to pointer %c",s1[0][0] );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Aside: "conversion specifier %c expects an object of the type char." --> of type `int`. "the int argument is converted to an unsigned char, and the resulting character is written." – chux - Reinstate Monica Jun 15 '21 at 16:34
  • @chux-ReinstateMonica It expects an argument of the type char. But due to integer conversions the argument is converted to the type int and in turn is converted to unsigned char by the function and is outputted. – Vlad from Moscow Jun 15 '21 at 17:33