MST

星途 面试题库

面试题:C语言中数组名与指针在复杂数据结构和动态内存分配下的深层关系

假设有一个结构体`struct Node { int data[10]; struct Node *next; };`,现在通过动态内存分配创建一个链表,每个节点的`data`数组需要存储不同的数据。在这个场景下,分析数组名(如`node->data`)与指针(例如用于遍历链表的`next`指针以及可能用于操作`data`数组元素的指针)之间的复杂关系。考虑如何在链表的插入、删除操作中正确处理`data`数组与指针的关系,避免内存泄漏和指针错误。并编写相应的代码示例,详细注释每一步对数组名和指针的操作及其意义。
28.2万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试
  1. 数组名与指针的关系分析

    • node->data:这里的node是指向struct Node结构体的指针,node->data是结构体中名为data的数组。数组名在大多数情况下会被隐式转换为指向其首元素的指针。所以node->data可以当作一个int*类型的指针,指向data数组的第一个元素data[0]
    • next指针:struct Node *next是链表节点中用于指向下一个节点的指针。通过这个指针,我们可以遍历整个链表。
    • 操作data数组元素的指针:如果我们要操作data数组中的元素,可以通过node->data转换得到的指针来进行。例如,*(node->data + i)等价于node->data[i],这里i是数组的索引。
  2. 链表插入、删除操作中处理数组名与指针关系,避免内存泄漏和指针错误

    • 插入操作
      • 当插入一个新节点时,需要为新节点分配内存,并正确设置新节点的next指针,以及data数组中的数据。要确保新节点的next指针指向正确的后续节点,并且不会覆盖任何现有节点的next指针,直到新节点完全插入。
    • 删除操作
      • 当删除一个节点时,需要先保存被删除节点的next指针,以便后续继续遍历链表。然后释放被删除节点的内存。如果不保存next指针,会导致链表断裂,无法继续遍历。同时,由于data数组是结构体的一部分,释放节点内存时,data数组占用的内存也会被正确释放,不需要额外处理。
  3. 代码示例

#include <stdio.h>
#include <stdlib.h>

// 定义结构体
struct Node {
    int data[10];
    struct Node *next;
};

// 创建新节点
struct Node* createNode(int newData[10]) {
    struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
    if (newNode == NULL) {
        printf("内存分配失败\n");
        return NULL;
    }
    // 将传入的新数据复制到新节点的data数组中
    for (int i = 0; i < 10; i++) {
        newNode->data[i] = newData[i];
    }
    newNode->next = NULL;
    return newNode;
}

// 在链表头部插入新节点
struct Node* insertAtHead(struct Node *head, int newData[10]) {
    struct Node *newNode = createNode(newData);
    if (newNode == NULL) {
        return head;
    }
    newNode->next = head;
    return newNode;
}

// 删除链表头部节点
struct Node* deleteAtHead(struct Node *head) {
    if (head == NULL) {
        return NULL;
    }
    struct Node *temp = head;
    head = head->next;
    free(temp);
    return head;
}

// 打印链表中每个节点的data数组
void printList(struct Node *head) {
    struct Node *current = head;
    while (current != NULL) {
        printf("Node data: ");
        for (int i = 0; i < 10; i++) {
            printf("%d ", current->data[i]);
        }
        printf("\n");
        current = current->next;
    }
}

// 释放整个链表的内存
void freeList(struct Node *head) {
    struct Node *current = head;
    struct Node *nextNode;
    while (current != NULL) {
        nextNode = current->next;
        free(current);
        current = nextNode;
    }
}

int main() {
    struct Node *head = NULL;
    int data1[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int data2[10] = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20};

    head = insertAtHead(head, data1);
    head = insertAtHead(head, data2);

    printList(head);

    head = deleteAtHead(head);
    printList(head);

    freeList(head);
    return 0;
}

代码注释说明:

  • createNode函数
    • 使用malloc为新节点分配内存。
    • 检查内存分配是否成功,如果失败,打印错误信息并返回NULL
    • 通过循环将传入的newData数组中的数据复制到新节点的data数组中。
    • 将新节点的next指针初始化为NULL
  • insertAtHead函数
    • 调用createNode创建新节点。
    • 如果新节点创建失败,返回原链表头指针。
    • 将新节点的next指针指向原链表头节点。
    • 返回新的链表头指针。
  • deleteAtHead函数
    • 检查链表是否为空,如果为空,直接返回NULL
    • 使用临时指针temp保存原链表头节点。
    • 将链表头指针移动到下一个节点。
    • 释放原链表头节点的内存。
    • 返回新的链表头指针。
  • printList函数
    • 使用current指针遍历链表。
    • 循环打印每个节点data数组中的元素。
  • freeList函数
    • 使用currentnextNode指针遍历链表并释放每个节点的内存。