Reputation: 1656
The goal of this program is to scan a string populated with numbers and white spaces between them and insert each number into an array. Then each number from the array is sent to checkPowerOfTwo
function which determines if the number sent is a power of two and prints the calculation.
When I run this program on windows everything is workingfine. Running on Linux causes a segmentation fault.
I'm compiling my code on a Linux server with : gcc -std=c99 -Wall -pedantic-errors -Werror -DNDEBUG main.c -o mtm_tot
. It compiles successfully with no errors or warnings. The problem arises when I try to run a tester : ./mtm_tot< test1.in > tmpout
. After pressing enter on this line Segmentation fault
rises.
test1.in contains : 8
5 9 -1 4 20 256 -32 17 32
The code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int checkPowerOfTwo(int x);
int main()
{
int exp,size,sum=0,*numbers;
char term,*str=NULL,*token;
printf("Enter size of input:\n");
if(scanf("%d%c", &size, &term) != 2 || term != '\n'){
printf("Invalid Size\n");
return 0;
} if(size<=0){
printf("Invalid size\n");
return 0;
} else{
numbers=(int*)malloc(size * sizeof(int));
str=(char*)malloc(sizeof(int)*(size+1) + (size-1)*sizeof(char));
if(numbers==NULL||str==NULL){
printf("Out of memory\n");
return 0;
} //else{
//printf("Memory allocated\n");
//}
printf("Enter numbers:");
fgets (str, sizeof(int)*(size+1) + (size-1), stdin);
//printf("%s",str);
token=strtok(str," ");
while(token!=NULL){
for(int i=0;i<size;i++){
//printf("token is %s\n",token);
//numbers[i]=token;
sscanf(token,"%d",&numbers[i]);
//printf("Inserting %s to the array\n ",numbers[i]);
token=strtok(NULL," ");
}
}
}
for(int j =0;j<size;j++)
{
//sscanf(numbers[j],"%d",&x);
//printf("the number im sending is : %d ",x);
exp=checkPowerOfTwo(numbers[j]);
if (exp>=0){
printf("The number %d is a power of 2: %d=2^%d\n",numbers[j],numbers[j],exp);
sum+=exp;
}
}
printf("Total exponent sum is %d",sum);
free(numbers);
free(str);
}
int checkPowerOfTwo(int x)
{
int exponent=0;
//sscanf(n,"%d",&x);
//printf("checking number %d\n",x);
if (x==0){
return -1;
} if (x==1){
return 0;
}
while( x != 1)
{
if(x % 2 != 0){
return -1;
}
x /= 2;
exponent++;
}
return exponent;
}
Upvotes: 1
Views: 141
Reputation: 9875
With the input file test1.in
as shown in the question you specify a size of 8 and provide 9 numbers.
Your code
while(token!=NULL){
for(int i=0;i<size;i++){
//printf("token is %s\n",token);
//numbers[i]=token;
sscanf(token,"%d",&numbers[i]);
//printf("Inserting %s to the array\n ",numbers[i]);
token=strtok(NULL," ");
}
}
will enter the outer while
loop and process 8 numbers in the first run of the inner for
loop.
As you have entered 9 numbers, token
will not be NULL
and the outer loop will repeat and run the inner loop again. This will partially overwrite the numbers in the array. After processing the 9th number in the first cycle, token
will become NULL
and in the 2nd cycle sscanf
will try to use the NULL
pointer which may lead to a segmentation fault.
You should combine the counter and the check for NULL
in the loop condition.
I also recommend to check the return value of sscanf
because a value != 1
will indicate invalid input.
for(int i=0; (i<size) && (token!=NULL); i++) {
if(sscanf(token,"%d",&numbers[i]) != 1) {
/* invalid input */
break;
}
token=strtok(NULL," ");
}
Of course the code following the loop must handle the case that the loop ends with i < size
if not enough values were present.
Edit: additional clarification below
Note: The error checking for scanf
is incomplete. It will return 0
if it couldn't convert an integer number, but it will also return 1
if it converted an integer number and anything is following it, e.g. for 123abc
it will convert 123
and return 1
. To check what may follow the number you could add a %c
conversion and if the return values is 2
check the converted character. ('\n'
or '\r'
may be OK here.)
I would prefer to use strtol
in a loop to parse the numbers in str
.
BTW: The size calculation for the allocation of str
is wrong. sizeof int
is the size of the internal binary representation of an int
value which is 4 (4 bytes = 32 bits) on many systems. It has nothing to do with how many characters are needed for a string representation of a number. A valid number -2147483648
needs 11 characters.
(You could use a buffer str
that is too small for the whole line but big enough for more than a valid number if you move the remaining data to the beginning and append new data after pasing a number until you have read the terminating newline.)
Upvotes: 3
Reputation: 50882
Your program logic is wrong:
for (int i = 0; i < size; i++) {
sscanf(token, "%d", &numbers[i]);
token = strtok(NULL, " ");
// token may become NULL here
// and sscanf will segfault right after
}
There may be other problems though.
Upvotes: 1