gets(buf);
scanf("%s", buf);
strcpy(buf, argv[1]);
scanf("%80s", buf);
strncpy(buf, argv[1], 80);
snprintf(buf, 80, argv[1]);
----The delightful answer is none of them. The first three have no bounds checking at all, meaning that they will happily overflow the buffer to an arbitrary extent (gets, at least, will usually trigger a warning on modern compilers). The next two have off-by-one errors: scanf will write a NUL byte out of bounds (and that's exploitable! https://googleprojectzero.blogspot.com/2014/08/the-poisoned-...) while strncpy will fail to NUL-terminate the string. The last one uses the right buffer length, but treats user input as a format string and can leak memory contents or produce arbitrary memory corruption with the %n format specifier.
C string handling practically invites off-by-one errors and horrible security practices out-of-the-box.