Pwn 基础之C/C++(四):数组、字符串与字节序
本篇目标:理解连续内存、数组与指针的关系、C 字符串及大小端序。
预计阅读时间:20~25 分钟。
1. 一维数组
int numbers[4] = {10, 20, 30, 40};
数组包含固定数量的同类型对象,并通常连续排列:
numbers[0] numbers[1] numbers[2] numbers[3]
10 20 30 40
下标从 0 开始:
printf("%d\n", numbers[0]);
printf("%d\n", numbers[3]);
2. 计算数组长度
size_t count = sizeof(numbers) / sizeof(numbers[0]);
sizeof(numbers)是整个数组的字节数;sizeof(numbers[0])是一个元素的字节数。
这种方法只适用于当前表达式中仍是真正数组的情况。
3. 数组与指针的关系
在许多表达式中,数组名会转换为首元素指针:
int *pointer = numbers;
此时:
numbers[2]
等价于:
*(numbers + 2)
但是数组和指针不是同一种对象:
sizeof(numbers); // 整个数组大小
sizeof(pointer); // 指针变量大小
4. 数组作为参数
void print_array(const int values[], size_t count)
{
for (size_t i = 0; i < count; i++) {
printf("%d\n", values[i]);
}
}
参数中的 int values[] 会被调整为 int *values,函数无法仅靠它恢复原数组长度,所以需要单独传入 count。
5. 二维数组
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
内存中通常按行连续存储:
1 2 3 4 5 6
传入函数时需要让编译器知道后续维度:
void print_matrix(int matrix[][3], size_t rows)
{
for (size_t i = 0; i < rows; i++) {
for (size_t j = 0; j < 3; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
}
6. C 字符串的本质
C 字符串是以空字符 \0 结尾的字符数组:
char text[] = "hello";
实际内容为:
'h' 'e' 'l' 'l' 'o' '\0'
十六进制表示:
68 65 6c 6c 6f 00
字符串有 5 个可见字符,但数组需要 6 个字节。
7. \0 与字符 0
'\0' = 0x00
'0' = 0x30
前者是字符串结束标志,后者是可见字符零。
8. 字符数组与字符串字面量
char text[] = "hello";
text[0] = 'H';
这里创建了可修改数组。
const char *text = "hello";
这里让指针指向字符串字面量,不应尝试修改字面量内容。
9. 常用字符串与内存函数
需要:
#include <string.h>
strlen
size_t length = strlen("hello"); // 5
不计算结尾 \0。
strcmp
if (strcmp(left, right) == 0) {
printf("equal\n");
}
left == right 通常比较地址,而不是字符串内容。
memcpy
memcpy(destination, source, count);
按指定字节数复制。
memcmp
memcmp(left, right, count);
按指定字节数比较。
memset
memset(buffer, 0, sizeof(buffer));
按字节设置。下面不会把每个 int 设置为数值 1:
int values[4];
memset(values, 1, sizeof(values));
它会把每个字节设置成 0x01。
10. 字符串与原始字节
char text[] = "ABC";
是以 \0 结尾的字符串。
unsigned char data[] = {0x41, 0x00, 0x42, 0xff};
是原始字节数组,不一定能作为字符串处理。处理二进制数据时应同时保存地址和长度。
11. 大小端序
假设:
uint32_t value = 0x12345678;
在常见 x86-64 小端环境中,内存顺序是:
78 56 34 12
低有效字节放在低地址。
大端序则是:
12 34 56 78
逐字节验证:
#include <stdint.h>
#include <stdio.h>
int main(void)
{
uint32_t value = 0x12345678;
unsigned char *bytes = (unsigned char *)&value;
for (size_t i = 0; i < sizeof(value); i++) {
printf("%02x ", bytes[i]);
}
printf("\n");
return 0;
}
字节序描述的是多字节数据在内存中的排列,而不是数值打印时的书写顺序。
本篇练习
- 使用下标和指针两种方式遍历数组。
- 手写
string_length计算字符串长度。 - 手写字符串比较函数。
- 逐字节打印
0x12345678。 - 比较字符数组、字符串字面量指针及其
sizeof。
本篇小结
- 数组保存连续的同类型对象;
- 数组名会在很多表达式中转换成首元素指针;
- 数组与指针并不完全相同;
- C 字符串以
\0结尾; - 原始字节不等于字符串;
- x86-64 通常使用小端序。
下一篇
下一篇学习结构体、联合体、枚举、成员偏移和内存对齐。

说些什么吧!