Dev X
Dev X

Reputation: 21

Why declaring multiple string arrays in one line cause initialized values to be messed up

I am declaring two arrays in my code without initializing them:

    char verbe[27],
         radical[27],
         termi[6][4] = { "e", "es", "e", "ons", "ez", "ent" },
         pronom[6][10] = { "Je", "Tu", "Il/elle", "Nous", "Vous", "Ils/elles" };
        
    printf("entrer un verbe\n");
    scanf("%s", verbe);
       
    strncpy(radical, verbe, strlen(verbe)-2);
        
    for (int i = 0; i < 6; i++) {
        printf("%s", pronom[i]);
        printf(" %s", radical);
        printf("%s", termi[i]);
        printf("\n");
    }

when my input for verbe[] is manger, it prints:

Je mang\376e
Tu mang\376es
Il/elle mang\376e
Nous mang\376ons
Vous mang\376ez
Ils/elles mang\376ent

I can't figure out where that \376 comes from as it's supposed to print.

If I initialize verbe[] and radical[] as empty it prints the right result which is:

 Je mange
 Tu manges
 Il/elle mange
 Nous mangons
 Vous mangez
 Ils/elles mangent

Upvotes: 1

Views: 345

Answers (2)

chqrlie
chqrlie

Reputation: 144999

The reason for the surprising output is you use strncpy to extract the verb from the input line: strncpy(radical, verbe, strlen(verbe)-2);.

strncpy is a false friend, it does not do what you think it does. As used in your code fragment, it copies all but the last 2 bytes in the input line, presumably to strip the er at the end of the verb of the first group.

The problem is it does not add a null terminator after the bytes it copied because there is no such terminator in the part of the source string it copied. Hence the rest of the radical array is unchanged and since it is not initialized, it still contains whatever garbage is present in memory at the time your function is called. In your case there happens to be a byte with the value 0xFE and a null byte at offsets 5 and 6 of the radical array, but anything else could happen on your computer or any other computer. This is called undefined behavior, and the consequences are unpredictable.

My advice is to never use strncpy. You can learn why on this page: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/

For your purpose, you can use a different technique: you can set a null terminator explicitly to strip the last 2 letters from the verb, after checking that it has indeed at least 3 letters and ends with er.

Note also that manger is not a fully regular verb: the first person plural spells nous mangeons instead of nous mangons which would be pronounced differently and be meaningless.

Here is a modified version:

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

int main() {
    char verbe[27];
    char radical[27];
    int len;
    char termi[6][4] = { "e", "es", "e", "ons", "ez", "ent" };
    char pronom[6][10] = { "Je", "Tu", "Il/elle", "Nous", "Vous", "Ils/elles" };
        
    printf("Entrer un verbe: ");
    if (scanf("%26s", verbe) != 1) {
        printf("fin de fichier inattendue\n");
        return 1;
    }
    len = strlen(verbe);
    if (len < 3) {
        printf("verbe trop court: %s\n", verbe);
        return 1;
    }
    if (verbe[len - 2] != 'e' || verbe[len - 1] != 'r') {
        printf("pas un verbe du premier groupe: %s\n", verbe);
        return 1;
    }
    strcpy(radical, verbe);
    radical[len - 2] = '\0';
        
    for (int i = 0; i < 6; i++) {
        printf("%s", pronom[i]);
        printf(" %s", radical);
        if (i == 3 && radical[len - 3] == 'g') {
            printf("e");
        }
        printf("%s", termi[i]);
        printf("\n");
    }
    return 0;
}

Example:

Entrer un verbe: manger
je mange
tu manges
il/elle mange
nous mangeons
vous mangez
ils/elles mangent

Of course French is full of special cases, for example:

Entrer un verbe: aimer
je aime
tu aimes
il/elle aime
nous aimons
vous aimez
ils/elles aiment

the first person singular should be j'aime

Entrer un verbe: placer
je place
tu places
il/elle place
nous placons
vous placez
ils/elles placent

The first person plural should be nous plaçons

And many other exceptions, even for the first group:

  • jeter -> je jette
  • péter -> je pète
  • régler -> je règle
  • appeler -> j'appelle
  • marteler -> je martèle
  • dépuceler -> je dépucelle, mais plus moderne: je dépucèle... vive l'Académie
  • habiter -> j'habite
  • harponner -> je harponne
  • yoper -> je yope
  • aller -> je vais (pas officiellement du premier groupe)

Check this site for reference: https://leconjugueur.lefigaro.fr/conjugaison/verbe/

Upvotes: 0

Vlad from Moscow
Vlad from Moscow

Reputation: 311088

The code snippet has undefined behavior because the arrays if they are declared in a block scope are uninitialized and you may not call printf

printf("%s ", B);

for uninitialized arrays.

This call expects that a passed array contains a string.

EDIT: After you changed your question cardinally then the array radical after the call of strncpy

strncpy(radical, verbe, strlen(verbe)-2);

does not contain a string. So this call

printf(" %s",radical);

again invokes undefined behavior.

You need to write at least like

strncpy(radical, verbe, strlen(verbe)-2);
radical[ strlen(verbe)-2 ] = '\0';

Upvotes: 2

Related Questions