CS107-Lecture 6-Note

int型Stack

Lecture 5讲到用C的语法实现“栈”,并给出了StackNew()的实现。

  1. StructNew通过传入结构体s的地址,将某个12字节的内容未知的内存块用来在逻辑上表示一个深度为0的栈:

Jerry: and take it from a 12-byte block of question marks to be logically representing a stack of depth zero.

  1. malloc()动态分配内存过程:计算内存大小、在heap 中找到一个合适的内存块、做标记、返回该块首地址:

Jerry:All that malloc feels is an incoming 16. It goes to heap and finds a figure that’s that big, puts a little halo around it saying it’s in use and then returns the base of it.

  1. 代码段最后的断言:

Jerry:I told you to get in the habit of using assert macro, just to confirm that s->elems != NULL. If malloc fails for some reason, it rarely will fail because it runs out of memory, it’s more likely to fail because you called free on something you shouldn’t have called freed on. So you’ve messed up the whole memory allocator behind the scenes. That’s a very good thing to do right there because it very clearly tells you where there’s a problem, as opposed to it just seg filtering or bus erroring or it crashing in some anonymous way, and you have no idea how to back trace or figure out where the problem started.

Program.1. implementation of StackDispose()

void StackDispose(Stack *s)
{
    free(s->elems); 
    free(s); //no
}

是否应该释放变量s本身占用的内存块?

Jerry: No.
i) Nowhere during StackNew did you allocate space for stack. You assumed that space for the stack has been set aside already, and that the address of it had been identified to the StackNew function. So you don’t even know it was dynamically allocated.
ii) regardless of whether or not the stack, at the moment this thing is called, is of depth zero or of depth 4500, the actual ints that are held inside the dynamically allocated rectangle of bytes, there’s no reason to zero them out. And there’s certainly no freeing needs held by those integers.

StackPush()函数的关键点是the algorithm that’s in place to manage the extension of what’s been allocated to be that much bigger because you’ve saturated what’s already there.

Program.2. implementation of StackPush()

void StackPush(Stack *s, int value)
{
    if(s->logicLen == s->allocateLen) 
    {
        s->allocateLen *= 2;
        s->elems = realloc(s->elems, s->allocateLen*sizeof(int));
        assert(s->elems != NULL);
    }
}

realloc()可以在原先分配的内存块地址基础上直接调整大小,或者重新寻找一块合适大小的内存,然后复制原有的位模式、释放原有分配的内存。如果把realloc()的第一个参数设置为NULL,那么在技术上,它就可以替代malloc()。

line 6, realloc后一定要捕获返回的内存地址,否则会出错;如果realloc失败,会返回NULL,原来的内存块也不会被释放。尽管,in theory,you don’t have to assert. 但在这段代码中,可以通过assert来检查realloc的返回情况,打印一小段error message,比如告诉用户“I actually cannot extend the stack at this time.”

free知道要释放多少内存,系统里有人在登记这些信息,free知道要归还给heap多少内存。

Program.3. implementation of StackPop()

void StackPop(Stack *s)
{
    assert(s->logicLen > 0);
    s->logicLen--;
    return s->elems[s->logicLen];
}

Generic Stack

Program.4. stack.h


typedef struct {
    void *elems;
    int elemSize;
    int loglength;
    int alloclength;
    [??? //preserved. one byte to store something
}stack;

void StackNew(stack *s, int elemSize);
void StackDispose(stack *s);
void StackPush(stack *s, void *elemAddr);
void StackPop(stack *s, void *elemAddr);

Program.5. stack.c

void StackNew(stack *s, int elemSize)
{
    assert(s->elemSize > 0); //it's not a bad thing to put there
    s->elemSize = elemSize;
    s->loglength = 0;
    s->alloclength = 4;
    s->elems = malloc(4*elemSize);
    assert(s->elems != NULL);
}

void StackDispose(stack *s)
{
    //preserved, change soon
    free(s->elems);
}

void StackPush(void *s, void *elemAddr)
{
    if(s->loglength == s->alloclength)
        StackGrow(s); //intention: reallocation
    void *target = (char *)s->elems + s->loglength*s->elemSize; //the hardest part of StackPush()
    memcpy(target, elemAddr, s->elemSize);
    s->loglenght++;  
}

//pop的栈元素地址返回给elemAddr
void StackPop(stack *s, void *elemAddr)
{
    void *source = (char *)s->elems + (s->logiclength-1)*s->elemsize;
    memcpy(elemAddr, source, s->elemSize); 
    //将要pop的位模式放到elemAddr里,或者malloc的一块内存里然后返回void*
    s->loglength--;
}

tip:由于s->elems是void*类型,不能进行指针算数运算,所以先转换为char*;有些人采用unsigned long写法也可以;在32位系统中,char*和unsigned long表示的长度相等,4 bytes, You can do whatever you want.


static

当用static decorating the prototype of a C or a C++ function, not a method in a class just a regular function, such as:

static void StackGrow(stack *s)
{ 
    s->alloclength *= 2;
    s->elems = realloc(s->elems, s->alloclength*s->elemSize);
}

尽管static在C和C++中有至少85种含义, 这里还有一种情况需要解释。技术上,这种含义被称作“内链接”。

The technically explanation is that static marks this function for what’s called internal linkage. You know how you’re generating all these .o files, and when you type make all this stuff appears in your directory, some of them are .o files.

有些函数是global的,有些则是内链接,即不能再其他.o文件中被调用,这也是为了避免大型项目中,多个同名函数导致compiler mess up。

I’ll show you a tool later on where you can actually look at what’s exported and used internally by those .o files, StackPush and StackNew and StackDispose are all marked as global functions, and that the symbols, or the names of those functions should be exported and accessible from other .o files, or made available to other .o files. StackGrow() is called local or internal.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值