指针操作与字符串实战
·
01.README.md/pointer_copy
# pointer_copy
简介
- 演示基于指针的整数数组遍历与复制。包含前向复制(copy_forward)和在重叠区域下的安全复制(copy_safe,类似 memmove)。
目的
- 理解指针算术(p + n、p++)与数组名退化为指针的含义。
- 掌握重叠内存拷贝为何需要选择合适的拷贝方向(前向或后向)。
主要文件
- pointer_copy.c — 示例源码(遵循 snake_case、指针后缀 `_ptr`、输出参数 `*_out` 约定)
编译与运行(说明)
- 可使用标准 C 编译器(例如 gcc/clang)进行编译并在本地运行,或在支持的交叉编译环境下交叉编译再在目标平台运行。
示例输出(大致)
- source array before copy: 1 2 3 4 5 6
- destination array after copy_forward: 1 2 3 4 5 6
- buffer before overlap copy: 10 20 30 40 50 60
- buffer after overlap copy (safe): 10 20 10 20 30 40
学习要点
- `arr[i]` 等价于 `*(arr + i)`。
- 指针加法以元素为单位(`p + 1` 增加 `sizeof(*p)` 字节)。
- 当 `destination > source` 且存在重叠时,应从后向前复制以避免覆盖尚未复制的数据。
- one-past-the-end(如 `src + n`)可作为边界比较,但不能解引用。
扩展练习
- 在 `copy_safe` 中打印 `read_ptr`/`write_ptr` 的地址和值以便观察。
- 实现并比较 `memcpy`、`memmove` 与手写实现的差异。
- 实现数组/字符串的 in-place 反转。
注意事项
- 调用方需保证传入的 `element_count` 合理且内存有效;跨对象的指针算术为未定义行为。
/*
* pointer_copy.c
*
* - 命名风格:snake_case
* - 指针变量以 _ptr 后缀表示(如 read_ptr、write_ptr)
* - 避免单字符变量名,使用 element_count、index、buffer_size 等语义化名称
*/
#include <stdio.h>
#include <stddef.h> /* for size_t */
void print_int_array(const int *source_array_ptr, size_t element_count, const char *label_text) {
if (label_text == NULL) label_text = "(null)";
printf("%s:", label_text);
for (size_t index = 0; index < element_count; ++index) {
printf(" %d", source_array_ptr[index]);
}
printf("\n");
}
/* 向前复制(当 source 与 destination 不重叠时安全) */
void copy_forward(const int *source_array_ptr, int *destination_array_ptr, size_t element_count) {
const int *read_ptr = source_array_ptr;
int *write_ptr = destination_array_ptr;
const int *source_end_ptr = source_array_ptr + element_count;
while (read_ptr < source_end_ptr) {
*write_ptr++ = *read_ptr++;
}
}
/* 安全复制(简单 memmove 思路):
* 若目标在源之后且有重叠,则从后向前复制;否则前向复制。
*/
void copy_safe(const int *source_array_ptr, int *destination_array_ptr, size_t element_count) {
if (element_count == 0) return;
if (destination_array_ptr > source_array_ptr &&
destination_array_ptr < source_array_ptr + element_count) {
/* 后向复制:初始化为 one-past-the-end,然后先 -- 再解引用 */
const int *read_ptr = source_array_ptr + element_count;
int *write_ptr = destination_array_ptr + element_count;
while (element_count--) {
*--write_ptr = *--read_ptr;
}
} else {
/* 向前复制 */
const int *read_ptr = source_array_ptr;
int *write_ptr = destination_array_ptr;
while (element_count--) {
*write_ptr++ = *read_ptr++;
}
}
}
int main(void) {
/* 示例 1:不重叠的源数组与目标数组 */
int source_array[] = {1, 2, 3, 4, 5, 6};
int destination_array[6] = {0};
size_t element_count = sizeof(source_array) / sizeof(source_array[0]);
print_int_array(source_array, element_count, "source array before copy");
copy_forward(source_array, destination_array, element_count);
print_int_array(destination_array, element_count, "destination array after copy_forward");
/* 示例 2:同一数组内重叠复制,需要安全后向复制 */
int buffer_array[] = {10, 20, 30, 40, 50, 60};
const size_t buffer_size = sizeof(buffer_array) / sizeof(buffer_array[0]);
const size_t overlap_count = 4; /* 复制的元素个数 */
print_int_array(buffer_array, buffer_size, "buffer before overlap copy");
/* 将 buffer_array[0..3] 复制到 buffer_array[2..5] */
copy_safe(buffer_array, buffer_array + 2, overlap_count);
print_int_array(buffer_array, buffer_size, "buffer after overlap copy (safe)");
return 0;
}
02.README.md/multi_return
# multi_return
简介
- 演示如何通过指针参数返回多个值(输出参数),以及如何在堆上分配内存并将指针返回给调用者(调用者负责 free)。
目的
- 理解 C 函数参数按值传递的语义,以及如何通过传入地址修改调用者的数据。
- 理解 malloc/free 的用法与异常处理(检查 NULL),以及不要返回局部栈地址的原因。
主要文件
- multi_return.c — 示例源码(使用 `*_out` 作为输出参数后缀,堆上分配返回指针以 `_ptr` 结尾)
编译与运行(说明)
- 使用常见的 C 编译器进行编译;在运行前请确保所用环境允许分配堆内存,并注意检查 malloc 返回值。
示例输出(大致)
- value_a=6 value_b=7 sum=13 product=42
- squares: 0 1 4 9 16
学习要点
- 使用 `&sum_out`、`&product_out` 把结果写回调用者内存。
- `malloc` 可能返回 `NULL`,必须检测并妥善处理。
- 不要返回指向局部变量或局部数组的指针(会出现悬垂指针)。
- 若需返回多个值,可考虑返回 `struct` 以减少输出参数。
扩展练习
- 将 `sum_and_product` 改为返回 `struct`,比较两种接口优劣。
- 在 `make_squares` 中加入越界与溢出检查,以及对极大 `n` 的防护策略。
- 模拟内存分配失败并验证程序行为。
注意事项
- 使用 `free` 释放 `malloc` 分配的内存以避免内存泄漏。
- 输出参数可为 `NULL`,函数应能安全处理(示例已实现)。
/*
* multi_return.c
*
* - 命名风格:snake_case
* - 输出参数使用 *_out 风格(如 sum_out、product_out)
* - 动态分配返回指针命名使用 _ptr 后缀(如 square_array_ptr)
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h> /* for size_t */
/* 通过输出参数返回和与积(输出参数可为 NULL 表示调用者不需要该输出) */
void sum_and_product(int value_a, int value_b, int *sum_out, int *product_out) {
if (sum_out) *sum_out = value_a + value_b;
if (product_out) *product_out = value_a * value_b;
}
/* 在堆上分配 element_count 个 int,返回指针(调用者负责 free) */
int *make_squares(size_t element_count) {
int *square_array_ptr = malloc(element_count * sizeof(int));
if (!square_array_ptr) return NULL;
for (size_t index = 0; index < element_count; ++index) {
square_array_ptr[index] = (int)(index * index);
}
return square_array_ptr;
}
int main(void) {
int value_a = 6;
int value_b = 7;
int sum_out_value = 0;
int product_out_value = 0;
sum_and_product(value_a, value_b, &sum_out_value, &product_out_value);
printf("value_a=%d value_b=%d sum=%d product=%d\n",
value_a, value_b, sum_out_value, product_out_value);
const size_t element_count = 5;
int *square_array_ptr = make_squares(element_count);
if (!square_array_ptr) {
fprintf(stderr, "make_squares failed\n");
return 1;
}
printf("squares:");
for (size_t index = 0; index < element_count; ++index) {
printf(" %d", square_array_ptr[index]);
}
printf("\n");
free(square_array_ptr);
return 0;
}
03-README.md/string_pointer
# string_pointer
简介
- 演示指针与 C 字符串的基本操作:基于指针实现的 `my_strlen`、`my_strcpy`,以及指针数组(类似 `argv`)的使用。
目的
- 掌握 C 字符串以 `'\0'` 终止的语义以及 `const` 在字符串上下文中的作用。
- 理解指针数组与 `char **` 的关系,学习如何遍历字符串与字符串数组。
主要文件
- string_pointer.c — 示例源码(`my_strlen`、`my_strcpy`、指针数组示例)
编译与运行(说明)
- 使用常见 C 编译器编译并运行;请在运行时保证目标缓冲区有足够大小以避免溢出。
示例输出(大致)
- string: "hello, pointer!"
- my_strlen = 16
- copied string: "hello, pointer!"
- iterate words using char **:
- one (length 3)
- two (length 3)
- three (length 5)
学习要点
- `my_strlen` 用指针遍历直到遇到 `'\0'`,返回 `p - s`。
- `my_strcpy` 可用 `(*d++ = *s++)` 的赋值方式一次复制一个字符,包括终止符。
- 字符串字面量通常位于只读静态区,应通过 `const char *` 引用并避免写入。
- 指针数组的元素是指向字符串的指针(`const char * words[]`),数组在表达式中可退化为 `const char **`。
扩展练习
- 实现 `my_strchr`、`my_strncmp`、`my_strcat`。
- 实现带边界检查的安全版 `my_strncpy` 或 `my_strlcpy`。
- 比较指针实现与索引实现的性能差别(micro-benchmark)。
注意事项
- 传入 `NULL` 或非 NUL 结尾的内存会导致未定义行为。
- 写入目标缓冲区前应确保其大小足够以避免缓冲区溢出。
/*
* string_pointer.c
*
* - 命名风格:snake_case
* - 指针使用 _ptr 后缀(如 scan_ptr、dest_ptr)
* - 避免单字符变量名(使用 index、buffer_size 等)
*/
#include <stdio.h>
#include <stddef.h> /* for size_t */
/* 基于指针遍历实现 strlen */
size_t my_strlen(const char *text_literal_ptr) {
const char *scan_ptr = text_literal_ptr;
while (*scan_ptr) ++scan_ptr;
return (size_t)(scan_ptr - text_literal_ptr);
}
/* 基于指针实现 strcpy(假设 dest_buffer_ptr 足够大) */
char *my_strcpy(char *dest_buffer_ptr, const char *src_text_ptr) {
char *dest_ptr = dest_buffer_ptr;
while ((*dest_ptr++ = *src_text_ptr++))
; /* 复制包括末尾 '\\0' */
return dest_buffer_ptr;
}
int main(void) {
const char *text_literal = "hello, pointer!";
printf("string: \"%s\"\n", text_literal);
printf("my_strlen = %zu\n", my_strlen(text_literal));
const size_t buffer_size = 64;
char buffer_destination[64];
my_strcpy(buffer_destination, text_literal);
printf("copied string: \"%s\"\n", buffer_destination);
/* 指针数组示例(类似 argv) */
const char *word_list[] = {"one", "two", "three", NULL};
const char **current_word_ptr = word_list;
printf("iterate words using char **:\n");
while (*current_word_ptr) {
const char *current_word_ptr_value = *current_word_ptr;
printf(" %s (length %zu)\n", current_word_ptr_value, my_strlen(current_word_ptr_value));
++current_word_ptr;
}
return 0;
}
更多推荐



所有评论(0)