스 택 에 분 배 된 배열 (문자열) 을 되 돌려 주지 못 하 는 이 유 를 철저히 알 아 보 세 요.

배경
최근 에 튜 토리 얼 을 준 비 했 습 니 다. 사례 를 준비 하 는 과정 에서 다음 과 같은 코드 조각 을 준비 하여 해석 http scheme 을 보 여 주 었 습 니 다.
#include 
#include 
#include 

char *parse_scheme(const char *url)
{
    char *p = strstr(url,"://");
    return strndup(url,p-url);
}

int main()
{
    const char *url = "http://static.mengkang.net/upload/image/2019/0907/1567834464450406.png";
    char *scheme = parse_scheme(url);
    printf("%s
",scheme); free(scheme); return 0; }

위 는 strndup 방식 을 통 해 뒤에 도 의존 malloc 했 기 때문에 마지막 에 도 필요 하 다 free.위 챗 그룹 에서 의 개인 편지 parse_schemechar [] 로 되 돌려 줄 수 있 습 니까?스 택 에 있 는 배열 도 문자열 을 저장 할 수 있다 는 것 을 알 고 있 습 니 다. 다음 과 같이 바 꿀 수 있 습 니까?
char *parse_scheme(const char *url)
{
    char *p = strstr(url,"://");
    long l = p - url + 1;
    char scheme[l];
    strncpy(scheme, url, l-1);
    return scheme;
}

대부분의 사람들 은 이렇게 쓰 면 안 된다 는 것 을 알 고 있 습 니 다. 스 택 의 주 소 를 되 돌려 주 었 기 때문에 이 함수 에서 돌아 온 후에 그 스 택 공간의 조작 권 도 풀 렸 습 니 다. 이 주 소 를 다시 사용 할 때 값 은 확실 하지 않 습 니 다.
그럼 우 리 는 오늘 이런 상황 이 발생 한 배후 의 진정한 원 리 를 함께 토론 합 시다.
기초 준비
모든 함수 가 실 행 될 때 함수 파라미터 와 부분 변수 등 을 저장 하기 위해 메모리 가 필요 하기 때문에 모든 함수 에 연속 적 인 메모 리 를 분배 해 야 합 니 다. 이 메모 리 는 함수 의 스 택 프레임 (Stack Frame) 이 라 고 합 니 다.연속 적 인 메모리 주소 이기 때문에 프레임 이 라 고 합 니 다.왜 하나 더 넣 으 라 고 했 어 요?모두 함수 호출 스 택 에 익숙 해 졌 을 것 입 니 다. 왜 함수 호출 스 택 이 라 고 부 릅 니까?다음 표현 식
array_values(explode(",",file_get_contents(...)));

함수 의 실행 순 서 는 가장 안쪽 함수 가 가장 먼저 실 행 된 다음 에 순서대로 바깥쪽 함 수 를 실행 하 는 것 입 니 다.그래서 함수 의 집행 은 스 택 의 데이터 구 조 를 이용 하여 스 택 프레임 이 라 고 합 니 다.
x86_64 cpu 의   레지스터 저장 함수 창고 밑 주소, rbp 레지스터 저장 함수 스 택 상단 주소.
실험 하 다.
#include 

void foo(void)
{
    int i;
    printf("%d
", i); i = 666; } int main(void) { foo(); foo(); return 0; }
$gcc -g 2.c

$./a.out
0
666

왜 두 번 째 호출 rsp 함수 출력 결 과 는 모두 지난번 함수 호출 의 할당 입 니까?어 셈 블 리 후의 코드 를 먼저 보 세 요.
000000000040052d :
#include 

void foo(void)
{
  40052d:    55                       push   %rbp
  40052e:    48 89 e5                 mov    %rsp,%rbp
  400531:    48 83 ec 10              sub    $0x10,%rsp
    int i;
    printf("%d
", i); 400535: 8b 45 fc mov -0x4(%rbp),%eax 400538: 89 c6 mov %eax,%esi 40053a: bf 00 06 40 00 mov $0x400600,%edi 40053f: b8 00 00 00 00 mov $0x0,%eax 400544: e8 c7 fe ff ff callq 400410 i = 666; 400549: c7 45 fc 9a 02 00 00 movl $0x29a,-0x4(%rbp) } 400550: c9 leaveq 400551: c3 retq 0000000000400552
: int main(void) { 400552: 55 push %rbp 400553: 48 89 e5 mov %rsp,%rbp foo(); 400556: e8 d2 ff ff ff callq 40052d foo(); 40055b: e8 cd ff ff ff callq 40052d return 0; 400560: b8 00 00 00 00 mov $0x0,%eax } 400565: 5d pop %rbp 400566: c3 retq 400567: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) 40056e: 00 00

이론 분석
처음  foo 함수 전후foo 함수 에 들 어가 기 전에 foo 에 매개 변수 도 없고 부분 변수 도 없 기 때문에 main 의 스 택 프레임 의 길 이 는 0 이 고 mainrbp 가 같다 rsp.집행 할 때
callq  40052d 
0x7fffffffe2c0 함 수 를 호출 main 한 후에 실 행 된 다음 줄 코드 의 주 소 를 압축 합 니 다. 64 비트 기계 이 고 주소 8 바이트 이기 때 문 입 니 다.입장 foo
push   %rbp
foo 의 값 을 창고 에 저장 합 니 다. 저 장 된 주소 이기 때문에 8 바이트 를 차지 하기 때문에 함수 rbp 를 초기 화 할 때 foo
mov    %rsp,%rbp
rbp 는 이미 원래 의 기초 위 에 rsp 바이트 가 붙 었 기 때문에 16 에서 0x7fffffffe2c0 로 바 뀌 었 다.
sub    $0x10,%rsp
0x7fffffffe2b0 함수 안의 부분 변 수 는 컴 파일 할 때 foo 바이트 가 예약 되 어 있 기 때문에 16rsp 로 바 뀌 어 마지막 에 실 행 됩 니 다.
movl   $0x29a,-0x4(%rbp)
0x7fffffffe2a0666 에 두 었 습 니 다. 두 번 째 호출 시 인쇄 0x7fffffffe2ac 의 어 셈 블 리 코드 는 다음 과 같 습 니 다.
    printf("%d
", i); 400535: 8b 45 fc mov -0x4(%rbp),%eax 400538: 89 c6 mov %eax,%esi 40053a: bf 00 06 40 00 mov $0x400600,%edi 40053f: b8 00 00 00 00 mov $0x0,%eax 400544: e8 c7 fe ff ff callq 400410

이차 진입  i 함수 전후
지난번 foo 에 저 장 된 -0x4(%rbp) 과 두 번 째 호출 666foo 값 이 처음 과 같 기 때문에 하나의 주소 입 니 다.그래서 rbp 인쇄 되 었 습 니 다.
주제 로 돌아가다
#include 
#include 
#include 

char *parse_scheme(const char *url)
{
    char *p = strstr(url,"://");
    long l = p - url + 1;
    char scheme[l];
    strncpy(scheme, url, l-1);
    printf("%s
",scheme); return scheme; } int main() { const char *url = "http://static.mengkang.net/upload/image/2019/0907/1567834464450406.png"; char *scheme = parse_scheme(url); printf("%s
",scheme); return 0; }

디 버 깅 정 보 는 다음 과 같 습 니 다. 666 에서 돌아 올 때 인쇄 parse_scheme 의 결 과 는 scheme 입 니 다. 그러나 우리 가 호출 http 한 후에 상기 사례 와 마찬가지 로 printf 스 택 에서 나 오고 parse_scheme 스 택 에 들 어가 면 스 택 에 메모리 가 다시 교체 되 기 때문에 인쇄 된 결 과 는 반드시 printf 가 아 닙 니 다.
주 몽 강
원문 을 읽다
본 고 는 운 서 지역사회 의 오리지널 내용 으로 허락 없 이 전재 할 수 없다.

좋은 웹페이지 즐겨찾기