It no longer works like that. The 64-bit Linux executables that you're likely using are position-independent and they're loaded into memory at an arbitrary address. In that case ELF file does not contain any fixed base address.
While you could make a position-dependent executable as instructed by Marco Bonelli it is not how things work for arbitrary executables on modern 64-bit linuxen, so it is more worthwhile to learn to do this with position-independent ones, but it is a bit trickier.
This worked for me to print ELF i.e. the elf header magic, and the interpreter string. This is dirty in that it probably only works for a small executable anyway.
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
int main(){
    // convert main to uintptr_t
    uintptr_t main_addr = (uintptr_t)main;
    // clear bottom 12 bits so that it points to the beginning of page
    main_addr &= ~0xFFFLLU;
    // subtract one page so that we're in the elf headers...
    main_addr -= 0x1000;
    // elf magic
    puts((char *)main_addr);
    // interpreter string, offset from hexdump!
    puts((char *)main_addr + 0x318);
}
There is another trick to find the beginning of the ELF executable in memory: the so-called auxiliary vector and getauxval:
The getauxval() function retrieves values from the auxiliary vector,
a mechanism that the kernel's ELF binary loader uses to pass certain
information to user space when a program is executed.
The location of the ELF program headers in memory will be
#include <sys/auxv.h>
char *program_headers = (char*)getauxval(AT_PHDR);
The actual ELF header is 64 bytes long, and the program headers start at byte 64 so if you subtract 64 from this you will get a pointer to the magic string again, therefore our code can be simplified to
#include <stdio.h>
#include <inttypes.h>
#include <sys/auxv.h>
int main(){
    char *elf_header = (char *)getauxval(AT_PHDR) - 0x40;
    puts(elf_header + 0x318); // or whatever the offset was in your executable
}
And finally, an executable that figures out the interpreter position from the ELF headers alone, provided that you've got a 64-bit ELF, magic numbers from Wikipedia...
#include <stdio.h>
#include <inttypes.h>
#include <sys/auxv.h>
int main() {
    // get pointer to the first program header
    char *ph = (char *)getauxval(AT_PHDR);
    // elf header at this position
    char *elfh = ph - 0x40;
    // segment type 0x3 is the interpreter;
    // program header item length 0x38 in 64-bit executables
    while (*(uint32_t *)ph != 3) ph += 0x38;
    // the offset is 64 bits at 0x8 from the beginning of the 
    // executable
    uint64_t offset = *(uint64_t *)(ph + 0x8);
    // print the interpreter path...
    puts(elfh + offset);
}