【问题标题】:Generic stack in C using void pointer not working for stringsC中使用void指针的通用堆栈不适用于字符串
【发布时间】:2013-11-06 01:34:12
【问题描述】:

我正在尝试使用 void 指针在 C 中实现一个通用堆栈。这没什么大不了的,只是为了好玩和学习。它按预期使用 int 和 float 。但我面临的问题是char *,即字符串。它不是复制字符串的地址,而是尝试将实际字符串复制到 4 个字节(因为在我的系统指针大小为 4 个字节)。

如果可能的话,如何告诉 C 复制字符串的地址而不是实际的字符串,而不会破坏已经工作的 int 和 float 的功能?

到目前为止我的实现如下,


typedef struct{
        int top;
        void *data;
        int capacity;
        size_t ele_size;
}stack_t;

int stack_init(stack_t *s, int capacity, size_t ele_size)
{
        /*  Initializes the stack with the given capacity
         *  @param s: Pointer to stack_t type variable
         *  @param capacity: capacity of the stack to be created
         *  Returns : Zero if succesful in allocating memory to the stack,
         *              -1 Otherwise
         */
        s->top = -1;
        s->capacity = capacity;
        s->ele_size = ele_size;
        s->data = calloc(s->capacity, s->ele_size);
        if (s-> data != NULL || s->capacity == 0) {
                return 0;
        } else {
                return -1;
        }
}

int stack_push(stack_t *s, void *x)
{
        /*  Pushes an element on to the stack
         *  @param s: Pointer to stack_t type variable
         *  @param x: Value to Push on to the stack
         *  Returns : Zero if stack is not full when stack_push() is called,
         *              -1 Otherwise
         */
        if (stack_len(s) capacity) {
                s->top++;
                memcpy(s->data + s->ele_size * s->top, x, s->ele_size);
                return 0;
        } else {
                return -1;
        }
}

int stack_pop(stack_t *s, void *value)
{
        /*  Value that is popped from the stack is placed in value parameter,
         *  @param s: Pointer to stack_t type variable
         *  @param x: Pointer to a variable to store the value popped from the 
                        stack
         *  Returns:  Zero if stack is not empty when stack_pop() is called,
         *              -1 Otherwise
         */
        if (stack_len(s) > 0) {
                memcpy(value, s->data + s->ele_size * s->top, s->ele_size);
                s->top--;
                return 0;
        } else {
                return -1;
        }
}

栈的完整实现请参考here

上述栈的使用如下: 实际上有很多不相关的东西,比如使用伪随机数生成器 将随机数插入堆栈。



#include"../src/stack.h"

START_TEST(int_push_pop)
{
        stack_t s;
        int n = random() % 60267;
        int *a = calloc(n, sizeof (int));
        ck_assert_int_eq(stack_init(&s, n, sizeof (int)), 0);
        int i;
        for (i = 0; i = 0; i--) {
                int value;
                int x = stack_pop(&s, &value);
                ck_assert_int_eq(x, 0);
                ck_assert_int_eq(value, a[i]);
                x = stack_len(&s);
                ck_assert_int_eq(x, i);
        }
        stack_clear(&s);
        stack_destroy(&s);
}

END_TEST

START_TEST(float_push_pop)
{
/* similar to int_push_pop, so ignored here. */
}

END_TEST


START_TEST(string_push_pop)
{
        stack_t s;

        char *str = "stack overflow";
        stack_push(&s, str);
        char **popval = malloc(sizeof(char *));
        stack_pop(&s, popval);
        printf("%s\n", popval);

        stack_destroy(&s);

}

END_TEST


Suite* stack_suite()
{
        Suite *s = suite_create("Stack");

        TCase *tc_int = tcase_create("int");
        /* Stack int data type Test Case*/

        TCase *tc_float = tcase_create("float");
        /* Stack float data type Test Case*/

        tcase_add_test(tc_int, int_push_pop);
        tcase_add_test(tc_float, float_push_pop);

        suite_add_tcase(s, tc_int);
        suite_add_tcase(s, tc_float);

        return s;
}


int main()
{
        int number_failed;
        Suite *s = stack_suite();
        SRunner *sr = srunner_create(s);
        srunner_run_all(sr, CK_NORMAL);
        number_failed = srunner_ntests_failed(sr);
        srunner_free(sr);

        return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

【问题讨论】:

  • 离题:恭喜您记录了您的 API!我可以建议您交换那些成功/失败返回值0-1?如果您返回 0 表示失败并返回 -1(或任何其他非零值)表示成功,您可以进行更直观的测试:!stack_init(…) 将表示失败而不是成功(将 ! 读出为“不是”)。
  • @stakx:我也是这么想的,但是以后如果我想返回特定的错误代码而不是错误的零,可以使用不同的非零值。比如 1 表示内存不足,2 表示无效操作,-1 表示任何其他错误等。零表示成功。
  • 很公平! (只要您为这些错误值定义常量或#defines... :)
  • 这里贴的代码和github上的代码有区别。您可以在此处编辑代码以反映正确的代码吗?特别是 stack_push、stack_pop 在您的 github 代码中采用 void 指针。
  • 我也不确定你是如何调用函数的,这应该会有所启发。如果要复制字符串的指针,则需要将 char** 作为参数传递给 stack_push 函数。(对于其他 api 也是如此),例如stack_push(s,&str)

标签: c stack void-pointers generic-programming


【解决方案1】:

由于stack_push()stack_pop() 函数采用void* 指针,因此您需要将指针传递给需要推送的字符数组(字符串),而不是字符数组本身。例如如果您将字符串声明为

char str[] = "hello world";

你必须调用函数

stack_push(s,&str);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-27
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多