0

I was testing some of the string.h function and found a strange behaviour when using the functions strncpy and strncat.

#include <stdio.h>
#include <string.h>

int main()
{
    char name1[30], name2[30], name3[30], name4[30], name5[30];
    char *res;
        
    printf("First name: ");
    scanf("%[^\n]", name1);

    setbuf(stdin, NULL);
    printf("\nSecond name: ");
    scanf("%[^\n]", name2);
    
    strcpy(name3, name2);
    printf("\nname3: %s", name3);
    
    strncpy(name4, name3, 5);
    printf("\nname4: %s", name4);

    //printf("\nSize of first name: %zu", strlen(nome1)); //zu para size_t
    //printf("\nTamanho do segundo nome: %d", (int) strlen(nome2));

    strncpy(name5, name1, 3);
    printf("\nname1: %s", name1);
    printf("\nname2: %s", name2);
    printf("\nname3: %s", name3);
    printf("\nname4: %s", name4);
    printf("\nname5: %s", name5);
    strcat(name1, name2);
    printf("\nname1: %s", name1);
    
    strncat(name4, name5, 3);
    printf("\nname4: %s", name4);
    
    return 0;
}

When I run the code, I get the following output:

First name: apple

Second name: elefant

name3: elefant
name4: elefa
name1: apple
name2: elefant
name3: elefant
name4: elefa
name5: app|�
name1: appleelefant
name4: elefaapp

Why is name5 getting app|� instead of just getting app? Besides, sometimes I was getting the last name4, which is the concatenation of name4 with name5, as elefaapp|�. Can anyone explain this strange behavior, please?

I already searched and read the functions documentation, but I have no clue regarding that.

Dalton Cézane
  • 3,672
  • 2
  • 35
  • 60
  • 2
    You forgot to null-terminate `name5`, so it contains `'a'`, `'b'`, `'c'`, followed by garbage. Add `name5[3] = '\0';` after the `strncat` call to fix it. – Tom Karzes Jul 24 '21 at 00:08
  • 1
    `strncpy` does not add a null terminator to the string so you are getting garbage in `name5`. Use `memset(dest, '\0', sizeof(dest));` before any `strncpy` attempt. – cachique Jul 24 '21 at 00:08
  • 2
    As it says in the `strncpy` documentation, "... at most n bytes of src are copied. Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null-terminated." You need to put in a terminating null character. – SGeorgiades Jul 24 '21 at 00:18
  • @cachique , fgets puts '\0' to the empty spaces of a string, right? So, if I read a string with fgets, it should be terminated. Even when I was using fgets, the behavior was the same... Is my understanding wrong? – Dalton Cézane Jul 24 '21 at 00:30
  • 3
    @DaltonCézane You never passed `name5` to `fgets` or `scanf`. The problem isn't with `name1`. It's with `name5`. The initial values in `name5` are undefined. You then pass it to `strncpy` with a length of 3. This defined the first three values of `name5`, leaving the remaining values with their original undefined values. So after the call, `name5` contains three defined values followed by 27 undefined values. If you're going to use `strncpy` to set `name5`, then you have to make sure the first value after the last copied value gets set to `'\0'`. You forgot to do that. – Tom Karzes Jul 24 '21 at 00:35
  • 1
    The problem is not `name1` or how it is obtained. The problem is `name5` and its undefined content. – cachique Jul 24 '21 at 00:36
  • 1
    This is really something you should be able to reason out. You know what `strncpy` does, and it has been painstakingly explained here that it does not guarantee to null-terminate the result (regardless of whether the source value is null-terminated). The rest should be obvious. – Tom Karzes Jul 24 '21 at 00:38
  • Very good explanation, @Tom. Very clear. Thank you, everybody. This is not so clear when we first learned other languages such as Java and C#. Besides, I had read the wrong documentation, given that it did not warn regarding the lack of the string terminator. – Dalton Cézane Jul 24 '21 at 00:51
  • @DaltonCézane Could you link the documentation you actually read? – klutt Jul 24 '21 at 01:18
  • 1
    Dalton Cézane, `scanf("%[^\n]", name1);` without a width limit, is worse the [gets](https://stackoverflow.com/q/1694036/2410359). – chux - Reinstate Monica Jul 26 '21 at 21:54
  • @klutt , if I remember well: https://www.tutorialspoint.com/c_standard_library/c_function_strncpy.htm . This is the price paid because I did not read the official documentation... And also: even reading the official documentation, we must pay attention to the whole information. – Dalton Cézane Jul 26 '21 at 21:54
  • 1
    @DaltonCézane tutorialspoint is know for being unreliable – klutt Aug 01 '21 at 14:36
  • @klutt , I will mark you answer as "the answer". Could you please add your preferable way to terminate a string in this case? Just to make the answer more complete. – Dalton Cézane Aug 05 '21 at 13:32
  • @DaltonCézane The "best" way would depend on the situation. If the buffers are for one time use, you can simply initialize them to zero. – klutt Aug 05 '21 at 13:54

1 Answers1

2

From the documentation:

https://www.cplusplus.com/reference/cstring/strncpy/

No null-character is implicitly appended at the end of destination if source is longer than num. Thus, in this case, destination shall not be considered a null terminated C string (reading it as such would overflow).

In general, you need to manually add a '\0' to terminate the string when using strncpy

klutt
  • 30,332
  • 17
  • 55
  • 95