前言
指针是C语言的灵魂,也是最具挑战性的核心概念。理解指针不仅关乎语法掌握,更关系到对计算机内存模型的深刻认知。本文将系统性地解析指针的各个方面,即使是看似与指针无关的冒泡排序,我们也会揭示其与指针的内在联系。
目录
[1. 内存和地址](#1. 内存和地址)
[2. 指针变量和地址](#2. 指针变量和地址)
[3. 指针变量类型的意义](#3. 指针变量类型的意义)
[4. const修饰指针](#4. const修饰指针)
[5. 指针运算](#5. 指针运算)
[6. 野指针](#6. 野指针)
[7. assert断言](#7. assert断言)
[8. 指针的使用和传址调用](#8. 指针的使用和传址调用)
[9. 数组名的理解和使用指针访问数组](#9. 数组名的理解和使用指针访问数组)
[10. 一维数组传参的本质](#10. 一维数组传参的本质)
[11. 冒泡排序的指针分析](#11. 冒泡排序的指针分析)
[12. 二级指针](#12. 二级指针)
[13. 指针数组](#13. 指针数组)
[14. 指针数组模拟二维数组](#14. 指针数组模拟二维数组)
[15. 字符指针变量](#15. 字符指针变量)
[16. 数组指针变量](#16. 数组指针变量)
[17. 二维数组传参的本质](#17. 二维数组传参的本质)
[18. 函数指针变量](#18. 函数指针变量)
[19. 函数指针数组](#19. 函数指针数组)
[20. 转移表](#20. 转移表)
[21. 回调函数](#21. 回调函数)
[22. qsort使用举例](#22. qsort使用举例)
[23. qsort函数的模拟实现](#23. qsort函数的模拟实现)
[24. sizeof和strlen的对比](#24. sizeof和strlen的对比)
[25. 数组和指针笔试题解析](#25. 数组和指针笔试题解析)
[26. 指针运算笔试题解析](#26. 指针运算笔试题解析)
正文
1. 内存和地址
内存本质:计算机内存是由无数个以字节为单位内存单元组成的线性空间,每个单元都有唯一的地址标识。
cpp
#include <stdio.h>
int main()
{
int a = 10;
printf("变量a的值: %d\n", a);
printf("变量a的地址: %p\n", &a);
// 内存地址的十六进制表示
// 例如:0x7ffd42a1b23c
return 0;
}
关键理解:变量名是给程序员使用的标签,编译器会将其转换为内存地址。
2. 指针变量和地址
指针变量专门用于存储内存地址。
cpp
#include <stdio.h>
int main()
{
int num = 42;
int *ptr = # // ptr是指向int的指针变量
printf("num的值: %d\n", num);
printf("num的地址: %p\n", &num);
printf("ptr存储的地址: %p\n", ptr);
printf("通过ptr访问的值: %d\n", *ptr);
*ptr = 100; // 通过指针修改变量值
printf("修改后num的值: %d\n", num);
return 0;
}
3. 指针变量类型的意义
指针类型决定了:
-
解引用时访问的字节数
-
指针运算的步长
cpp
#include <stdio.h>
int main()
{
int arr[5] = {10, 20, 30, 40, 50};
int *int_ptr = arr;
char *char_ptr = (char*)arr;
printf("int指针: %p -> ", int_ptr);
printf("值: %d\n", *int_ptr);
printf("char指针: %p -> ", char_ptr);
printf("值: %d\n", *char_ptr);
// 指针运算演示类型差异
printf("\n指针+1操作:\n");
printf("int_ptr + 1 = %p (步长%d字节)\n", int_ptr + 1, (int)((char*)(int_ptr + 1) - (char*)int_ptr));
printf("char_ptr + 1 = %p (步长%d字节)\n", char_ptr + 1, (int)(char_ptr + 1 - char_ptr));
return 0;
}
4. const修饰指针
const与指针的三种组合方式:
cpp
#include <stdio.h>
int main()
{
int a = 10, b = 20;
// 1. 指向常量的指针 - 不能通过指针修改数据
const int *ptr1 = &a;
// *ptr1 = 30; // 错误:不能修改
ptr1 = &b; // 正确:可以改变指向
// 2. 常量指针 - 不能改变指针的指向
int *const ptr2 = &a;
*ptr2 = 30; // 正确:可以修改数据
// ptr2 = &b; // 错误:不能改变指向
// 3. 指向常量的常量指针
const int *const ptr3 = &a;
// *ptr3 = 40; // 错误
// ptr3 = &b; // 错误
return 0;
}
5. 指针运算
指针支持多种运算操作:
cpp
#include <stdio.h>
int main()
{
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr;
int *ptr_end = arr + 4;
printf("数组元素:\n");
for(int *p = arr; p <= ptr_end; p++)
{
printf("arr[%ld] = %d, 地址: %p\n", p - arr, *p, p);
}
// 指针关系运算
printf("\n指针比较:\n");
printf("ptr < ptr_end: %d\n", ptr < ptr_end);
printf("ptr_end - ptr: %ld\n", ptr_end - ptr);
return 0;
}
6. 野指针
野指针指向无效内存区域,是常见错误来源:
cpp
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 1. 未初始化的指针
int *wild_ptr1;
// printf("%d\n", *wild_ptr1); // 未定义行为
// 2. 已释放的指针
int *ptr = (int*)malloc(sizeof(int));
*ptr = 100;
free(ptr); // ptr成为野指针
// *ptr = 200; // 危险操作
// 3. 越界访问
int arr[3] = {1, 2, 3};
int *wild_ptr3 = arr + 5; // 越界
// printf("%d\n", *wild_ptr3); // 未定义行为
// 正确做法:释放后置为NULL
ptr = NULL;
return 0;
}
7. assert断言
assert用于调试阶段检查假设条件:
cpp
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
int* create_array(int size)
{
assert(size > 0 && "数组大小必须为正数");
int *arr = (int*)malloc(size * sizeof(int));
assert(arr != NULL && "内存分配失败");
return arr;
}
int main()
{
int *arr = create_array(5);
// 使用数组...
free(arr);
return 0;
}
8. 指针的使用和传址调用
理解值传递与地址传递的区别:
cpp
#include <stdio.h>
// 值传递 - 无法修改实参
void swap_by_value(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
// 地址传递 - 可以修改实参
void swap_by_pointer(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int x = 10, y = 20;
printf("交换前: x = %d, y = %d\n", x, y);
swap_by_value(x, y);
printf("值传递后: x = %d, y = %d\n", x, y);
swap_by_pointer(&x, &y);
printf("地址传递后: x = %d, y = %d\n", x, y);
return 0;
}
9. 数组名的理解和使用指针访问数组
数组名在多数情况下退化为指针:
cpp
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
printf("数组名作为指针:\n");
printf("arr = %p\n", arr);
printf("&arr[0] = %p\n", &arr[0]);
// 三种访问数组元素的方式
printf("\n访问数组元素:\n");
for(int i = 0; i < 5; i++) {
printf("arr[%d] = %d, ", i, arr[i]);
printf("*(arr + %d) = %d, ", i, *(arr + i));
int *ptr = arr;
printf("ptr[%d] = %d\n", i, ptr[i]);
}
// 数组名与指针的区别
printf("\n大小信息:\n");
printf("sizeof(arr) = %zu (整个数组大小)\n", sizeof(arr));
printf("sizeof(ptr) = %zu (指针大小)\n", sizeof(int*));
return 0;
}
10. 一维数组传参的本质
数组传参时退化为指针:
cpp
#include <stdio.h>
// 三种等价的函数声明方式
void print_array1(int arr[], int size)
{
for(int i = 0; i < size; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void print_array2(int *arr, int size)
{
for(int i = 0; i < size; i++)
{
printf("%d ", *(arr + i));
}
printf("\n");
}
void print_array3(int arr[5], int size)
{ // 5被编译器忽略
for(int i = 0; i < size; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[5] = {1, 2, 3, 4, 5};
printf("数组传参演示:\n");
print_array1(arr, 5);
print_array2(arr, 5);
print_array3(arr, 5);
// 验证数组退化为指针
printf("函数内sizeof(arr): ");
void check_size(int param[])
{
printf("%zu\n", sizeof(param)); // 输出指针大小,不是数组大小
}
check_size(arr);
return 0;
}
11. 冒泡排序的指针分析
冒泡排序与指针的深度结合:
cpp
#include <stdio.h>
// 传统数组下标版本
void bubble_sort_traditional(int arr[], int n)
{
for(int i = 0; i < n - 1; i++)
{
for(int j = 0; j < n - 1 - i; j++)
{
if(arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 纯指针版本 - 展示指针运算的强大
void bubble_sort_pointer(int *arr, int n)
{
int *end = arr + n - 1; // 指向最后一个元素
for(int *i = arr; i < end; i++)
{
for(int *j = arr; j < end - (i - arr); j++)
{
if(*j > *(j + 1))
{
// 指针交换
int temp = *j;
*j = *(j + 1);
*(j + 1) = temp;
}
}
}
}
// 优化的指针版本
void bubble_sort_optimized(int *arr, int n)
{
int *start = arr;
int *end = arr + n - 1;
int swapped;
do
{
swapped = 0;
int *current = start;
while(current < end)
{
if(*current > *(current + 1))
{
// 交换
int temp = *current;
*current = *(current + 1);
*(current + 1) = temp;
swapped = 1;
}
current++;
}
end--; // 每次循环后,末尾元素已排序
} while(swapped && start < end);
}
void print_array(int *arr, int n, const char *name)
{
printf("%s: ", name);
for(int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int arr1[] = {64, 34, 25, 12, 22, 11, 90};
int arr2[] = {64, 34, 25, 12, 22, 11, 90};
int arr3[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(arr1) / sizeof(arr1[0]);
printf("原始数组: ");
print_array(arr1, n, "原始");
bubble_sort_traditional(arr1, n);
print_array(arr1, n, "传统排序");
bubble_sort_pointer(arr2, n);
print_array(arr2, n, "指针排序");
bubble_sort_optimized(arr3, n);
print_array(arr3, n, "优化指针");
return 0;
}
指针视角分析:
-
数组名
arr
作为指针参数传递 -
指针运算
arr + i
替代数组索引arr[i]
-
指针比较
current < end
作为循环条件 -
通过指针直接操作内存,提升效率
12. 二级指针
指向指针的指针:
cpp
#include <stdio.h>
#include <stdlib.h>
int main()
{
int value = 100;
int *ptr = &value;
int **pptr = &ptr; // 二级指针
printf("变量关系:\n");
printf("value = %d, &value = %p\n", value, (void*)&value);
printf("*ptr = %d, ptr = %p, &ptr = %p\n", *ptr, (void*)ptr, (void*)&ptr);
printf("**pptr = %d, *pptr = %p, pptr = %p\n", **pptr, (void*)*pptr, (void*)pptr);
// 通过二级指针修改变量值
**pptr = 200;
printf("\n修改后 value = %d\n", value);
// 动态内存分配示例
int **matrix = (int**)malloc(3 * sizeof(int*));
for(int i = 0; i < 3; i++)
{
matrix[i] = (int*)malloc(4 * sizeof(int));
}
// 使用矩阵...
// 释放内存
for(int i = 0; i < 3; i++)
{
free(matrix[i]);
}
free(matrix);
return 0;
}
13. 指针数组
元素为指针的数组:
cpp
#include <stdio.h>
#include <string.h>
int main()
{
// 整数指针数组
int a = 10, b = 20, c = 30;
int *ptr_arr[3] = {&a, &b, &c};
printf("整数指针数组:\n");
for(int i = 0; i < 3; i++)
{
printf("ptr_arr[%d] = %p, *ptr_arr[%d] = %d\n",
i, (void*)ptr_arr[i], i, *ptr_arr[i]);
}
// 字符串指针数组
char *names[] = {"Alice", "Bob", "Charlie", "David"};
int name_count = sizeof(names) / sizeof(names[0]);
printf("\n字符串指针数组:\n");
for(int i = 0; i < name_count; i++)
{
printf("names[%d] = %s, 地址: %p\n", i, names[i], (void*)names[i]);
}
return 0;
}
14. 指针数组模拟二维数组
用指针数组实现类似二维数组的功能:
cpp
#include <stdio.h>
#include <stdlib.h>
int main()
{
int rows = 3, cols = 4;
// 创建指针数组
int **array = (int**)malloc(rows * sizeof(int*));
// 为每一行分配内存
for(int i = 0; i < rows; i++)
{
array[i] = (int*)malloc(cols * sizeof(int));
}
// 初始化数组
int counter = 1;
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
array[i][j] = counter++;
}
}
// 打印数组
printf("模拟的二维数组:\n");
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
printf("%2d ", array[i][j]);
}
printf("\n");
}
// 使用指针算术访问
printf("\n使用指针算术访问:\n");
for(int i = 0; i < rows; i++)
{
int *row_ptr = array[i];
for(int j = 0; j < cols; j++)
{
printf("%2d ", *(row_ptr + j));
}
printf("\n");
}
// 释放内存
for(int i = 0; i < rows; i++)
{
free(array[i]);
}
free(array);
return 0;
}
15. 字符指针变量
字符指针的特殊用法:
cpp
#include <stdio.h>
#include <string.h>
int main()
{
// 字符数组
char str1[] = "Hello World";
// 字符指针指向字符串常量
char *str2 = "Hello World";
printf("字符数组: %s\n", str1);
printf("字符指针: %s\n", str2);
// 重要区别
str1[0] = 'h'; // 正确:修改栈上数组
// str2[0] = 'h'; // 错误:修改只读内存
str2 = "New String"; // 正确:改变指针指向
printf("修改后:\n");
printf("str1: %s\n", str1);
printf("str2: %s\n", str2);
// 字符指针遍历
char *ptr = str1;
printf("\n字符指针遍历: ");
while(*ptr != '\0')
{
printf("%c", *ptr);
ptr++;
}
printf("\n");
return 0;
}
16. 数组指针变量
指向整个数组的指针:
cpp
#include <stdio.h>
int main()
{
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
// 数组指针 - 指向包含3个int的数组
int (*ptr)[3] = arr;
printf("数组指针演示:\n");
for(int i = 0; i < 2; i++)
{
for(int j = 0; j < 3; j++)
{
printf("ptr[%d][%d] = %d, 地址: %p\n",
i, j, ptr[i][j], (void*)&ptr[i][j]);
}
}
// 指针运算
printf("\n指针运算:\n");
printf("ptr = %p\n", (void*)ptr);
printf("ptr + 1 = %p (跳过%d字节)\n",
(void*)(ptr + 1), (int)((char*)(ptr + 1) - (char*)ptr));
return 0;
}
17. 二维数组传参的本质
二维数组参数传递的真相:
cpp
#include <stdio.h>
// 方式1:指定第二维大小
void print_2d_array1(int arr[][3], int rows)
{
printf("方式1 - 指定列数:\n");
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < 3; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
// 方式2:数组指针
void print_2d_array2(int (*arr)[3], int rows)
{
printf("方式2 - 数组指针:\n");
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < 3; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
// 方式3:作为一维数组处理
void print_2d_array3(int *arr, int rows, int cols)
{
printf("方式3 - 一维数组视角:\n");
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
printf("%d ", *(arr + i * cols + j));
}
printf("\n");
}
}
int main()
{
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
print_2d_array1(arr, 2);
printf("\n");
print_2d_array2(arr, 2);
printf("\n");
print_2d_array3(&arr[0][0], 2, 3);
return 0;
}
18. 函数指针变量
指向函数的指针:
cpp
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int multiply(int a, int b)
{
return a * b;
}
void greet()
{
printf("Hello from function pointer!\n");
}
int main()
{
// 函数指针声明
int (*func_ptr)(int, int);
void (*void_func_ptr)();
// 指向add函数
func_ptr = add;
printf("加法: %d\n", func_ptr(10, 20));
// 指向multiply函数
func_ptr = multiply;
printf("乘法: %d\n", func_ptr(10, 20));
// 无参函数指针
void_func_ptr = greet;
void_func_ptr();
return 0;
}
19. 函数指针数组
管理多个相关函数:
cpp
#include <stdio.h>
// 一系列数学运算函数
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { return b != 0 ? a / b : 0; }
int main()
{
// 函数指针数组
int (*operations[])(int, int) = {add, subtract, multiply, divide};
char *operation_names[] = {"加法", "减法", "乘法", "除法"};
int a = 100, b = 25;
int operation_count = sizeof(operations) / sizeof(operations[0]);
printf("函数指针数组演示:\n");
for(int i = 0; i < operation_count; i++)
{
int result = operations[i](a, b);
printf("%s: %d %c %d = %d\n",
operation_names[i], a,
i == 0 ? '+' : i == 1 ? '-' : i == 2 ? '*' : '/',
b, result);
}
return 0;
}
20. 转移表
使用函数指针数组实现跳转表:
cpp
#include <stdio.h>
#include <stdlib.h>
// 计算器操作函数
double add(double a, double b)
{
return a + b;
}
double subtract(double a, double b)
{
return a - b;
}
double multiply(double a, double b)
{
return a * b;
}
double divide(double a, double b)
{
if(b == 0)
{
printf("错误: 除数不能为0\n");
return 0;
}
return a / b;
}
void print_menu()
{
printf("\n=== 简单计算器 ===\n");
printf("1. 加法\n");
printf("2. 减法\n");
printf("3. 乘法\n");
printf("4. 除法\n");
printf("0. 退出\n");
printf("请选择操作: ");
}
int main()
{
// 转移表 - 函数指针数组
double (*calculator[])(double, double) = {NULL, add, subtract, multiply, divide};
int choice;
double num1, num2;
do
{
print_menu();
scanf("%d", &choice);
if(choice == 0)
{
printf("再见!\n");
break;
}
if(choice < 1 || choice > 4)
{
printf("无效选择!\n");
continue;
}
printf("输入两个数字: ");
scanf("%lf %lf", &num1, &num2);
// 使用转移表调用对应函数
double result = calculator[choice](num1, num2);
printf("结果: %.2lf\n", result);
} while(1);
return 0;
}
21. 回调函数
回调函数的原理和应用:
cpp
#include <stdio.h>
#include <stdlib.h>
// 回调函数类型定义
typedef void (*CallbackFunc)(int, void*);
// 处理数据的函数,接受回调函数作为参数
void process_data(int *array, int size, CallbackFunc callback, void *user_data)
{
printf("开始处理数据...\n");
for(int i = 0; i < size; i++)
{
// 对每个元素调用回调函数
callback(array[i], user_data);
}
printf("数据处理完成\n");
}
// 不同的回调函数实现
// 回调1: 打印元素
void print_element(int value, void *data)
{
printf("%d ", value);
}
// 回调2: 累加元素
void sum_elements(int value, void *data)
{
int *sum = (int*)data;
*sum += value;
}
// 回调3: 找最大值
void find_max(int value, void *data)
{
int *max = (int*)data;
if(value > *max)
{
*max = value;
}
}
int main()
{
int numbers[] = {23, 45, 12, 67, 89, 34};
int size = sizeof(numbers) / sizeof(numbers[0]);
printf("原始数组: ");
process_data(numbers, size, print_element, NULL);
printf("\n");
int total = 0;
process_data(numbers, size, sum_elements, &total);
printf("数组总和: %d\n", total);
int maximum = numbers[0];
process_data(numbers, size, find_max, &maximum);
printf("数组最大值: %d\n", maximum);
return 0;
}
22. qsort使用举例
标准库qsort函数的使用:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 整数比较函数
int compare_int(const void *a, const void *b)
{
return (*(int*)a - *(int*)b);
}
// 字符串比较函数
int compare_string(const void *a, const void *b)
{
return strcmp(*(const char**)a, *(const char**)b);
}
// 结构体比较函数
typedef struct
{
char name[50];
int age;
double salary;
} Person;
int compare_person_by_age(const void *a, const void *b)
{
return ((Person*)a)->age - ((Person*)b)->age;
}
int compare_person_by_name(const void *a, const void *b)
{
return strcmp(((Person*)a)->name, ((Person*)b)->name);
}
void print_array(int *arr, int n, const char *message)
{
printf("%s: ", message);
for(int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void print_strings(char **strings, int n, const char *message)
{
printf("%s: ", message);
for(int i = 0; i < n; i++)
{
printf("%s ", strings[i]);
}
printf("\n");
}
void print_persons(Person *persons, int n, const char *message)
{
printf("%s:\n", message);
for(int i = 0; i < n; i++)
{
printf(" 姓名: %s, 年龄: %d, 薪资: %.2lf\n",
persons[i].name, persons[i].age, persons[i].salary);
}
}
int main()
{
// 1. 整数数组排序
int numbers[] = {64, 34, 25, 12, 22, 11, 90};
int num_count = sizeof(numbers) / sizeof(numbers[0]);
print_array(numbers, num_count, "排序前");
qsort(numbers, num_count, sizeof(int), compare_int);
print_array(numbers, num_count, "排序后");
printf("\n");
// 2. 字符串数组排序
char *names[] = {"Charlie", "Alice", "David", "Bob"};
int name_count = sizeof(names) / sizeof(names[0]);
print_strings(names, name_count, "排序前");
qsort(names, name_count, sizeof(char*), compare_string);
print_strings(names, name_count, "排序后");
printf("\n");
// 3. 结构体数组排序
Person employees[] =
{
{"张三", 25, 5000.0},
{"李四", 30, 8000.0},
{"王五", 22, 4000.0},
{"赵六", 35, 10000.0}
};
int emp_count = sizeof(employees) / sizeof(employees[0]);
print_persons(employees, emp_count, "按年龄排序前");
qsort(employees, emp_count, sizeof(Person), compare_person_by_age);
print_persons(employees, emp_count, "按年龄排序后");
qsort(employees, emp_count, sizeof(Person), compare_person_by_name);
print_persons(employees, emp_count, "按姓名排序后");
return 0;
}
23. qsort函数的模拟实现
理解qsort的工作原理:
cpp
#include <stdio.h>
#include <string.h>
// 交换函数 - 使用字节操作
void swap(void *a, void *b, size_t size)
{
char temp[size];
memcpy(temp, a, size);
memcpy(a, b, size);
memcpy(b, temp, size);
}
// 快速排序分区函数
int partition(void *base, int low, int high, size_t size,
int (*compar)(const void*, const void*))
{
char *bytes = (char*)base;
void *pivot = bytes + high * size;
int i = low - 1;
for(int j = low; j < high; j++)
{
if(compar(bytes + j * size, pivot) <= 0)
{
i++;
swap(bytes + i * size, bytes + j * size, size);
}
}
swap(bytes + (i + 1) * size, bytes + high * size, size);
return i + 1;
}
// 模拟qsort函数
void my_qsort(void *base, int count, size_t size,
int (*compar)(const void*, const void*))
{
if(count <= 1) return;
// 使用栈模拟递归,避免栈溢出
int stack[count];
int top = -1;
stack[++top] = 0;
stack[++top] = count - 1;
while(top >= 0)
{
int high = stack[top--];
int low = stack[top--];
int pi = partition(base, low, high, size, compar);
if(pi - 1 > low)
{
stack[++top] = low;
stack[++top] = pi - 1;
}
if(pi + 1 < high)
{
stack[++top] = pi + 1;
stack[++top] = high;
}
}
}
// 测试比较函数
int compare_int(const void *a, const void *b)
{
return (*(int*)a - *(int*)b);
}
void print_array(int *arr, int n, const char *message)
{
printf("%s: ", message);
for(int i = 0; i < n; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
int main()
{
int numbers[] = {64, 34, 25, 12, 22, 11, 90, 5, 77, 43};
int count = sizeof(numbers) / sizeof(numbers[0]);
print_array(numbers, count, "排序前");
my_qsort(numbers, count, sizeof(int), compare_int);
print_array(numbers, count, "排序后");
return 0;
}
24. sizeof和strlen的对比
理解两个关键操作符的区别:
cpp
#include <stdio.h>
#include <string.h>
int main()
{
// 情况1: 字符数组
char str1[] = "Hello";
printf("char str1[] = \"Hello\";\n");
printf("sizeof(str1) = %zu (包含空字符)\n", sizeof(str1));
printf("strlen(str1) = %zu (字符串长度)\n\n", strlen(str1));
// 情况2: 字符指针
char *str2 = "Hello";
printf("char *str2 = \"Hello\";\n");
printf("sizeof(str2) = %zu (指针大小)\n", sizeof(str2));
printf("strlen(str2) = %zu (字符串长度)\n\n", strlen(str2));
// 情况3: 数组作为参数
char str3[100] = "Hello";
printf("char str3[100] = \"Hello\";\n");
printf("sizeof(str3) = %zu (数组总大小)\n", sizeof(str3));
printf("strlen(str3) = %zu (字符串长度)\n\n", strlen(str3));
// 情况4: 数组名和指针的区别
printf("数组名和指针的区别:\n");
printf("sizeof(str1) = %zu (数组大小)\n", sizeof(str1));
printf("sizeof(&str1[0]) = %zu (指针大小)\n", sizeof(&str1[0]));
return 0;
}
25. 数组和指针笔试题解析
经典面试题分析:
cpp
#include <stdio.h>
void array_pointer_test()
{
printf("=== 数组和指针笔试题 ===\n\n");
int a[] = {1, 2, 3, 4};
printf("int a[] = {1, 2, 3, 4};\n");
printf("sizeof(a) = %zu\n", sizeof(a)); // 整个数组大小
printf("sizeof(a + 0) = %zu\n", sizeof(a + 0)); // 指针大小
printf("sizeof(*a) = %zu\n", sizeof(*a)); // int大小
printf("sizeof(a + 1) = %zu\n", sizeof(a + 1)); // 指针大小
printf("sizeof(a[1]) = %zu\n", sizeof(a[1])); // int大小
printf("sizeof(&a) = %zu\n", sizeof(&a)); // 指针大小
printf("sizeof(*&a) = %zu\n", sizeof(*&a)); // 整个数组大小
printf("sizeof(&a + 1) = %zu\n", sizeof(&a + 1)); // 指针大小
printf("sizeof(&a[0]) = %zu\n", sizeof(&a[0])); // 指针大小
printf("sizeof(&a[0] + 1) = %zu\n\n", sizeof(&a[0] + 1)); // 指针大小
// 地址分析
printf("地址分析:\n");
printf("a = %p\n", (void*)a);
printf("&a[0] = %p\n", (void*)&a[0]);
printf("&a = %p\n", (void*)&a);
printf("a + 1 = %p\n", (void*)(a + 1));
printf("&a[0] + 1 = %p\n", (void*)(&a[0] + 1));
printf("&a + 1 = %p\n", (void*)(&a + 1));
}
void string_pointer_test()
{
printf("\n=== 字符串指针测试 ===\n\n");
char str[] = "hello";
char *p = str;
printf("char str[] = \"hello\";\n");
printf("char *p = str;\n\n");
printf("sizeof(str) = %zu\n", sizeof(str));
printf("sizeof(p) = %zu\n", sizeof(p));
printf("sizeof(*p) = %zu\n", sizeof(*p));
printf("\n指针运算:\n");
printf("p = %p, *p = '%c'\n", (void*)p, *p);
printf("p + 1 = %p, *(p + 1) = '%c'\n", (void*)(p + 1), *(p + 1));
printf("&p[2] = %p, p[2] = '%c'\n", (void*)&p[2], p[2]);
}
int main()
{
array_pointer_test();
string_pointer_test();
return 0;
}
26. 指针运算笔试题解析
深入理解指针运算:
cpp
#include <stdio.h>
void pointer_arithmetic_test()
{
printf("=== 指针运算笔试题 ===\n\n");
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
printf("int arr[5] = {1, 2, 3, 4, 5};\n");
printf("int *ptr = arr;\n\n");
// 基础指针运算
printf("基础运算:\n");
printf("*ptr = %d\n", *ptr);
printf("*(ptr + 2) = %d\n", *(ptr + 2));
printf("ptr[3] = %d\n", ptr[3]);
// 复杂表达式
printf("\n复杂表达式:\n");
printf("*ptr++ = %d\n", *ptr++); // 先取值,后递增
printf("现在 *ptr = %d\n", *ptr);
printf("*++ptr = %d\n", *++ptr); // 先递增,后取值
printf("现在 *ptr = %d\n", *ptr);
printf("(*ptr)++ = %d\n", (*ptr)++); // 先取值,后值递增
printf("现在 *ptr = %d\n", *ptr);
printf("现在 arr[2] = %d\n", arr[2]);
// 重置指针
ptr = arr;
printf("\n指针比较:\n");
printf("ptr < arr + 4: %d\n", ptr < arr + 4);
printf("ptr == arr: %d\n", ptr == arr);
}
void multi_dimensional_test()
{
printf("\n=== 多维数组指针运算 ===\n\n");
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
printf("int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};\n\n");
printf("matrix = %p\n", (void*)matrix);
printf("matrix[0] = %p\n", (void*)matrix[0]);
printf("&matrix[0][0] = %p\n", (void*)&matrix[0][0]);
printf("\n指针类型分析:\n");
printf("sizeof(matrix) = %zu\n", sizeof(matrix)); // 整个数组
printf("sizeof(matrix[0]) = %zu\n", sizeof(matrix[0])); // 一行
printf("sizeof(matrix[0][0]) = %zu\n", sizeof(matrix[0][0])); // 一个元素
printf("\n地址运算:\n");
printf("matrix + 1 = %p (跳过一行)\n", (void*)(matrix + 1));
printf("matrix[0] + 1 = %p (跳过一个元素)\n", (void*)(matrix[0] + 1));
}
int main()
{
pointer_arithmetic_test();
multi_dimensional_test();
return 0;
}
总结
通过本文的全面解析,我们可以看到指针在C语言中的核心地位和广泛应用。从基础的内存地址概念,到复杂的函数指针和回调机制,指针贯穿了C语言的各个方面。
关键要点总结:
-
指针本质:指针是内存地址的变量化表示,提供了直接操作内存的能力
-
类型安全:指针类型确保了内存访问的正确性和安全性
-
运算灵活性:指针运算使得高效的数据处理成为可能
-
函数抽象:函数指针实现了运行时多态和回调机制
-
内存管理:正确使用指针是有效内存管理的基础
即使是看似与指针无关的算法如冒泡排序,在深入分析后也揭示了与指针的紧密联系。理解指针不仅有助于编写高效的C代码,更是深入理解计算机系统工作原理的重要途径。
指针的学习需要理论与实践相结合,通过不断的练习和思考,才能真正掌握这一强大而灵活的工具。