什么时候会重新分配堆栈内存?

时间:2018-03-30 02:07:16

标签: c pointers stack-memory

有以下代码:

int fun(){

    char* pc = NULL;

    {
        char ac[100] = "addjfidsfsfds";
        pc = ac;
    }

    ...

    pc[0] = 'd';
    printf("%s\n", pc);
    ...
}

那么,在pc的范围结束后,我可以安全地使用ac吗?因为我不确定编译器是否会为其他用途重新分配为ac分配的堆栈内存。

4 个答案:

答案 0 :(得分:8)

理解的缺陷与对象的存储持续时间有关。除非您使用线程,否则您需要关注三种类型,静态,自动和已分配。通过在块中声明char ac[100] = "addjfidsfsfds";并且没有static存储类说明符,存储持续时间为自动,并且当生成器的执行结束时,其生命周期结束。之后尝试访问该值的是未定义行为

C-Standard在第6.2.4节中详细说明了这一点,例如

  

C11 - 6.2.4 Storage durations of objects

     

1对象具有确定其生命周期的存储持续时间。那里   有四个存储持续时间:静态,线程,自动和   分配。分配的存储在中描述   7.22.3

     

2 对象的生命周期是程序执行的一部分   保证为其保留存储空间。一个对象   存在,具有常量地址,33)并保留其最后存储的值   在其整个生命周期中.34)如果一个物体被引用到它的外面   生命周期,行为未定义。指针的值   当它指向的对象(或刚刚过去)变得不确定   到了生命的尽头。

     

3声明标识符的对象没有存储类   说明符_Thread_local,以及外部或内部链接   或者使用存储类说明符static,具有静态存储   持续时间。它的生命周期是整个程序的执行及其   在程序启动之前,存储的值只初始化一次。

     

(省略_Thread_local详细信息)

     

5声明标识符没有链接且没有链接的对象   存储类说明符static具有自动存储持续时间,如   做一些复合文字。试图间接的结果   从其他线程访问具有自动存储持续时间的对象   而不是与对象相关联的那个   实现定义的。

     

6 对于没有可变长度数组的此类对象   类型,它的生命周期从进入延伸到它所在的块   关联,直到该块的执行以任何方式结束。 (进入   封闭的块或调用函数暂停但不结束,   执行当前块。)如果以递归方式输入块,   每次都会创建一个新的对象实例。初始值   对象是不确定的。如果指定了初始化   对象,每次声明或复合时执行   在块的执行中达到了字面值;否则,价值   每次达到声明时变得不确定。

     

7对于具有可变长度数组类型的对象,   它的寿命从对象的声明延伸到   程序的执行离开了声明的范围.35)如果   范围是递归输入的,对象的新实例是   每次都创造。对象的初始值是不确定的。

如果对是否允许访问值存在疑问,请参阅标准。

答案 1 :(得分:5)

来自C标准#6.2.1p4 [强调我的]

  

每个其他标识符的范围由其声明的位置(在声明符或类型说明符中)确定。如果声明标识符的声明符或类型说明符出现在任何块或参数列表之外,则标识符具有文件范围,该范围终止于转换单元的末尾。 如果声明标识符的声明符或类型说明符出现在块内或函数定义中的参数声明列表中,则标识符具有块作用域,该作用域终止于关联块的末尾。。 .....

变量private static final int CAMERA_REQUEST_CODE = 1; private static final int SELECT_FILE = 0; public PersonalDataStep(){ } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.personal_data_step, container, false); ButterKnife.bind(this, view); //imageEktp.setClickable(true); getPhoto.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //Log.d("test : ", "testing"); CharSequence menu[] = new CharSequence[]{"Take From Galery", "Open Camera"}; AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setTitle("Pick a Picture"); builder.setItems(menu, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { if(i == 0){ Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); intent.setType("image/*"); startActivityForResult(intent, SELECT_FILE); }else if(i == 1){ Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(intent, CAMERA_REQUEST_CODE); } } }); builder.show(); } }); return view; } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(resultCode== Activity.RESULT_OK) { if (resultCode == CAMERA_REQUEST_CODE) { Bundle bundle = data.getExtras(); final Bitmap bitmap = (Bitmap) bundle.get("data"); imageEktp.setImageBitmap(bitmap); } else if (resultCode == SELECT_FILE) { Uri selectedImage = data.getData(); imageEktp.setImageURI(selectedImage); Log.d("image from galery : ", selectedImage.toString()); } }else{ Toast.makeText(getActivity(), "You haven't picked Image",Toast.LENGTH_LONG).show(); } } 是一个本地(自动)非静态变量,其生命周期仅限于其范围,即声明它的块。任何在其生命周期之外访问它的企图都会导致未定义的行为。

来自C标准#6.2.4p2 [强调我的]

  

对象的生命周期是程序执行的一部分,在此期间保证为其保留存储。一个对象存在,具有一个常量地址,33)并在其整个生命周期内保留其最后存储的值.34)如果一个对象在其生命周期之外被引用,则该行为是未定义的。当指针指向(或刚刚过去)的对象到达其生命周期的末尾时,指针的值变得不确定。

答案 2 :(得分:3)

在示例中,在内部范围之外使用pc是不安全的。因为acpc = ac之后被销毁,所以你有一个经典的悬空指针。启用了足够的警告后,我认为大多数编译器都会发出警告。

可能似乎适用于您的系统,但这并不意味着它是安全的。有些编译器可能会选择一次性回收所有原语作为优化,但它们不需要这样做。

答案 3 :(得分:2)

可能在块结束后(当它超出范围时)重新分配。

是否 重新分配会有所不同,具体取决于程序以及运行它的机器(以及编译器)的体系结构。

当你声明另一个局部变量(在堆栈上)时,可能被重新分配;和/或,当你进行函数调用时(将参数值和返回地址压入堆栈,并让被调用函数声明新变量)。

理论上可能它可以在伪随机时间,通过硬件中断或类似方式重新分配。在某些实现中,中断服务处理程序(可能在任何时间运行)通过将当前机器状态保存在堆栈上来启动。