Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using sizeof to append a character to the end of a string form user

I am trying to use the following code to append a right parenthesis to the end of a string took from the user

int main( void )
{
    char charArray[ 20 ];

    fgets( charArray, 20, stdin );

    charArray[ sizeof( charArray ) / sizeof( charArray[ 0 ] ) ] = ')';

    printf( "%s", charArray );
}

but if I enter: 4+5 the output is just: 4+5 rather then: 4+5)

I tried even the following variant, just in case

int main( void )
{
    char charArray[ 20 ];
    int n;

    fgets( charArray, 20, stdin );

    n = sizeof( charArray ) / sizeof( charArray[ 0 ] );

    charArray[ n ] = ')';

    printf( "%s", charArray );
}

but it doesn't work. So I obviated the need for a sizeof by doing

int main( void )
{
    char charArray[ 20 ];
    int i = 0;

    do{
        scanf( "%c", &charArray[ i ] );
        ++i;
    } while( charArray[ i - 1 ] != '\n' );

    charArray[ i - 1 ] = ')';

    printf( "%s", charArray );
}

However I could need sizeof in the future so I would like to know what I am doing wrong. Also the use of fgets and sizeof seems more direct and concise than that of scanf and do while with the mysterious [ i - 1 ] subscript. Just enlighten me about the sizeof approach please: what's wrong with my piece of code?

like image 272
anotherOne Avatar asked Aug 31 '20 10:08

anotherOne


2 Answers

First, the size of char is always 1, it's defined in the specification.

Second, the size of an array is the size of the array itself, in "bytes".

That means

sizeof( charArray ) / sizeof( charArray[ 0 ] )

is equal to

20 / 1

which is equal to 20.

That is, you will always write to the twenty-first element of the array, which is out of bounds and lead to undefined behavior.

If you want to append a character to the string either use strcat to append the characters as a string (making sure you don't write out of bounds):

// Check to make sure there's space in the array, the -1 is for the terminator
if (strlen(charArray) < (sizeof charArray - 1))
{
    strcat(charArray, ")");
}

Or use strlen to get the position of the string null-terminator and overwrite it with your char (but remember to add a new null-terminator, as well as check so you don't go out of bounds):

// Get the index of the current null-terminator in the string
size_t terminator_index = strlen(charArray);

// Check to make sure there's space in the array, the -1 is for the terminator
if (terminator_index < (sizeof charArray - 1))
{
    // Can append new character
    charArray[terminator_index] = ')';

    // Add terminator
    charArray[terminator_index + 1] = '\0';
}
like image 153
Some programmer dude Avatar answered Sep 28 '22 14:09

Some programmer dude


What you're doing wrong here is not knowing the difference between an array and a string. You have declared an array with size 20. That size will never change.

What you are looking for is the length of the string, and you get that with strlen(charArray).

A string is a sequence of printable characters terminated with the \0 character. The length of a string is the number of characters before the terminator.

This code will print 5, 20:

char arr[20] = "Hello";
printf("%zu, %zu\n", strlen(arr), sizeof(arr));

Note that this will print 3:

char arr[20] = "Hello";
printf("%zu\n", strlen(&arr[2]));

Also note that you can do things like this:

char arr[20] = "Hello\0World!\n";
printf("%zu, %zu\n", 
       strlen(&arr[0]), // Hello
       strlen(arr[strlen(&arr[0]) + 1]) // World!\n
);

This will print 5, 7. The second has a length of seven because the newline also counts. Also note that when you initialize an array, a '\0' is always appended, unless the size of the array is to small. This will declare a string without terminator:

char no_terminator[5] = "Hello";

Avoid that. It could cause you a lot of problems.

So in your case, you could do like this:

fgets( charArray, 20, stdin );

// Standard way of removing newline from a string. It's a very useful
// trick since fgets saves the newline in the string.
charArray[strcspn(charArray, "\n")] = '\0';

size_t len = strlen(charArray);
charArray[len] = ')';
charArray[len+1] = '\0';

But be careful so that len+1 never exceeds 20. If it does, you're invoking undefined behavior, which can be a real mess to debug. Also, it is wise to check if fgets returned NULL. If that's the case, you should handle the error.

If you handle this kind of input a lot, I could recommend this wrapper:

void my_readline(char *str, int n)
{
    if(fgets(str, n, stdin) == NULL) {
        perror("Error reading string");
        exit(EXIT_FAILURE);
    }
    str[strcspn(charArray, "\n")] = '\0';
}

Of course, you can customize the error handling in whatever way you want.

like image 31
klutt Avatar answered Sep 28 '22 14:09

klutt