Strange output without a pointer

Viewed 37

I'm a beginner in C, I wanted to write a program that converts user input into Morse code. This is what I did:

char morseCode[26][4] = {  
                        ".-",   /*  A   */
                        "-..",  /*  B   */
                        "-.-.", /*  C*  */
                        "-..",  /*  D   */
                        ".",    /*  E   */
                        "..-.", /*  F*  */
                        "--.",  /*  G   */
                        "....", /*  H*  */
                        "..",   /*  I   */
                        ".---", /*  J   */
                        "-.-",  /*  K   */
                        ".-..", /*  L*  */
                        "--",   /*  M   */
                        "-.",   /*  N   */
                        "---",  /*  O   */
                        ".--.", /*  P*  */
                        "--.-", /*  Q*  */
                        ".-.",  /*  R   */
                        "...",  /*  S   */
                        "-",    /*  T   */
                        "..-",  /*  U   */
                        "...-", /*  V*  */
                        ".--",  /*  W   */
                        "-..-", /*  X*  */
                        "-.--", /*  Y*  */
                        "--.."  /*  Z   */
                        };
char uString[100], newString[100];
int uChoice;

int main(){
    //printf("morseCode[%d] = %s\n", 2, morseCode[2]);

    printf("Enter text (max 100 characters): ");
    fgets(uString, 100, stdin);

    printf("\nString in morse code: \n");

    for (int i = 0; i < strlen(uString); i++) {
        uString[i] = toupper(uString[i]);
        newString[i] = uString[i] - 'A';
        
        printf("newString[%d] = %d\tmorseCode[newString[%d]] = %s\n", i, newString[i], i, morseCode[newString[i]]);
        }
    return 0;
}

Notice I placed a * in the comments where I was facing output errors. This is what it looked like when I printed it out: imgur

I looked online for solutions and I found someone who did the same thing, but he declared his version of "morseCode" like this:

char *morseCode[] = {/* Strings here*/};

When I did this, everything worked perfectly and outputted exactly as expected. Why does making morseCode a pointer fix these issues? I thought any array with a char datatype would immediately become a pointer anyway? Is this just a best practice I should do all the time?

2 Answers

char morseCode[26][4] allocates only four bytes for each string. Strings in C must be terminated by a null character. String literals such as ".-." automatically include this character. Some of your strings have four non-null characters, such as ".--.". Thus, they require five characters when the null character is included. Because you defined the arrays with [4], there was not enough room for the null character in the arrays, and it was not included. This caused errors when the strings were printed.

You could have fixed it by changing [4] to [5].

Changing to pointers worked because the string literals, instead of being used to initialize the arrays, were placed in memory, and pointers to them were created. When they were initializing the arrays, the null character was left out if it did not fit. When they were placed in memory, the null character was included.

The problem with your code is that the second dimension (4) of type char [26][4] is not big enough to contain the contents of some of your strings, because some of them consist of four printable characters plus a string terminator. When you use such a string to initialize one of your array elements, only the first four characters are used, leaving that element without a terminator. Undefined behavior results from attempting to use one of those unterminated elements as if it were a string. The problem could be resolved by changing the declaration to

char morseCode[26][5] = /* ... */

to leave enough room for a string terminator in every case.

Why does making morseCode a pointer fix these issues?

Making morseCode an array of pointers fixes the issue because then the array contains pointers to string literals, which are always properly terminated, instead of containing the characters directly.

I thought any array with a char datatype would immediately become a pointer anyway?

No. There is no such special behavior associated specifically with arrays of char. You are probably thinking of the property of arrays in general that in almost all contexts, when an array-valued expression is evaluated, the resulting array is automatically converted to a pointer to the array's first element. That does not mean that arrays are the same thing as pointers, and in particular, it does not mean that a char *[26] is the same as a char[26][4]. The elements of the former are pointers (8 bytes each on x86_64). The elements of the latter are arrays of four chars each (4 bytes, regardless of architecture).

Is this just a best practice I should do all the time?

It is not a best practice to prefer pointers to arrays, nor to prefer arrays of pointers to arrays of arrays. You do need to learn and understand the difference. Then you should choose whichever is more appropriate in any given programming situation.

Related