Ошибка сегментации на Linux, работающей на Windows - PullRequest
1 голос
/ 30 октября 2019

Цель этой программы - отсканировать строку, заполненную числами и пробелами между ними, и вставить каждое число в массив. Затем каждое число из массива отправляется функции checkPowerOfTwo, которая определяет, является ли отправленное число степенью двойки, и печатает вычисление.

Когда я запускаю эту программу в Windows, все работает нормально. Запуск в Linux вызывает ошибку сегментации.

Я компилирую свой код на сервере Linux с: gcc -std=c99 -Wall -pedantic-errors -Werror -DNDEBUG main.c -o mtm_tot. Он успешно компилируется без ошибок и предупреждений. Проблема возникает, когда я пытаюсь запустить тестер: ./mtm_tot< test1.in > tmpout. После нажатия введите , в этой строке Segmentation fault поднимается.

test1.in contains : 8

5 9 -1 4 20 256 -32 17 32

Код:

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

Ответы [ 2 ]

3 голосов
/ 30 октября 2019

Для входного файла test1.in, как показано в вопросе, вы задаете размер 8 и вводите 9 цифр.

Ваш код

        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," ");
            }
        }

будет вводить внешний whileцикл и обработка 8 чисел в первом запуске внутреннего цикла for. Поскольку вы ввели 9 цифр, token не будет NULL, и внешний цикл будет повторяться и снова запускать внутренний цикл. Это частично перезапишет числа в массиве. После обработки 9-го числа в первом цикле token станет NULL, а во 2-м цикле sscanf попытается использовать указатель NULL, что может привести к ошибке сегментации.

Выследует объединить счетчик и проверку для NULL в состоянии цикла. Я также рекомендую проверить возвращаемое значение sscanf, потому что значение != 1 будет указывать на недопустимый ввод.

        for(int i=0; (i<size) && (token!=NULL); i++) {
            if(sscanf(token,"%d",&numbers[i]) != 1) {
                /* invalid input */
                break;
            }
            token=strtok(NULL," ");
        }

Конечно, код, следующий за циклом, должен обрабатывать случай, когда цикл заканчивается на i < size если присутствовало недостаточно значений.

Редактировать: дополнительные пояснения ниже

Примечание: проверка ошибок для scanf не завершена. Он вернет 0, если он не смог преобразовать целое число, но он также вернет 1, если он преобразовал целое число и что-то последует за ним, например, для 123abc он преобразует 123 и вернет 1. Чтобы проверить, что может следовать за числом, вы можете добавить %c преобразование и, если возвращаемые значения 2, проверьте преобразованный символ. ('\n' или '\r' может быть в порядке.)

Я бы предпочел использовать strtol в цикле для разбора чисел в str.

Кстати: расчет размера для выделения str неверен. sizeof int - это размер внутреннего двоичного представления значения int, которое во многих системах составляет 4 (4 байта = 32 бита). Это не имеет никакого отношения к тому, сколько символов необходимо для строкового представления числа. Для действительного числа -2147483648 требуется 11 символов.

(Вы можете использовать буфер str, который слишком мал для всей строки, но достаточно велик для допустимого числа, если вы переместите оставшиеся данные вначинайте и добавляйте новые данные после вставки номера, пока не прочитаете завершающий символ новой строки.)

1 голос
/ 30 октября 2019

Ваша логика программы неверна:

  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
  }

Могут быть и другие проблемы.

...