#include <stdio.h>
#include <stdlib.h>
// 定义结构体
struct Node {
int data;
struct Node *next;
};
int main() {
// 构建链表
struct Node *head = (struct Node*)malloc(sizeof(struct Node));
head->data = 1;
struct Node *current = head;
for(int i = 2; i <= 5; i++) {
current->next = (struct Node*)malloc(sizeof(struct Node));
current = current->next;
current->data = i;
}
current->next = NULL;
// 使用指针算术运算遍历链表并求和
int sum = 0;
current = head;
while(current != NULL) {
sum += *((int*)current);
current = *((struct Node**)((char*)current + sizeof(int)));
}
printf("Sum of data in nodes: %d\n", sum);
// 释放链表内存
current = head;
struct Node *nextNode;
while(current != NULL) {
nextNode = current->next;
free(current);
current = nextNode;
}
return 0;
}
原理
- 访问
data
成员:通过将 struct Node*
指针强制转换为 int*
指针,利用指针算术运算直接访问 data
成员。因为在结构体中,data
是第一个成员,所以其地址与结构体起始地址相同。
- 访问
next
成员:先将 struct Node*
指针强制转换为 char*
指针,这样可以以字节为单位进行指针偏移。由于 int
类型通常占4个字节(具体取决于系统),通过偏移 sizeof(int)
字节后再将指针转换回 struct Node**
,从而得到 next
成员的地址,再解引用该指针得到下一个节点的地址。
风险
- 可移植性问题:不同系统中
int
类型的大小可能不同,struct Node
内部成员的内存布局也可能因编译器优化而有所差异。这种依赖特定内存布局的指针算术运算在不同平台上可能无法正常工作。
- 代码可读性差:这种方式使得代码难以理解和维护,增加了程序员阅读和修改代码的难度。
- 容易出错:指针算术运算要求对内存布局有精确的了解,一旦计算错误,很容易导致内存访问越界或非法访问,进而引发程序崩溃或未定义行为。