【问题标题】:stack with objects of different type in c在c中与不同类型的对象堆叠
【发布时间】:2014-12-29 05:44:43
【问题描述】:

我想设计一个可以包含不同类型(int、float、double 或 char)对象的堆栈。堆栈的类型取决于声明。这可以在模板的帮助下使用 c++ 轻松实现。我想知道如何在没有模板的c中完成它......? 这是我所做的基本定义:

#define STACKSIZE 100
#define INT 1
#define FLOAT 2
#define CHAR 3

struct stackelement{
    int etype;
    union {
      int ival;
      float fval;
      char cval;  
    } element;
};
struct stack{
    int top;
    struct stackelement items[STACKSIZE];
};

使用这个定义,我如何声明具有特定类型的堆栈,以及如何实现推送和弹出操作?

【问题讨论】:

  • 您想对所有元素类型使用相同的堆栈类型吗?堆栈应该能够包含不同的元素类型吗?使用您当前的方法,两者都是可能的,但两者都不同于 C++ 模板(类型在运行时解析)。要获得与模板等效的编译时类型,您需要使用预处理器或代码生成。除此之外,我看不出你在哪里卡住了。它与任何其他堆栈并没有真正的不同。
  • 帮我处理声明和 push()、pop() 方法
  • 当我不知道你的问题出在哪里时,这很难做到……
  • @mafso:它是 C 而不是 C++!所以模板不相关
  • @BasileStarynkevitch:帖子中提到了模板,我只是要求澄清一下 OP 是否想要等效的东西或想要继续使用已经显示的不同方法。 (虽然接受的答案已被接受,但这里的 cmets 似乎相互矛盾......)

标签: c data-structures stack


【解决方案1】:

这里有几种不同的方法:

  1. 如果您只是想重用代码,并且您编写的每个单独的程序将只使用特定数据类型的堆栈,那么在您的 "stack.h" 文件或等效文件中,您可以:

    typedef int STACK_DATA;
    

    或类似的,根据STACK_DATA 定义所有函数,并在每个应用程序的编译时更改typedef

  2. 1234563如stack_float_pop() 等。打字有点多,但很干净。
  3. 您可以根据您的示例代码使用您的 union,并且仍然为您的不同数据类型提供单独的函数,例如 stack_float_push()

  4. 您可以通过使用 union 并使用可变参数函数来使其完全通用。您必须通过指针弹出元素,并且您会失去一些类型安全性,但优点是您只需编写一个代码块,并且您可以根据需要将不同的类型存储在同一个堆栈中。

一个堆栈中的单一类型

如果您只想在每个堆栈中存储一种类型的元素,则以下代码将执行此操作:

stack.h:

#ifndef PG_SAMPLES_AND_DEMOS_STACK_GENERIC_H
#define PG_SAMPLES_AND_DEMOS_STACK_GENERIC_H

#include <stdbool.h>

enum stack_type {
    STACK_CHAR,
    STACK_INT,
    STACK_LONG,
    STACK_FLOAT,
    STACK_DOUBLE,
    STACK_POINTER
};

typedef struct stack * Stack;

Stack stack_create(const size_t capacity, const enum stack_type type);
void stack_destroy(Stack stack);
void stack_push(Stack stack, ...);
void stack_pop(Stack stack, void * p);
bool stack_is_empty(Stack stack);

#endif      /*  PG_SAMPLES_AND_DEMOS_STACK_GENERIC_H  */

stack.c:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "stack.h"

/*  Struct to contain stack element  */

struct stack_element {
    union {
        char val_c;
        int val_i;
        long val_l;
        float val_f;
        double val_d;
        void * val_p;
    } data;
};

/*  Struct to contain stack  */

struct stack {
    size_t top;
    size_t capacity;
    enum stack_type type;
    struct stack_element * elements;
};

/*  Creates and returns a new stack of specified type and capacity  */

struct stack * stack_create(const size_t capacity, const enum stack_type type)
{
    struct stack * new_stack = malloc(sizeof *new_stack);
    if ( !new_stack ) {
        perror("couldn't allocate memory for stack");
        exit(EXIT_FAILURE);
    }

    new_stack->capacity = capacity;
    new_stack->top = 0;
    new_stack->type = type;

    new_stack->elements = malloc(sizeof *new_stack->elements * capacity);
    if ( !new_stack->elements ) {
        free(new_stack);
        perror("couldn't allocate memory for stack elements");
        exit(EXIT_FAILURE);
    }

    return new_stack;
}

/*  Destroys a previously created stack  */

void stack_destroy(struct stack * stack)
{
    free(stack->elements);
    free(stack);
}

/*  Pushes an element onto the stack  */

void stack_push(struct stack * stack, ...)
{
    if ( stack->top == stack->capacity ) {
        fprintf(stderr, "Stack full!\n");
        exit(EXIT_FAILURE);
    }

    va_list ap;
    va_start(ap, stack);

    switch ( stack->type ) {
        case STACK_CHAR:
            stack->elements[stack->top++].data.val_c = (char) va_arg(ap, int);
            break;

        case STACK_INT:
            stack->elements[stack->top++].data.val_i = va_arg(ap, int);
            break;

        case STACK_LONG:
            stack->elements[stack->top++].data.val_l = va_arg(ap, long);
            break;

        case STACK_FLOAT:
            stack->elements[stack->top++].data.val_f = (float) va_arg(ap, double);
            break;

        case STACK_DOUBLE:
            stack->elements[stack->top++].data.val_d = va_arg(ap, double);
            break;

        case STACK_POINTER:
            stack->elements[stack->top++].data.val_p = va_arg(ap, void *);
            break;

        default:
            fprintf(stderr, "Unknown type in stack_push()\n");
            exit(EXIT_FAILURE);
    }

    va_end(ap);
}

/*  Pops an element from the stack  */

void stack_pop(struct stack * stack, void * p)
{
    if ( stack->top == 0 ) {
        fprintf(stderr, "Stack empty!\n");
        exit(EXIT_FAILURE);
    }

    switch ( stack->type ) {
        case STACK_CHAR:
            *((char *) p) = stack->elements[--stack->top].data.val_c;
            break;

        case STACK_INT:
            *((int *) p) = stack->elements[--stack->top].data.val_i;
            break;

        case STACK_LONG:
            *((long *) p) = stack->elements[--stack->top].data.val_l;
            break;

        case STACK_FLOAT:
            *((float *) p) = stack->elements[--stack->top].data.val_f;
            break;

        case STACK_DOUBLE:
            *((double *) p) = stack->elements[--stack->top].data.val_d;
            break;

        case STACK_POINTER:
            *((void **) p) = stack->elements[--stack->top].data.val_p;
            break;

        default:
            fprintf(stderr, "Unknown type in stack_pop()\n");
            exit(EXIT_FAILURE);
    }
}

/*  Returns true if the stack is empty  */

bool stack_is_empty(struct stack * stack) {
    return stack->top == 0;
}

main.c:

#include <stdio.h>
#include "stack.h"

int main(void)
{
    /*  Create, push and pop with stack of type int  */

    Stack istk = stack_create(3, STACK_INT);

    stack_push(istk, 123);
    stack_push(istk, 456);
    stack_push(istk, 789);

    while ( !stack_is_empty(istk) ) {
        int i;
        stack_pop(istk, &i);
        printf("Popped int %d from stack.\n", i);
    }

    /*  Create, push and pop with stack of type long  */

    if ( sizeof(long) >= 8U ) {
        Stack lstk = stack_create(3, STACK_LONG);

        stack_push(lstk, 123000000000L);
        stack_push(lstk, 456000000000L);
        stack_push(lstk, 789000000000L);

        while ( !stack_is_empty(lstk) ) {
            long l;
            stack_pop(lstk, &l);
            printf("Popped long %ld from stack.\n", l);
        }

        stack_destroy(lstk);
    }

    /*  Create, push and pop with stack of type float  */

    Stack fstk = stack_create(3, STACK_FLOAT);

    stack_push(fstk, 1.23);
    stack_push(fstk, 4.56);
    stack_push(fstk, 7.89);

    while ( !stack_is_empty(fstk) ) {
        float f;
        stack_pop(fstk, &f);
        printf("Popped float %f from stack.\n", f);
    }

    /*  Create, push and pop with stack of type double  */

    Stack dstk = stack_create(3, STACK_DOUBLE);

    stack_push(dstk, 1.23);
    stack_push(dstk, 4.56);
    stack_push(dstk, 7.89);

    while ( !stack_is_empty(dstk) ) {
        double d;
        stack_pop(dstk, &d);
        printf("Popped double %f from stack.\n", d);
    }

    /*  Create, push and pop with stack of type void *  */

    Stack pstk = stack_create(3, STACK_POINTER);

    stack_push(pstk, (void *) &istk);
    stack_push(pstk, (void *) &fstk);
    stack_push(pstk, (void *) &dstk);

    while ( !stack_is_empty(pstk) ) {
        void * p;
        stack_pop(pstk, &p);
        printf("Popped pointer %p from stack.\n", p);
    }

    /*  Destroy stacks and exit  */

    stack_destroy(pstk);
    stack_destroy(dstk);
    stack_destroy(fstk);
    stack_destroy(istk);

    return 0;
}

带输出:

paul@thoth:~/src/sandbox/stack_generic$ ./stack_generic
Popped int 789 from stack.
Popped int 456 from stack.
Popped int 123 from stack.
Popped long 789000000000 from stack.
Popped long 456000000000 from stack.
Popped long 123000000000 from stack.
Popped float 7.890000 from stack.
Popped float 4.560000 from stack.
Popped float 1.230000 from stack.
Popped double 7.890000 from stack.
Popped double 4.560000 from stack.
Popped double 1.230000 from stack.
Popped pointer 0x7fff4a1dc1d8 from stack.
Popped pointer 0x7fff4a1dc1e0 from stack.
Popped pointer 0x7fff4a1dc1e8 from stack.
paul@thoth:~/src/sandbox/stack_generic$ 

一个堆栈中的多种类型

如果您想在一个堆栈中存储多个类型,则必须在每个 pop()push() 调用中传递元素的类型。

stack.h:

#ifndef PG_SAMPLES_AND_DEMOS_STACK_MULTITYPE_H
#define PG_SAMPLES_AND_DEMOS_STACK_MULTITYPE_H

#include <stdbool.h>

enum stack_type {
    STACK_CHAR,
    STACK_INT,
    STACK_LONG,
    STACK_FLOAT,
    STACK_DOUBLE,
    STACK_POINTER
};

typedef struct stack * Stack;

Stack stack_create(const size_t capacity);
void stack_destroy(Stack stack);
void stack_push(Stack stack, const enum stack_type type, ...);
void stack_pop(Stack stack, void * p);
enum stack_type stack_type_peek(Stack stack);
bool stack_is_empty(Stack stack);

#endif      /*  PG_SAMPLES_AND_DEMOS_STACK_MULTITYPE_H  */

stack.c:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "stack.h"

/*  Struct to contain stack element  */

struct stack_element {
    enum stack_type type;
    union {
        char val_c;
        int val_i;
        long val_l;
        float val_f;
        double val_d;
        void * val_p;
    } data;
};

/*  Struct to contain stack  */

struct stack {
    size_t top;
    size_t capacity;
    enum stack_type type;
    struct stack_element * elements;
};

/*  Creates and returns a new stack of specified type and capacity  */

struct stack * stack_create(const size_t capacity)
{
    struct stack * new_stack = malloc(sizeof *new_stack);
    if ( !new_stack ) {
        perror("couldn't allocate memory for stack");
        exit(EXIT_FAILURE);
    }

    new_stack->capacity = capacity;
    new_stack->top = 0;

    new_stack->elements = malloc(sizeof *new_stack->elements * capacity);
    if ( !new_stack->elements ) {
        free(new_stack);
        perror("couldn't allocate memory for stack elements");
        exit(EXIT_FAILURE);
    }

    return new_stack;
}

/*  Destroys a previously created stack  */

void stack_destroy(struct stack * stack)
{
    free(stack->elements);
    free(stack);
}

/*  Pushes an element onto the stack  */

void stack_push(struct stack * stack, const enum stack_type type, ...)
{
    if ( stack->top == stack->capacity ) {
        fprintf(stderr, "Stack full!\n");
        exit(EXIT_FAILURE);
    }

    va_list ap;
    va_start(ap, type);

    switch ( type ) {
        case STACK_CHAR:
            stack->elements[stack->top].data.val_c = (char) va_arg(ap, int);
            break;

        case STACK_INT:
            stack->elements[stack->top].data.val_i = va_arg(ap, int);
            break;

        case STACK_LONG:
            stack->elements[stack->top].data.val_l = va_arg(ap, long);
            break;

        case STACK_FLOAT:
            stack->elements[stack->top].data.val_f = (float) va_arg(ap, double);
            break;

        case STACK_DOUBLE:
            stack->elements[stack->top].data.val_d = va_arg(ap, double);
            break;

        case STACK_POINTER:
            stack->elements[stack->top].data.val_p = va_arg(ap, void *);
            break;

        default:
            fprintf(stderr, "Unknown type in stack_push()\n");
            exit(EXIT_FAILURE);
    }

    stack->elements[stack->top++].type = type;

    va_end(ap);
}

/*  Pops an element from the stack  */

void stack_pop(struct stack * stack, void * p)
{
    if ( stack->top == 0 ) {
        fprintf(stderr, "Stack empty!\n");
        exit(EXIT_FAILURE);
    }

    switch ( stack->elements[--stack->top].type ) {
        case STACK_CHAR:
            *((char *) p) = stack->elements[stack->top].data.val_c;
            break;

        case STACK_INT:
            *((int *) p) = stack->elements[stack->top].data.val_i;
            break;

        case STACK_LONG:
            *((long *) p) = stack->elements[stack->top].data.val_l;
            break;

        case STACK_FLOAT:
            *((float *) p) = stack->elements[stack->top].data.val_f;
            break;

        case STACK_DOUBLE:
            *((double *) p) = stack->elements[stack->top].data.val_d;
            break;

        case STACK_POINTER:
            *((void **) p) = stack->elements[stack->top].data.val_p;
            break;

        default:
            fprintf(stderr, "Unknown type in stack_pop()\n");
            exit(EXIT_FAILURE);
    }
}

/*  Returns the type of the top element on the stack  */

enum stack_type stack_type_peek(struct stack * stack)
{
    if ( stack->top == 0 ) {
        fprintf(stderr, "Stack empty!\n");
        exit(EXIT_FAILURE);
    }

    return stack->elements[stack->top - 1].type;
}

/*  Returns true if the stack is empty  */

bool stack_is_empty(struct stack * stack) {
    return stack->top == 0;
}

还有一个样本main.c

#include <stdio.h>
#include <stdlib.h>
#include "stack.h"

int main(void)
{
    Stack stk = stack_create(5);

    stack_push(stk, STACK_CHAR, 'x');
    stack_push(stk, STACK_INT, 123);
    stack_push(stk, STACK_FLOAT, 4.56);
    stack_push(stk, STACK_DOUBLE, 7.89);
    stack_push(stk, STACK_POINTER, (void *) &stk);

    while ( !stack_is_empty(stk) ) {
        char c;
        int i;
        float f;
        double d;
        void * p;

        switch ( stack_type_peek(stk) ) {
            case STACK_CHAR:
                stack_pop(stk, &c);
                printf("Popped char '%c' from stack.\n", c);
                break;

            case STACK_INT:
                stack_pop(stk, &i);
                printf("Popped int %d from stack.\n", i);
                break;

            case STACK_FLOAT:
                stack_pop(stk, &f);
                printf("Popped float %f from stack.\n", f);
                break;

            case STACK_DOUBLE:
                stack_pop(stk, &d);
                printf("Popped double %f from stack.\n", d);
                break;

            case STACK_POINTER:
                stack_pop(stk, &p);
                printf("Popped pointer %p from stack.\n", p);
                break;

            default:
                fprintf(stderr, "Unknown type.\n");
                return EXIT_FAILURE;
        }
    }

    stack_destroy(stk);

    return 0;
}

带输出:

paul@thoth:~/src/sandbox/stack_multitype$ ./stack_multitype
Popped pointer 0x7fff401ab528 from stack.
Popped double 7.890000 from stack.
Popped float 4.560000 from stack.
Popped int 123 from stack.
Popped char 'x' from stack.
paul@thoth:~/src/sandbox/stack_multitype$ 

【讨论】:

  • 对于包含不同类型的堆栈(正如 OP 所要求的那样),您将如何在弹出之前检查要弹出的类型?检查stack[top-1]的类型?您的示例代码假定推送类型的顺序是已知的,然后反向回滚,这可能不是真的(例如,我正在考虑 PostScript)。
  • @Jongware:正如我提到的(在编辑中,所以你可能在我这样做之前已经评论过)如果你将类型存储在元素本身中,那么编写一个例程来窥视就很容易了在它。稍等,我会写出来的。
  • @Jongware:已更新。我也更改了 stack_pop() 函数,因为我们在那里使用指针,并且只有 stack_push() 函数实际上需要使用变量参数。
【解决方案2】:

在这种简单的情况下,您可以使用一些宏魔法来伪造模板。例如,您可以将其放入文件poly_stack.h

/* No include guards here! */

#define CONCAT_NAME_R(A, B, C) A ## B ## C
#define CONCAT_NAME(A, T) CONCAT_NAME_R(A, _, T)

struct CONCAT_NAME(stack, VALUE_TYPE)
{
  VALUE_TYPE top;
  VALUE_TYPE items[STACKSIZE];
};

然后使用

#define VALUE_TYPE int
#include poly_stack.h
#undef VALUE_TYPE

要完成这项工作,您需要在头文件中定义所有堆栈操作inline

我不认为这是一个非常优雅的解决方案。当然,您始终可以使用sedawk 等工具来实现外部代码生成。

最后,您可以使用运行时多态性并声明

struct poly_stack
{
  void * top;
  void * items[STACKSIZE];
};

但与“模板化”解决方案相比,这在类型安全性和性能方面都较差。而且使用起来也不太方便。

【讨论】:

    【解决方案3】:

    还有另一种简单的方法,它使用不同堆栈数据结构的联合。这种方法限制较少,易于使用。


    #include <stdio.h>
    
    #define SZ (20)
    
    union stack_s
    {
        struct stack_int
        {
            int top;
            int arr[SZ];
        }istack;
    
        struct stack_char
        {
            char top;
            char arr[SZ];
        }cstack;
    
        struct stack_float
        {
            float top;
            float arr[SZ];
        }fstack;
    };
    
    void main(void)
    {
    union stack_s stack_elm;
    
    stack_elm.istack.arr[0] = 0x1234;
    printf("stack of integers = %x\n",stack_elm.istack.arr[0]);
    
    stack_elm.cstack.arr[0] = 'A';
    printf("stack of chars = %c\n",stack_elm.cstack.arr[0]);
    
    return;
    }
    

    输出如下:

    整数栈 = 1234

    字符堆栈 ​​= A

    【讨论】:

      【解决方案4】:

      编译器在template 场景中所做的是使用代码中使用的数据类型创建该函数的不同签名。

      例如,如果你有一个方法 template&lt;class T&gt; void setValue(T value)

      如果您在代码中使用此方法:

      setValue<float>(3.5f);
      setValue<int>(7);
      

      那么实际上你最终会得到同一个函数的 2 个签名:

      void setValue(float value)
      void setValue(int value)
      

      因此,您可以为每种值类型编写不同的推送和弹出方法。例如:

      void push(int val) {
          //some malloc operations
          items[top].ival = val;
      }
      
      void push(float val) {
          //some malloc operations
          items[top].fval = val;
      }
      

      【讨论】:

      • 他在问如何在 C 中做到这一点,所以需要 C++ 的答案是没有帮助的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-15
      • 1970-01-01
      • 2016-02-04
      • 2016-09-02
      • 1970-01-01
      • 1970-01-01
      • 2013-09-10
      相关资源
      最近更新 更多