The program takes a pointer to a char
array and an int
. The char
array consists of two numbers, separated by a space.
The use of the function is to read the values of the char
array as integers and replace them with the multiplied value of the input:
void read_and_mul(char *arr, int scale) {
int num_arr[2]; // saving values in a int[]
char *ch = strtok(arr, " ");
num_arr[0] = scale * (atoi(ch));
ch = strtok(NULL, " ");
num_arr[1] = scale * (atoi(ch));
memset(arr, 0, sizeof(arr)); // deleting the previous value of the char[]
char one[sizeof(int)];
char two[sizeof(int)];
sprintf(one, "%d", num_arr[0]); // saving the altered numbers as chars
sprintf(two, "%d", num_arr[1]);
strcat(arr, one); // writing the multiplied values to the string
strcat(arr, " ");
strcat(arr, two);
}
However if I use it like this, it works as intended but causes a stack-smashing:
int main(int argc, char *argv[]) {
char str[] = "1 2";
read_and_mul((char *)&str, 10);
printf("string after call: %s\n", str);
return 0;
}
The terminal message in CLion is:
*** stack smashing detected ***: terminated
string after call: 10 20
Is this a potential error or an IDE warning and what is causing it?
The function has to build the string "10 20"
that contains 6
characters including the terminating null character '\0'
.
But you are trying to store this string in an array that has only 4
characters
char str[] = "1 2";
due to these statements
strcat(arr,one); // writing the multiplied values to the string
strcat(arr, " ");
strcat(arr,two);
As a result the function already invokes undefined behavior.
Another problem is in this call of memset
:
memset(arr,0,sizeof(arr));
The variable arr
within the function has the pointer type char *
. If sizeof( char * )
is equal to 8
then again there is an attempt to write to memory outside the array.
And the function should not depend on magic numbers like 2
used in this declaration
int num_arr[2];
You should always try to write more general functions.
To resolve the problem you should within the function allocate dynamically a new character array where the result string will be stored and return a pointer to the array from the function.
Also, pay attention to that it will be more clear and correct to write
read_and_mul( str, 10 );
instead of
read_and_mul((char *) &str, 10);
Here is a demonstration program that shows a possible approach to solve the task.
#include <stdio.h>
#include <stdlib.h>
char * read_and_mul( const char *s, int scale )
{
size_t n = 0;
size_t length = 0;
const char *tmp = s;
int value;
for (char *endptr; value = strtol( tmp, &endptr, 10 ), endptr != tmp; tmp = endptr)
{
++n;
length += snprintf( NULL, 0, "%d", value * scale );
}
length += n == 0 ? 1 : n;
char *result = calloc( length, sizeof( char ) );
if (result != NULL)
{
const char *tmp = s;
int first = 1;
for (char *pos = result, *endptr; value = strtol( tmp, &endptr, 10 ), endptr != tmp; tmp = endptr)
{
if (!first)
{
*pos++ = ' ';
}
else
{
first = 0;
}
pos += sprintf( pos, "%d", value * scale );
}
}
return result;
}
int main( void )
{
char s[] = "1 2 3 4 5 6 7 8 9 10";
char *result = read_and_mul( s, 10 );
if (result) printf( "\"%s\"\n", result);
free( result );
}
The program output is
"10 20 30 40 50 60 70 80 90 100"
As in general multiplication of two integers can result in overflow then to avoid such a situation you may change these statements
length += snprintf( NULL, 0, "%d", value * scale );
pos += sprintf( pos, "%d", value * scale );
to the following
length += snprintf( NULL, 0, "%lld", ( long long int )value * scale );
pos += sprintf( pos, "%lld", ( long long int )value * scale );