문자 배열/문자열은 바이너리 파일에 어떻게 저장됩니까?
다른 컴파일러를 사용하여 이 코드를 컴파일하고 16진수 편집기에서 출력을 검사할 때 "낸시" 문자열을 어딘가에서 찾을 것으로 예상됩니다.
#include <stdio.h>
int main()
{
char temp[6] = "Nancy";
printf("%s", temp);
return 0;
}
다음에 대한 출력 파일
gcc -o main main.c
다음과 같이 표시됩니다.에 대한 출력
g++ -o main main.c
"낸시"를 찾을 수가 없어요.비주얼 스튜디오(MSVC 1929)에서 동일한 코드를 컴파일하면 16진수 편집기에 전체 문자열이 표시됩니다.
(1)의 문자열 중간에 랜덤 바이트가 있는 이유는 무엇입니까?
컴파일러가 생성하는 출력 파일에 데이터를 저장하는 방법에 대한 단일 규칙은 없습니다.
데이터는 "일정한" 섹션에 저장할 수 있습니다.
데이터는 명령어의 "즉시" 피연산자에 내장될 수 있으며, 여기서 데이터는 명령어를 인코딩하는 비트의 다양한 필드에 인코딩됩니다.
컴파일러에서 생성된 명령어를 사용하여 다른 데이터에서 데이터를 계산할 수 있습니다.
한 곳에서 "Nanc"를 보고 다른 곳에서 "y"를 보는 경우는 "Nanc"를 형성하는 바이트를 즉시 피연산자로 로드하는 로드 명령("mov"로 작성될 수 있음)과 뒤에 오는 널 문자로 "y"를 형성하는 바이트를 로드하는 다른 로드 명령을 사용하는 컴파일러일 것입니다.로드된 데이터를 스택에 저장하고 주소를 전달하는 다른 지침과 함께printf
.
진단하기에 충분한 정보를 제공하지 않았습니다.g++
case: 컴파일러 또는 해당 버전 번호의 이름을 지정하지 않았거나 생성된 출력의 일부를 제공하지 않았습니다.
저는 gcc 9.3.0(리눅스 민트 20.2)을 사용하여 x86-64 시스템(인텔)에서 복제했습니다.
의 결과hexdump -C
:
바이트 시퀀스는 동일합니다.
그래서 사용합니다.gcc -S -c
:
.file "teststr.c"
.text
.section .rodata
.LC0:
.string "%s"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movl $1668178254, -14(%rbp) # NOTE THIS PART HERE
movw $121, -10(%rbp) # AND HERE
leaq -14(%rbp), %rax
movq %rax, %rsi
leaq .LC0(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
movq -8(%rbp), %rdx
xorq %fs:40, %rdx
je .L3
call __stack_chk_fail@PLT
.L3:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
강조 표시된 값1668178254
16진수636E614E
또는 "cnaN"(x86이 리틀 엔디언 시스템이기 때문에 "Nanc"가 됨),121
16진수79
또는 "y".
따라서 짧은 문자열인 경우 파일의 바이트 문자열 섹션에서 루프 복사 대신 두 개의 이동 명령을 사용하며, 중간에 있는 "가비지"는 다음과 같습니다.movw
설명.최적화 플래그가 컴파일러에게 "공식적으로" 제공되지 않았음에도 불구하고 메모리를 통해 바이트 단위로 루프하는 것에 비해 초기화를 최적화하는 방법일 가능성이 높습니다. 즉, 컴파일러는 이와 관련하여 원하는 작업을 수행할 수 있습니다.따라서 마이크로소프트의 컴파일러는 컴파일 방식에서 더 "시학적"인 것처럼 보입니다. 왜냐하면 실제로는 문자열을 연속적으로 결합하는 것을 선호하기 때문에 최적화를 포기하기 때문입니다.
일반적으로 컴파일된 프로그램은 다른 유형의 "섹션"으로 나뉩니다.어셈블리 파일은 명령어를 사용하여 명령어 간에 전환합니다.
- 코드(.text")
- 정적 읽기 전용 데이터(.section .rodata")
- 초기화된 전역 또는 정적 변수(.data")
- 초기화되지 않은(또는 초기화되지 않은) 전역 또는 정적 변수(.bss")
C의 문자열 리터럴은 두 가지 방법으로 사용할 수 있습니다.
- 상수 데이터에 대한 포인터로 사용됩니다.
- 배열의 이니셜라이저로.
문자열 리터럴이 포인터로 사용되는 경우 컴파일러는 문자열 데이터를 읽기 전용 데이터 섹션에 배치할 가능성이 높습니다.
문자열 리터럴을 사용하여 글로벌/정적 배열을 초기화하는 경우 컴파일러는 초기화된 데이터 섹션(또는 배열이 const로 선언된 경우 읽기 전용 데이터 섹션)에 배열을 배치할 가능성이 높습니다.
그러나 이 경우 초기화하는 배열은 자동 로컬 변수입니다.따라서 프로그램 시작 전에는 사전 초기화할 수 없습니다.컴파일러는 함수가 실행될 때마다 코드를 초기화하기 위해 코드를 포함해야 합니다.
컴파일러는 문자열을 읽기 전용 데이터 위치에 저장한 다음 복사 루틴(인라인 또는 호출)을 사용하여 로컬 배열에 복사하도록 선택할 수 있습니다. (이 경우 전체의 연속된 복사본이 있을 것이고 그렇지 않으면 없습니다.)단순히 배열의 요소를 하나씩 설정하는 명령을 생성하도록 선택할 수 있습니다.동시에 여러 배열 요소를 설정하는 명령을 생성하도록 선택할 수 있습니다.(예: 4바이트 및 종료를 포함한 2바이트)'\0'
)
추신: 이 질문에 대한 다른 답변에 https//godbolt.org/ 링크를 올리는 사람들이 있습니다.컴파일러 탐색기는 유용한 도구이지만 기본적으로 어셈블리 출력에서 섹션 전환 지시사항을 숨깁니다.
언급URL : https://stackoverflow.com/questions/71932148/how-are-char-arrays-strings-stored-in-binary-files
'programing' 카테고리의 다른 글
ANR 오류 "Broadcast of Intent {act=com.google.화력 기지인스턴스_ID_EVENT"... "파이어베이스"인스턴스 IDAndroid 7.1 및 8.0용 "내부 수신기" (0) | 2023.06.09 |
---|---|
오라클.NetManagedDataAccess 오류: 'OracleInternal' 유형을 로드할 수 없습니다.어셈블리의 Common.ConfigBaseClass' (0) | 2023.06.09 |
SQL Group By and Order - 테이블의 가장 최근 항목에 대한 세부 정보 검색 (0) | 2023.06.09 |
Android 전화기의 전화 번호를 프로그래밍 방식으로 가져옵니다. (0) | 2023.06.09 |
Yum이 키보드 인터럽트 오류와 함께 충돌했습니다. (0) | 2023.06.09 |