why a pointer in C can print his content without dereference?

Viewed 532

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;
}
5 Answers

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.

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

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.

#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;
}

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] );
Related