当前位置: 首页 > news >正文

C语言指针全面解析:从内存管理到高级应用

前言

        指针是C语言的灵魂,也是最具挑战性的核心概念。理解指针不仅关乎语法掌握,更关系到对计算机内存模型的深刻认知。本文将系统性地解析指针的各个方面,即使是看似与指针无关的冒泡排序,我们也会揭示其与指针的内在联系。

目录

前言

正文

1. 内存和地址

2. 指针变量和地址

3. 指针变量类型的意义

4. const修饰指针

5. 指针运算

6. 野指针

7. assert断言

8. 指针的使用和传址调用

9. 数组名的理解和使用指针访问数组

10. 一维数组传参的本质

11. 冒泡排序的指针分析

12. 二级指针

13. 指针数组

14. 指针数组模拟二维数组

15. 字符指针变量

16. 数组指针变量

17. 二维数组传参的本质

18. 函数指针变量

19. 函数指针数组

20. 转移表

21. 回调函数

22. qsort使用举例

23. qsort函数的模拟实现

24. sizeof和strlen的对比

25. 数组和指针笔试题解析

26. 指针运算笔试题解析

总结


正文

1. 内存和地址

内存本质:计算机内存是由无数个以字节为单位内存单元组成的线性空间,每个单元都有唯一的地址标识。

#include <stdio.h>int main() 
{int a = 10;printf("变量a的值: %d\n", a);printf("变量a的地址: %p\n", &a);// 内存地址的十六进制表示// 例如:0x7ffd42a1b23creturn 0;
}

关键理解:变量名是给程序员使用的标签,编译器会将其转换为内存地址。

2. 指针变量和地址

指针变量专门用于存储内存地址。

#include <stdio.h>int main() 
{int num = 42;int *ptr = &num;  // 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. 指针变量类型的意义

指针类型决定了:

  • 解引用时访问的字节数

  • 指针运算的步长

#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与指针的三种组合方式:

#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. 指针运算

指针支持多种运算操作:

#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. 野指针

野指针指向无效内存区域,是常见错误来源:

#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);  // 未定义行为// 正确做法:释放后置为NULLptr = NULL;return 0;
}

7. assert断言

assert用于调试阶段检查假设条件:

#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. 指针的使用和传址调用

理解值传递与地址传递的区别:

#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. 数组名的理解和使用指针访问数组

数组名在多数情况下退化为指针:

#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. 一维数组传参的本质

数组传参时退化为指针:

#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. 冒泡排序的指针分析

冒泡排序与指针的深度结合:

#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. 二级指针

指向指针的指针:

#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. 指针数组

元素为指针的数组:

#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. 指针数组模拟二维数组

用指针数组实现类似二维数组的功能:

#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. 字符指针变量

字符指针的特殊用法:

#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. 数组指针变量

指向整个数组的指针:

#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. 二维数组传参的本质

二维数组参数传递的真相:

#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. 函数指针变量

指向函数的指针:

#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. 函数指针数组

管理多个相关函数:

#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. 转移表

使用函数指针数组实现跳转表:

#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. 回调函数

回调函数的原理和应用:

#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函数的使用:

#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的工作原理:

#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的对比

理解两个关键操作符的区别:

#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. 数组和指针笔试题解析

经典面试题分析:

#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. 指针运算笔试题解析

深入理解指针运算:

#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语言的各个方面。

关键要点总结

  1. 指针本质:指针是内存地址的变量化表示,提供了直接操作内存的能力

  2. 类型安全:指针类型确保了内存访问的正确性和安全性

  3. 运算灵活性:指针运算使得高效的数据处理成为可能

  4. 函数抽象:函数指针实现了运行时多态和回调机制

  5. 内存管理:正确使用指针是有效内存管理的基础

        即使是看似与指针无关的算法如冒泡排序,在深入分析后也揭示了与指针的紧密联系。理解指针不仅有助于编写高效的C代码,更是深入理解计算机系统工作原理的重要途径。

指针的学习需要理论与实践相结合,通过不断的练习和思考,才能真正掌握这一强大而灵活的工具。

http://www.dtcms.com/a/452806.html

相关文章:

  • 南通网站建设推广专家建站教程的优点
  • Spring Boot整合Apache Shiro权限认证框架(应用篇)
  • 杰理AC632N---RTC应用问题
  • 网站免费软件下载阳江人社局官网招聘
  • 第二十三章:解析天书,诠释法则——Interpreter的解释艺术
  • 论文阅读-FoundationStereo
  • bug日记
  • 大数据集群环境搭建(Ubantu)
  • 深入浅出 HarmonyOS 应用开发:ArkTS 语法精要与实践
  • 用 Python+OpenCV 实现实时文档扫描:从摄像头捕捉到透视矫正全流程
  • 普陀做网站公司任丘市建设局网站
  • 前端框架篇——VueReact篇
  • R语言从入门到精通Day4之【数据结构】
  • JavaScript快速入门_javascript入门教程
  • 有几家做网站的公司易贝跨境电商平台
  • 基于websocket的多用户网页五子棋(六)
  • 月光与饼:Python 爱情月饼可视化
  • 【C++】STL有序关联容器的双生花:set/multiset 和 map/multimap 使用指南
  • 迷你论坛项目
  • 【C++STL】一文掌握 String 核心接口:从基础到实用!
  • 长沙宁乡建设网站网站本地环境搭建
  • 从以太网到多个 CAN 网络的网关
  • 网站做弹窗怀化职院网站
  • ros2 功能包 package.xml 结构详细解释
  • ros2 功能包 CMakeLists.txt 结构详细解释
  • 【Python】小练习-考察变量作用域问题
  • YOLO算法原理详解系列 第007期-YOLOv7 算法原理详解
  • 【C++贪心】P8087 『JROI-5』Interval|普及+
  • C++知识点总结用于打算法
  • 【算法】二分查找(一)朴素二分