资讯专栏INFORMATION COLUMN

C 堆变量,栈变量,指针参数,非指针参数 剖析

xinhaip / 2294人阅读

摘要:栈变量函数中声明的局部变量,只能在函数内部访问,否则访问行为的结果是未定义的。通过上面的剖析我们可以知道是错误的,因为没有操作符作用在上,单纯对赋值操作只会影响到局部的栈变量的值,而不会对函数外的变量有任何影响。

1.基本概念

堆(Heap)
程序可以动态申请的存储空间,通过malloc系列函数分配,全局可访问。

栈(Stack)
这里说的栈不是数据结构中LIFO的栈,而是进程虚拟地址空间的栈;程序在进行函数调用时动态伸缩的存储空间,局限于函数内可以访问。

-堆变量(Heapvariables)
数据存储在堆的变量,全局可访问。

栈变量(Stackvariables)
函数中声明的局部变量,只能在函数内部访问,否则访问行为的结果是未定义的。

指针参数(Pointerparameters)
参数类型为指针的参数。

非指针参数(Non-pointerparameters)
参数类型不是指针的参数。

2.进程的虚拟地址空间

3.相关问题剖析 3.1 如何正确的分配内存

demo代码

void * fun_m1()
{
    char buf[100];
    return (void *)buf;
}

void * fun_m2(size_t size)
{
    return malloc(size);
}

void fun_m3(size_t size, void * p)
{
    p = malloc(size);
}

void fun_m4(size_t size, void ** p)
{
    *p = malloc(size);
}

剖析

fun_m1是错误的,因为它返回的栈变量的地址,如果对它指向的地址进行读写,程序行为的结果是未定义的,程序很可能崩溃,因为此时栈变量的空间已经被回收(栈顶指针改变了)。

fun_m2是正确的,因为它返回的是malloc申请的堆空间的地址。

fun_m3和fum_m4很具有迷惑性,要分区fun_m3和fun_m4的区别,我们这里需要澄清一个概念:任何的参数传递本质上都是值拷贝,任何参数都是栈变量。

在我们以往观念中参数传递就是两种:值传递,指针传递,而通过指针可以改变指针指向的变量。

为什么说参数传递都是值拷贝呢,这是因为不管参数是否为指针,传递的都是一份值的拷贝,只不过当你的参数类型为指针时,你传递的是指针变量的值,而通过*操作符作用在指针变量上,你又刚好可以影响到指针变量关联的其他变量的值。

通过上面的剖析我们可以知道fun_m3是错误的,因为没有*操作符作用在p上,单纯对p赋值操作只会影响到局部的栈变量p的值,而不会对函数fun_m3外的变量有任何影响。fun_m4是正确的,因为有*操作符作用在p上,通过对*p赋值来修改传递给p的参数,使它指向申请的堆空间。

3.2 如何判断堆和栈的“增长”方向(从低到高,还是从高到低)

判断栈的“增长”方向

#include 
#include 

void fun1(int * pb)
{
    int a;
    printf("stack alloc direction[%s]
", &a > pb ? "Up" : "Down");
}

void fun2()
{
    int b;
    fun1(&b);
}

int main()
{
    fun2();
    return 0;
}

编译运行

[root@iZ940zytujjZ test]# gcc -o test10 test10.c
[root@iZ940zytujjZ test]# ./test10
stack alloc direction[Down]
[root@iZ940zytujjZ test]#

从运行结果看,栈的增长方向是“从高到低”(Down)。

判断堆的“增长”方向

#include 
#include 
#include 

int main()
{
    void * a = sbrk(10);    //调整堆顶指针brk
    void * b = sbrk(20);
    printf("heap alloc direction[%s]
", b > a ? "Up" : "Down");
    return 0;
}

编译运行

[root@iZ940zytujjZ test]# gcc -o test11 test11.c
[root@iZ940zytujjZ test]# ./test11
heap alloc direction[Up]
[root@iZ940zytujjZ test]#

从运行结果看,堆的增长方向是“从低到高”(Up)。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/9866.html

相关文章

  • 秋招——语言篇(C++)

    摘要:语言不支持函数重载,编译后的代码只包含函数名。发布程序无需提供静态库,移植方便。全局和静态变量当且仅当对象首次用到时才进行构造。静态全局变量全局作用域文件作用域,无法在其他文件中使用。求数组数组名数据类型。 ...

    LuDongWei 评论0 收藏0
  • GC(@广告位出售)垃圾回收机制: 浅析与理解

    摘要:广告位出售垃圾回收机制浅析与理解对垃圾回收进行分析前,我们先来了解一些基本概念基本概念内存管理内存管理对于编程语言至关重要。里面的变量通常是局部变量函数参数等。 GC(@广告位出售)垃圾回收机制: 浅析与理解 对垃圾回收进行分析前,我们先来了解一些基本概念 基本概念 内存管理:内存管理对于编程语言至关重要。汇编允许你操作所有东西,或者说要求你必须全权处理所有细节更合适。C 语言中虽然...

    songjz 评论0 收藏0
  • GC(@广告位出售)垃圾回收机制: 浅析与理解

    摘要:广告位出售垃圾回收机制浅析与理解对垃圾回收进行分析前,我们先来了解一些基本概念基本概念内存管理内存管理对于编程语言至关重要。里面的变量通常是局部变量函数参数等。 GC(@广告位出售)垃圾回收机制: 浅析与理解 对垃圾回收进行分析前,我们先来了解一些基本概念 基本概念 内存管理:内存管理对于编程语言至关重要。汇编允许你操作所有东西,或者说要求你必须全权处理所有细节更合适。C 语言中虽然...

    xioqua 评论0 收藏0
  • TaintDroid深入剖析之启动篇

    摘要:虽然很多人都用过,甚至大牛们进行过二次开发,但是目前市面上并没有对进行深入剖析文章。栈帧结构的两端由两个指针来指定。分析当从内部的函数或通过反射调用一个时,系统会为之分配一个栈帧,为了方便,后文将这种栈帧统称为栈帧。 ​ 1 背景知识 1.1 Android平台软件动态分析现状 众所周知,在计算机领域中所有的软件分析方法都可以归为静态分析和动态分析两大类,在Android平台也不例外。...

    elliott_hu 评论0 收藏0
  • C/C++

    摘要:另外栈内存出了作用域就会自动释放掉,所以不需要手动去回收的。,其中指针变量的声明有如下三种形式其中第一种是被推荐的写法。数据类型 C语言中的基本数据类型,对于它分为两种: 1、signed 有符号的类型,也就是支持正负号的。 2、unsigned 无符号的类型,也就是没有负号,取值从0开始。 有符号和无符号的数据类型有啥区别呢?其实就是取值范围不一样,下面看一张对照表: showImg(ht...

    microcosm1994 评论0 收藏0

发表评论

0条评论

xinhaip

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<