Должна ли память быть выделена с помощью Malloc для этой функции?Это вызывает ошибку сегментации? - PullRequest
0 голосов
/ 16 марта 2019

Я написал этот псевдокод, потому что я хочу попытаться создать базовый калькулятор, который я использую в большой программе.Это выглядит следующим образом:


Для каждого токена t:

Если t является оператором или левой скобкой, поместите его в стек операндов.

Иначе, если t - правая скобка: многократно вставляйте стек операндов до тех пор, пока не достигнете левой скобки.Для каждого найденного оператора:

  • Дважды вытолкните стек значений, чтобы получить операнды.
  • Выполните операцию над этими операндами.
  • Нажмите результат операции настек значений.

Иначе t обозначает число.

Преобразует t в целое число и помещает его в стек значений.Когда у вас нет токенов, несколько раз выталкивайте стек операндов, пока он не станет пустым, выполняя каждую операцию, как и раньше.На этом этапе в стеке значений должно быть одно число, которое является ответом.


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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

Не уверены, правильно ли написано или выдает ошибку?

int operandApplication(int a, int b, char op){
  if (op == '+'){
    return a + b;
    }
    else {
      return a * b;
      }
} 


int popValStack(int valstack[], int *top){
  int valdata = valstack[*top]; 
  *top = *top - 1;  
 return valdata;
}

void pushValStack(int valstack[], int *top, int value){
  *top = *top + 1;
  valstack[*top] = value;
  }

char *popOpStack (char *charstack[], int *top){
  char *chardata = charstack[*top];
  *top = *top -1; 
  return chardata;
}

void pushOpStack (char *charstack[], int *top, char *value){
  *top = *top + 1;
  charstack[*top] = value;
  }

Нужно ли выделять память в следующей основной функции, используя malloc?

int main(int argc, char *argv[]){ 

int i;
int valueStack[50];
int valcounter = 0;
int opcounter = 0; 
int l; 
int m;
char *opStack[50]; 
char *op;

for(i = 1; i < argc; i++){ 

Я использовал сравнение строк здесь, потому что это казалось проще, но не уверен, что это полезно.

  char *t = argv[i];
  int s1 = strcmp(t, "[");
  int s2 = strcmp(t, "+");
  int s3 = strcmp(t, "x");
  int s4 = strcmp(t, "]");

  if(s1 == 0 | s2 ==0 | s3 == 0){
    pushOpStack(opStack, &opcounter, argv[i]); 
  } 

  else if(s4 == 0){ 

    char *S = popOpStack(opStack, &opcounter); 

    while (*S!= '[') { 
      int a = popValStack(valueStack,  &valcounter);
      int b = popValStack(valueStack, &valcounter);
      pushValStack (valueStack, &valcounter, operandApplication(a, b, *S));
    }
  }
  else {
    int x = atoi(t);
    pushValStack(valueStack, &valcounter, x);
  }
}

while (opcounter > 0){

  op = popOpStack(opStack, &opcounter);
  l = popValStack(valueStack, &valcounter);
  m = popValStack(valueStack, &valcounter);
  pushValStack (valueStack, &valcounter, 
  operandApplication(l, m, *op));
}

printf ("%d\n", valueStack[valcounter]);

}

1 Ответ

0 голосов
/ 16 марта 2019

Нет необходимости в malloc в этой программе, как написано.Память стека, обычные объявления переменных типа int i или int valueStack[50], живет до тех пор, пока не будет объявлена ​​функция, выходящая из нее.Таким образом, все переменные, которые вы объявляете в main, будут жить до завершения программы.

С другой стороны, если вы попытаетесь вернуть память стека следующим образом:

int *newStack() {
    int stack[50];
    return stack;
}


int *valueStack = newStack();

будет проблемой, память, на которую указывает stack, будет освобождена после возврата newStack.vauleStack осталось бы указывать на освобожденную память, которая может быть перезаписана.

Эмпирическое правило: если вы возвращаете указатель, оно должно быть malloc 'd.


Я не могу воспроизвести вашу проблему, но я вижу, где она может пойти не так.Есть несколько мест, где вы можете ходить со своими стеками.

Поскольку valueStack и opStack имеют фиксированные размеры, и pushValStack, и popOpStack сойдут с конца стека, когда topдостигает 49, что приводит к (ожиданию) переполнению стека.

int valueStack[50];
int valcounter = 0;
for( int i = 0; i < 50; i++ ) {
    pushValStack(valueStack, &valcounter, i);
}

( Это 49, а не 50, потому что top увеличивается до помещения значения в стек. Это приводит к отключениюошибка «один на один», когда для хранения чего-либо в позиции 0 top необходимо начинать с -1. )

Аналогично, popValStack и popOpStack могут идти с задней сторонымассив, ведущий к переполнению стека.Отрицательные показатели работают в C;они читают память перед указателем, что плохо.Интересно, что это не вызвало ошибку для меня.YMMV.

Один из способов обнаружить такие ошибки - добавить 1039 * s, чтобы убедиться, что вы не выходите за пределы.Утверждение - это выражение, которое вы считаете истинным.Если это не так, программа останавливается.

#include <stdio.h>
#include <assert.h>

// Here I put the stack size into a constant so it can be referenced by the asserts.
#define STACK_SIZE 50

int popValStack(int valstack[], int *top){
    assert( *top >= 0 );
    int valdata = valstack[*top]; 
    *top = *top - 1;  
    return valdata;
}

void pushValStack(int valstack[], int *top, int value){
    *top = *top + 1;
    assert( *top < STACK_SIZE );
    valstack[*top] = value;
}

int main(int argc, char *argv[]){ 
    int valueStack[STACK_SIZE];
    int valcounter = 0;
    for( int i = 0; i < STACK_SIZE; i++ ) {
        pushValStack(valueStack, &valcounter, 1);
    }
}

Тогда вместо загадочного segfault вы получите явную ошибку.

Assertion failed: (*top < STACK_SIZE), function pushValStack, file test.c, line 26.

Утверждения удобны для проверки любых предположенийи границы, которые у вас есть в вашем коде.


Сделав этот шаг дальше, мы можем улучшить код и целостность стека, используя структуру для сбора всех переменных, связанных со стеком, в одном месте..

typedef struct {
    int *stack;
    size_t size;
    size_t top;
} IntStack;

( Я использовал size_t здесь вместо int, потому что он гарантированно будет достаточно большим, чтобы вместить размер максимально возможного объекта, поэтому он подходит для массиваindexes. )

Тогда IntStack может передаваться как единое целое.Он знает его размер и местонахождение вершины.

void IntStackPush( IntStack *stack, int value ) {
    assert( stack->top < stack->size );
    stack->stack[stack->top] = value;
    stack->top += 1;
}

int IntStackPop( IntStack *stack ) {
    assert( stack->top > 0 );
    stack->top -= 1;
    return stack->stack[stack->top];
}

#define STACK_SIZE 50
int main(){ 
    int values[STACK_SIZE];
    IntStack valueStack = { .stack = values, .size = STACK_SIZE, .top = 0 };

    for( int i = 0; i < STACK_SIZE; i++ ) {
        IntStackPush(&valueStack, i);
    }

    for( int i = 0; i < STACK_SIZE; i++ ) {
        printf("%d\n", IntStackPop(&valueStack));
    }
}

Обратите внимание, что мы должны вручную инициализировать структуру, которая может быть подвержена ошибкам.Вместо этого мы можем написать функцию, которая сделает это за нас.Так как мы возвращаем указатель из функции сейчас нам нужно malloc.

IntStack *IntStackNew( size_t size ) {
    // Allocate space for the struct.
    IntStack *stack = malloc(sizeof(IntStack));

    stack->top = 0;
    stack->size = size;
    // And allocate space for the stack.
    stack->stack = malloc( size * sizeof(int) );

    return stack;
}

И так как мы выделяем память в куче, нам нужно ее освободить.

void IntStackFree( IntStack *stack ) {
    free(stack->stack);
    free(stack);
}

Теперь стек можно использовать, ничего не зная о его кишках.

#define STACK_SIZE 50
int main(){ 
    IntStack *valueStack = IntStackNew(STACK_SIZE);

    for( int i = 0; i < STACK_SIZE; i++ ) {
        IntStackPush(valueStack, i);
    }

    for( int i = 0; i < STACK_SIZE; i++ ) {
        printf("%d\n", IntStackPop(valueStack));
    }

    IntStackFree(valueStack);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...