简体   繁体   中英

Segmentation fault on linux-working on windows

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;
}

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, eg 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.)

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM