Search code examples
cpointersmemcpy

Pointer to int == Pointer to char (somewhat)?


In this code given below , i have declared a pointer to int and we all know that memcpy returns a void pointer to destination string , so if ptr is a pointer to int then why printf("%s",ptr); is totally valid , ptr is not a pointer to char after all.

#include <stdio.h>
#include <string.h>
//Compiler version gcc  6.3.0

int main()
{
  char a1[20] ={0} , a2[20] ={0};
  int *ptr;
  fgets(a1,20,stdin);
  fgets(a2,20,stdin);
  ptr = memcpy(a1,a2,strlen(a2)-1);
  printf("%s \n",ptr);
  if(ptr)
  printf("%s",a1);
  return 0;
}

Solution

  • First consider ptr = memcpy(a1,a2,strlen(a2)-1);. memcpy is declared as void *memcpy(void * restrict, const void * restrict, size_t), so it accepts the a1 and a2 passed to it because pointers to any unqualified object type may be converted to void * or to const void *. (Pointers to object types qualified with const may also converted to const void *.) This follows from the rules for function calls in C 2018 6.5.2.2 7 (arguments are converted to the parameter types as if by assignment) and 6.5.16 1 (one operand is a possibly-qualified void * and the left has all the qualifiers of the right) and 6.5.16 2 (the right operand is converted to the type of the left).

    Then memcpy returns a void * that is its first argument (after conversion to void *), and we attempt to assign this to ptr. This satisfies the constraints of the assignment (one of the operands is a void *), so it converts the pointer to the type of ptr, which is int *. This is governed by 6.3.2.3 7:

    A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer…

    Since a1 is a char array with no alignment requested, it could have any alignment. It might not be suitable for an int. If so, then the C standard does not define the behavior of the program, per the above.

    If a1 happens to be suitably aligned for an int or the C implementation successfully converts it anyway, we go on to printf("%s \n",ptr);.

    printf is declared as int printf(const char * restrict, ...). For arguments corresponding to ..., there is no parameter type to convert to. Instead, the default argument promotions are performed. These affect integer and float arguments but not pointer arguments. So ptr is passed to printf unchanged, as an int *.

    For a %s conversion, the printf rules in 7.21.6.1 8 say “the argument shall be a pointer to the initial element of an array of character type.” While ptr is pointing to the same place in memory as the initial element, it is a pointer to an int, not a pointer to the initial element. Therefore, it is the wrong type of argument.

    7.21.6.1 9 says “… If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.” Therefore, the C standard does not define the behavior of this program.

    In many C implementations, pointers are simple addresses in memory, int * and char * have the same representation, and the compiler will tolerate passing an int * for a %s conversion. In this case, printf receives the address it is expecting and will print the string in a1. That is why you observed the result you did. The C standard does not require this behavior. Because printf is part of the standard C library, the C standard permits a compiler to treat it specially when it is called with external linkage. The compiler could, hypothetically, treat the argument as having the correct type (even though it does not) and change the printf call into a loop that used ptr as if it were a char *. I am not aware of any compilers that would generate undesired code in this case, but the point is the C standard does not prohibit it.