programing

C의 스택 변수 자동 해제

powerit 2023. 10. 10. 21:13
반응형

C의 스택 변수 자동 해제

안타깝게도 C에는 똑똑한 포인터가 없습니다.그러나 변수 선언을 랩핑하고 변수가 선언된 범위를 떠날 때 해당 변수를 입력 변수로 함수 호출을 호출하는 매크로를 구축할 수 있습니까?

긴 문구로 죄송합니다만 참조 카운터가 내장된 많은 요소가 있는 xnu 커널에서 작업하고 있으며, 메모리 누수를 방지하기 위해 이 요소를 사용한 후 이 요소를 해제하는 것을 잊지 말아야 합니다.

예를 들어, 만약 내가 다음과 같은 유형을 가지고 있다면,proc_t:

struct proc;
typedef struct proc * proc_t;

범위 내에서 이 유형을 기준으로 스택 변수를 선언합니다. 예를 들어:

{
    proc_t_release_upon_exit proc_t proc_iter = proc_find(mypid);
    //the rest of the code in this scope 
}

전처리기가 매크로를 분석한 후 컴파일하기 전에 다음 코드가 생성될 것으로 예상됩니다.

{ 
    proc_t myproc = proc_find(mypid)
    //the rest of the code in scope
    proc_rele(myproc);
}

C와 같은 매크로를 정의할 수 있는 방법이 있습니까?

GCC에서 Cleanup variable 속성을 사용할 수 있습니다.이것을 보세요: http://echorand.me/site/notes/articles/c_cleanup/cleanup_attribute_c.html

샘플 코드:

#include <stdio.h>
#include <stdlib.h>

void free_memory(void **ptr)
{
    printf("Free memory: %p\n", *ptr);
    free(*ptr);
}

int main(void)
{
    // Define variable and allocate 1 byte, the memory will be free at
    // the end of the scope by the free_memory function. The free_memory 
    // function will get the pointer to the variable *ptr (double pointer
    // **ptr).
    void *ptr  __attribute__ ((__cleanup__(free_memory))) = malloc(1);
    return 0;
}

소스 코드를 main.c라는 이름의 파일에 저장하면 다음 명령으로 컴파일할 수 있습니다.

gcc main.c -o main

메모리 누수가 있는지 확인합니다.

valgrind ./main

valgrind에서의 출력 예제:

==1026== Memcheck, a memory error detector
==1026== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==1026== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==1026== Command: ./main
==1026== 
Free memory: 0x51ff040
==1026== 
==1026== HEAP SUMMARY:
==1026==     in use at exit: 0 bytes in 0 blocks
==1026==   total heap usage: 1 allocs, 1 frees, 1 bytes allocated
==1026== 
==1026== All heap blocks were freed -- no leaks are possible
==1026== 
==1026== For counts of detected and suppressed errors, rerun with: -v
==1026== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

C는 먼저 실행될 다른 코드보다 구문적으로 코드를 먼저 배치하는 방법을 제공합니다.for블록. a의 3항을 기억하세요.forstructure는 임의의 식을 포함할 수 있으며, 주 블록을 실행한 후 항상 실행됩니다.

따라서 다음 코드의 주어진 청크 후에 미리 결정된 호출을 하는 매크로를 만들 수 있습니다.for매크로의 블럭:

#define M_GEN_DONE_FLAG() _done_ ## __LINE__ 

#define M_AROUND_BLOCK2(FLAG, DECL, BEFORE, AFTER) \
  for (int FLAG = (BEFORE, 0); !FLAG; ) \
    for (DECL; !FLAG; FLAG = (AFTER, 1))

#define M_AROUND_BLOCK(DECL, BEFORE, AFTER) M_AROUND_BLOCK2(M_GEN_DONE_FLAG(), DECL, BEFORE, AFTER)

#define M_CLEANUP_VAR(DECL, CLEANUP_CALL) M_AROUND_BLOCK(DECL, (void)0, CLEANUP_CALL)

...이렇게 사용할 수 있습니다.

#include <stdio.h>

struct proc;
typedef struct proc * proc_t;

proc_t proc_find(int);
void proc_rele(proc_t);

void fun(int mypid) {
  M_CLEANUP_VAR (proc_t myproc = proc_find(mypid), proc_rele(myproc))
  {
    printf("%p\n", &myproc); // just to prove it's in scope
  }
}

여기서의 비결은.for블록은 다음 문장을 받아들이지만 실제로 매크로 정의에 이 문장을 넣지 않으면 일반 코드 블록으로 매크로 호출을 수행할 수 있으며 확장된 문장을 따르는 것만으로 새로운 범위-제어-구조 구문에 "마법적으로" 속하게 됩니다.for.

사용 가치가 있는 옵티마이저는 가장 낮은 최적화 설정에서 루프 플래그를 제거합니다.이름이 깃발과 충돌하는 것은 큰 걱정이 아니라는 것을 유의하십시오(즉, 당신은 정말로 필요하지 않습니다).gensym이 경우) 플래그는 루프 본체의 범위를 가지며, 중첩 루프는 동일한 플래그 이름을 사용하는 경우 안전하게 숨길 수 있기 때문입니다.

여기서 이점은 정리할 변수의 범위가 제한되고(해당 선언 직후에 화합물 외부에서 사용할 수 없음) 시각적으로 명확하다는 것입니다(해당 화합물 때문에).

장점:

  • 이것은 확장이 없는 표준 C입니다.
  • 제어 흐름이 간단합니다.
  • () ()합니다.__attribute__ __cleanup__

단점:

  • "" RAI 를(, )로부터 ).goto는 C++외:__cleanup__일반적으로 후드 아래에 C++ 기계로 구현되므로 더욱 완성도가 높습니다.)더 것은 은인입니다를 것입니다.return(고맙습니다 @Voo.)(적어도 위치가 잘못되지 않도록 보호할 수 있습니다.break- 만약 당신이 원한다면 - 세번째 줄을 추가함으로써,switch (0) default:M_AROUND_BLOCK2.)
  • 모든 사람이 구문 확장 매크로에 동의하는 것은 아닙니다. (그러나 여기서 C의 의미론을 확장하는 을 고려하면...)

당신이 듣고 싶어 하는 말이 아니라는 것을 알지만, 나는 당신이 이것을 하지 않기를 권합니다.

모든 것이 정리되기 전에 단일 반환점을 갖는 것은 완벽하게 허용되는 C 스타일입니다.예외가 없기 때문에 이것은 하기 쉽고, 기능을 보고 확인하기 쉽습니다.

이 작업을 위해 매크로 해커리 또는 컴파일러 "기능"을 사용하는 것은 C 스타일로 허용되지 않습니다.당신이 읽고 이해하는 것은 모든 사람들에게 부담이 될 것입니다.그리고 결국에는 큰 이득을 얻지 못합니다.

언급URL : https://stackoverflow.com/questions/44819409/auto-release-of-stack-variables-in-c

반응형