MST

星途 面试题库

面试题:C语言指针算术运算与复杂数据结构

定义一个结构体 `struct Node { int data; struct Node *next; };`,构建一个包含5个节点的单向链表,每个节点的 `data` 依次为1到5。使用指针算术运算遍历链表并计算所有节点 `data` 之和。注意,这里不能使用传统的 `->` 操作符来访问节点成员,只能通过指针算术运算来实现对结构体成员的访问,说明这样做的原理和风险。
48.5万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试
#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;
}

原理

  1. 访问 data 成员:通过将 struct Node* 指针强制转换为 int* 指针,利用指针算术运算直接访问 data 成员。因为在结构体中,data 是第一个成员,所以其地址与结构体起始地址相同。
  2. 访问 next 成员:先将 struct Node* 指针强制转换为 char* 指针,这样可以以字节为单位进行指针偏移。由于 int 类型通常占4个字节(具体取决于系统),通过偏移 sizeof(int) 字节后再将指针转换回 struct Node**,从而得到 next 成员的地址,再解引用该指针得到下一个节点的地址。

风险

  1. 可移植性问题:不同系统中 int 类型的大小可能不同,struct Node 内部成员的内存布局也可能因编译器优化而有所差异。这种依赖特定内存布局的指针算术运算在不同平台上可能无法正常工作。
  2. 代码可读性差:这种方式使得代码难以理解和维护,增加了程序员阅读和修改代码的难度。
  3. 容易出错:指针算术运算要求对内存布局有精确的了解,一旦计算错误,很容易导致内存访问越界或非法访问,进而引发程序崩溃或未定义行为。