Solidity의 mload()와 add()는 EVM 어셈블리(Assembly)에서 메모리를 다룰 때 사용되는 저수준 함수(low-level functions)이다.
이 두 함수는 일반적인 Solidity 코드에서는 잘 사용되지 않지만, 저수준 메모리 조작이 필요한 경우(예: call, delegatecall, staticcall 등의 실행)에는 필수적이다.
📌 1. mload() 함수: 메모리에서 데이터 읽기
📝 함수 정의
mload(uint256 p) -> value
p: 메모리 주소(메모리에서 읽을 위치, 바이트 단위)value:p주소에 저장된 32바이트(256비트) 크기의 값을 반환
🔹 mload()의 역할
- EVM의 메모리에서 32바이트(256비트) 데이터를 읽는 역할을 한다.
- Solidity에서는
bytes memory배열을 다룰 때, 첫 32바이트는 배열의 길이를 저장한다. - 따라서,
mload(data)를 호출하면data의 길이를 읽을 수 있다.
📌 예제 코드: mload()를 사용한 메모리 읽기
pragma solidity ^0.8.0;
contract TestMload {
function getMemoryData(bytes memory data) public pure returns (uint256) {
assembly {
let length := mload(data) // data의 길이를 가져옴
return(length, 0x20) // 길이를 반환 (첫 32바이트에 저장됨)
}
}
}
📌 설명
data는 동적 배열(bytes memory)이므로, 첫 32바이트는 데이터 길이를 저장하고 있음.mload(data)는 첫 32바이트 값을 읽어서length에 저장.return(length, 0x20)을 사용해 길이를 반환.
📌 실행 예제
bytes memory data = abi.encode("Hello, world!"); // "Hello, world!"를 바이너리로 변환
uint256 length = getMemoryData(data);
🔹 data의 첫 32바이트에는 "Hello, world!"의 길이(13)가 저장되어 있음 → mload(data) == 13
🔎 mload()가 메모리를 읽는 방식
메모리는 32바이트(256비트) 단위로 정렬되어 있음. mload()는 메모리의 특정 주소(p)부터 32바이트를 읽음.
예제: 메모리 상태
| 메모리 주소 | 데이터 |
|---|---|
0x00 |
0x000000000000000000000000000000000000000000000000000000000000000D (13 in decimal) |
0x20 |
0x48656c6c6f2c20776f726c642100000000000000000000000000000000000000 ("Hello, world!" in bytes) |
📌 mload(data) 실행 결과는 0x000000000000000000000000000000000000000000000000000000000000000D (13)
📌 mload(add(data, 0x20)) 실행 결과는 "Hello, world!"의 첫 32바이트 값
📌 2. add() 함수: 주소 또는 숫자 더하기
📝 함수 정의
add(uint256 a, uint256 b) -> result
a: 첫 번째 숫자 또는 메모리 주소b: 두 번째 숫자 또는 오프셋(offset)result:a + b결과 값
🔹 add()의 역할
- 두 개의 숫자를 더하는 역할을 한다.
- 일반적인 연산 뿐만 아니라, 메모리 주소 계산에 사용된다.
📌 예제 코드: add()를 사용한 주소 계산
pragma solidity ^0.8.0;
contract TestAdd {
function addNumbers(uint256 a, uint256 b) public pure returns (uint256 sum) {
assembly {
sum := add(a, b)
}
}
}
📌 설명
add(a, b)는 단순히 두 숫자를 더한 값(a + b)을 반환.- Solidity에서는 안전한 연산을 위해 기본적으로 오버플로우 방지가 적용되지만, EVM 어셈블리에서는 오버플로우가 발생할 수 있음.
📌 실행 예제
uint256 result = addNumbers(5, 10); // 결과: 15
✅ add(5, 10) → 15
📌 add()를 사용하여 메모리 주소 이동
배열에서 특정 데이터 위치를 찾을 때 활용됨
function getSecondItem(bytes memory data) public pure returns (uint256 secondItem) {
assembly {
secondItem := mload(add(data, 0x20)) // data의 두 번째 값 읽기
}
}
📌 설명
data의 첫 32바이트는 배열의 길이가 저장됨.add(data, 0x20)→data의 실제 데이터 시작 위치.mload(add(data, 0x20))→ 두 번째 항목을 읽음.
📌 add()와 mload()를 함께 사용하는 이유
Solidity에서 bytes memory는 첫 32바이트가 길이를 저장하고, 그 이후부터 실제 데이터가 저장됨.
따라서 데이터를 읽을 때, mload()와 add()를 조합하여 정확한 위치를 찾아야 함.
📌 실제 사용 예
success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
mload(data)→data배열의 길이를 가져옴.add(data, 0x20)→data의 첫 32바이트(길이 정보)를 건너뛰고, 실제 데이터 시작 위치를 찾음.call()을 실행하여 해당 데이터를 전송.
📌 정리
| 함수 | 역할 | 사용 예 |
|---|---|---|
mload(uint256 p) |
메모리 주소 p에서 32바이트 데이터 읽기 |
mload(data) → data의 길이 읽기 |
add(uint256 a, uint256 b) |
a + b 연산 수행 |
add(data, 0x20) → data에서 실제 데이터 시작 위치 찾기 |
✔ mload()는 32바이트(256비트) 단위로 데이터를 읽는다.
✔ add()는 숫자 연산 또는 메모리 주소 이동에 사용된다.
✔ Solidity에서 ABI 인코딩된 데이터를 처리할 때 필수적인 연산이다. 🚀🔥
'Solidity' 카테고리의 다른 글
| Over/UnderFlow 검사의 의도적 비활성화 - unchecked (0) | 2025.02.21 |
|---|---|
| 재진입공격 방지기법-nonReentrant (0) | 2025.02.21 |
| 저수준 호출(low-level call) 문법 - call, staticCall, delegateCall (0) | 2025.02.20 |
| assembly ("memory-safe"){} 문법 (1) | 2025.02.01 |
| Solidity 개발 시, 가스비 절감을 위한 최적화 기법 (1) | 2025.01.03 |