【问题标题】:C - Changing the value of a variable outside of a function by passing pointersC - 通过传递指针更改函数外部变量的值
【发布时间】:2013-09-11 12:44:14
【问题描述】:

我在 C 中有一个任务来实现一个抽象数据类型 STACK。数据类型的性质需要需要分配内存的键结构。我的问题是,我的导师现在坚持让初始化函数接收一个指向键结构的指针。 init() 函数只会分配结构所需的内存并将字段设置为零,但需要为传入的指针分配该内存位置。

如果不让函数返回一个指针,或者传入一个 2 星指针,我想不出一种方法——这两种方法都是不允许的。我知道函数原型必须是(其中 stackT* 是指向关键堆栈数据结构的指针):

    void init(stackT* stack);

我想出了这个并且效果很好:

    void init(stackT** stack){
        *stack = (stackT*) malloc(sizeof(stack));
        (*stack)->count = 0;
        return;
    }

但它不遵守赋值的限制。


tl;dr 版本:

基本上,我怎样才能将指向堆栈数据结构 (&stackPtr) 的原始指针的地址传递到一个以一星指针作为参数的函数中,并且不会收到指针类型的警告?此外,一旦您将参数更改为(stackT* stack),即使我以任何一种方式传递相同的东西,下面的代码也不起作用 - 这就是我的问题所在。

我认为如果您打算传递指向指针的指针,则需要将参数作为 2 星指针。当您取消引用指针时,编译器必须知道它在处理什么。

无论如何,鉴于限制,我不确定如何执行此操作。在我看来,这只会让它变得不必要地更加困难。

【问题讨论】:

  • 向您的讲师询问为什么带有指向指针的解决方案不适合。而你的init(你或许应该称之为init_stack)可能会返回一个指针......
  • 嗯,函数原型已经提供了——只剩下实现了。我看不到如何使用提供的版本来做到这一点。我同意你的看法——从 init 函数返回一个指针也很简单!但是不,我必须使用上面的函数原型提供实现 - 不知何故。
  • 您可能会在 typedef struct mystack_st stackT 并让您的 struct mystack_st 结构包含一个指针字段(可能还有一个长度和计数字段)。
  • 不确定你的意思。结构中的指针字段会做什么?
  • 它将指向一些包含堆栈内元素的calloc-ed 数组。

标签: c pointers structure


【解决方案1】:

我相信,正如评论中所指出的那样,您错过了意图。

我认为这个想法是“根”stackT 实例应该是一个众所周知的结构,以便您可以在本地声明一个。然后调用init() 来设置stackT 实例描述的实际堆栈:

int push_four(void)
{
  stackT my_stack;

  init(&my_stack);
  push(&my_stack, 1);
  push(&my_stack, 2);
  push(&my_stack, 3);
  push(&my_stack, 4);
}

上面假设堆栈存储整数,即init()内部的分配应该是这样的:

void init(stackT *stack)
{
  stack->items = malloc(64 * sizeof *items);
  stack->count = 0;
}

而这反过来又假设如下声明:

typedef struct {
  int *items;
  size_t count;
} stackT;

当然,默认的最大深度(64)应该是init()的参数,你必须检查(但不能强制转换!)malloc()的返回值,等等。

【讨论】:

  • 我实际上有一个root 结构,上面显示为stackT。这个结构内部是一个整数(计数),用于跟踪堆栈中有多少项目,以及指向其他结构的指针数组,这些结构稍后将被push-ed 到堆栈上。我遇到的问题是如何通过只传入stackT 类型的指针来初始化root 堆栈,而不使用我上面提出的2 星实现——该解决方案显然需要1 星。
  • 另外,我知道 malloc() 返回 void 指针 void * 的强制转换不是必需的,但为了便于阅读,我将其包括在内。但是,你还有其他理由说不投回报吗?
【解决方案2】:

通常,当您有复杂的结构时,会有一个控件struct,并且该控件将有一个指向实际内存的指针。

例子:

struct stack_control_s {
    void * memory;
    size_t memory_size;
    size_t current_size;
};

然后你将一个指向控制结构的指针传递给你的初始化器并让它做真正的工作;

#define STACK_MIN_SIZE 0x100
int stack_init(struct stack_control_s * stack) {
    memset(stack, 0, sizeof(*stack));

    stack->memory = calloc(STACK_MIN_SIZE, 1);

    if (!stack->memory)
        return -1; //error

    stack->memory_size = STACK_MIN_SIZE;

    return 0; // all good
}

这是我曾经制作的通用 C 列表的稍微修改的标题。我添加了宏以使其可用作堆栈。也许这会给你一些启发:

list_t.h

用途:

list_t(char) mylist;
list_init(&mylist);

list_push(&mylist, 'A');

printf("%c\n", list_pop(&mylist));

【讨论】:

  • 在我的(现在可能是基本的)实现中,我有一个root 结构类型定义为stackT,其中包含指向其他结构的指针数组,这些结构稍后将push-ed 到stack 和一个 count 字段来跟踪当前有多少项目在堆栈上。
  • 当然可以。我只是想知道一般情况下如何做到这一点,但我会的。
【解决方案3】:

这可能不是最佳解决方案,但您可以全局定义堆栈。

在这种情况下,它看起来像:

stackT G_stack;
....

void init(stackT* stack){
    stack->count = 0;
    return;
}

int main() {
    .....
    init(&G_stack);
    .....
}

在这种情况下,您不需要更改原型。

【讨论】:

  • 谢谢。我试图避免这样的事情,但我们会看到的。
  • @sherrellbc 我理解你。
【解决方案4】:

这会为指针分配堆栈定义的地址并传递要初始化的指针(使用单个 * :)... 这对您有用吗?

#include <ansi_c.h>  
typedef struct  {
    int count;
} COUNT;

typedef struct  {
    COUNT count;
    int *element1;
    int *element2;
    int address;
} STACK;

STACK stack, *pStack;

void InitStack(STACK *iS);

int main(void)
{    //This is how I think you will meet the 
     //criteria you are talking about (single *)
    pStack = &stack; //assigning address of stack to pointer

    InitStack(pStack);
    //pStack->address == pStack
    return 0;
}

void InitStack(STACK *iS)
{
    iS->count.count = 0;
    iS->address = (int)iS; //assigning address of stack to member of struct
    iS->element1 = calloc(10, sizeof(int));
    iS->element2 = calloc(10, sizeof(int));
}

【讨论】:

  • 这个解决方案在技术上是可行的,但是在这种情况下stack的创建是静态分配的,而堆栈中的组件是动态分配的。此外,堆栈和指针是全局声明的,我试图让命名空间保持整洁。无论如何,我喜欢这个解决方案:+1 谢谢。
  • 嗯。感谢您的 +1,但我仍在尝试了解我的断开连接。 init 函数接受一个指针,它为结构分配必要的内存(即成员 element1element2),它将成员 count 设置为零,并分配成员 address 以包含结构位置的地址。至于为整个结构的副本动态分配内存,你能不能做类似的事情: S_DEVICE *pI, i;那么 pI = &i;那么 pI = malloc(sizeof(S_DEVICE)+(20*sizeof(int)); ?让我知道我在哪里漏掉了重点。谢谢!
  • 另外,这里有一个 link(请看下面的答案部分),它详细介绍了创建、分配和释放复合体结构。我在尝试弄清楚如何初始化 gSoap 结构时这样做了,但其中一些可能也适用于您正在做的事情。
  • 好吧,上面的解决方案为堆栈静态分配内存并分配一个指向该静态内存块的指针。我基本上需要这样做,但是在函数内部进行动态内存分配并将内存块地址分配给传入的指针。按照上面的操作方式,当静态分配的堆栈地址时,地址已经在 main() 中分配分配给指针pStack。 init() 函数应该只为堆栈结构本身分配内存,而不是为它里面的元素分配内存——至少在我现在必须做的实现中,
  • 续。我有另一个函数push(),它将为需要push-ed 的项目分配所需的空间到堆栈的顶部。我不确定堆栈数据类型如何使用它更复杂的形式,但现在我必须这样做,因为通过调用 calloc() 并仅向堆栈添加单个项目是浪费空间。跨度>
猜你喜欢
  • 2011-07-12
  • 1970-01-01
  • 1970-01-01
  • 2019-02-11
  • 2021-08-09
  • 2021-03-03
  • 2018-08-19
  • 2020-10-20
相关资源
最近更新 更多