黑马程序员 C语言-----指针学习

---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------         


   指针

     只有传递地址在函数内修改形参的值才会影响到外面实参的值

     因为要传入的是一个地址,所以只能用能够接收地址的变量来接收传入的地址

     而指针就是专门用于接收(保存)地址的

     指针变量定义格式:

     变量类型* 变量名;

      int* p;

     变量类型:将来可以保存什么类型的变量的地址

     * : 仅仅是一个象征(标志), 象征着这是一个指针变量

     变量名: 区分和标记一个变量

指针变量是专门用来保存地址的,不能给指针变量赋值一个常量

        int *temp=13;//错误写法

定义指针变量的时候,写的什么类型,将来指针变量就只能指向(保存)什么类型的变量(地址)

指针变量没有初始化,不要间接访问其存储空间

指针变量也可以定义的时候初始化

     如果要定义一个指针保存一个变量的地址, 把变量的类型拷贝过来,然后加上一颗星

     如果要定义个指针保存另一个指针的地址,把指针的类型和星拷贝过来,然后加上一颗星

     int i =10;

     int *ip = &i;

     int **ipp = &ip;

     int ***ippp = &ipp;


指针与数组

数据类型都有三个共同的特征

  1.可以用来定义变量

  2.可以用来作为函数的参数

     3.可以用来作为函数的返回

数组作为函数参数的时候会自动转换为指针类型, 8个字节

数组元素的访问方式总结:

     1.数组名[下标];

     2.指针变量名[下标];

     3.*(指针变量名 + i);

 数组的地址就是所占用存储空间中字节地址最小的直接的地址


   数组名就是数组的地址

注意指针是可以进行加法运算的但是和普通变量的加法运输不一样

       指针变量加1是加指针变量类型所占用存储空间的字节数


指针与字符串


接收字符串的两种方式

1>用数组来接收一个字符串,这个字符串是一个可变字符串

char name[]="itcast";

2>用指针来接收一个字符串,这个字符串是一个字符串常量

char *name="itcast";

定义字符串的两种方式比较:

     1.利用数组

    

     特点:字符串中的内容是可以修改的

     使用场合:字符串的内容需要经常修改的时候用数组

     

     2.利用指针

     

     特点:字符串中的内容是不可以修改的

     使用场合:字符串中的内容不需要修改,或者字符串需要经常使用(性能比较高)

注意点: 不要返回数组保存的字符串给函数调用者,因为当函数执行完毕,数组就释放了

把指向函数的指针作为函数的参数提高了代码的可扩展性


     变量类型 *变量名;

     

     没有返回值没有参数的

     有返回值没有参数的

     有返回值有参数的

     

     void (*pointer)();

     (*pointer) 是固定写法,代表指针变量pointer将来肯定是指向一个函数

     左边的void 代表指针变量将来要指向的函数没有返回值

     右边的() 代表指针变量将来要指向的函数没有形参

       p = test;

   指针变量指向了test函数

注意:给指向函数的指针赋值,一定不要在函数名称后面加上括号,加上括号代表着将函数的返回值赋值给指针变量


---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ---------------------- 

<think>我们正在讨论C语言链表实现与操作。根据提供的引用,我们可以总结链表的基本概念和结构。 引用[1]和[2]介绍了链表的基本结构:链表由节点组成,每个节点包含数据域和指针域(指向下一个节点)。引用[1]中展示了一个有序链表的结构,包含头指针、尾指针和链表大小。引用[3]给出了一个学生管理系统中链表节点的结构体定义。 引用[4]则展示了一个链表应用的例子(约瑟夫环问题)。 用户问题:想学习C语言中链表的实现和操作。 因此,我们将按照以下步骤讲解: 1. 链表的基本概念 2. 链表节点的定义 3. 链表的初始化(以引用[1]中的有序链表为例) 4. 链表的基本操作(创建、插入、删除、遍历等) 5. 一个简单的链表应用示例 注意:引用中提供的代码片段可能存在错误或不完整,我们将修正并完善。 步骤1:链表的基本概念 链表是一种动态数据结构,通过指针将一系列节点连接起来。每个节点包含两部分:数据域(存储数据)和指针域(存储下一个节点的地址)。 步骤2:链表节点的定义 通常,我们使用结构体来定义链表节点,如引用[3]所示: ```c struct Node { int data; // 数据域,这里以int为例,可以是任意类型 struct Node *next; // 指针域,指向下一个节点 }; ``` 步骤3:链表的初始化 我们可以创建一个链表,并初始化头节点(有时也称为哨兵节点)。引用[1]中展示了一个有序链表的初始化函数,我们稍作调整: ```c #include <stdio.h> #include <stdlib.h> #define LEN sizeof(struct Node) // 定义节点长度 struct Node { int data; struct Node *next; }; // 定义链表结构(类似引用[1]中的OrderedList) struct LinkedList { struct Node *head; // 头指针 struct Node *tail; // 尾指针(在单链表中,尾指针可以方便尾部插入) int size; // 链表长度 }; // 初始化链表 int InitList(struct LinkedList *list) { // 创建头节点(哨兵节点) list->head = (struct Node*)malloc(LEN); if (list->head == NULL) { return 0; // 内存分配失败 } // 初始时,链表为空,头节点的next指向NULL list->head->next = NULL; list->tail = list->head; // 尾指针指向头节点 list->size = 0; return 1; } ``` 步骤4:链表的基本操作 4.1 插入节点(尾部插入) 在链表尾部插入新节点,并更新尾指针。 ```c // 尾部插入新节点 void Append(struct LinkedList *list, int data) { // 创建新节点 struct Node *newNode = (struct Node*)malloc(LEN); newNode->data = data; newNode->next = NULL; // 将新节点连接到尾节点后面 list->tail->next = newNode; list->tail = newNode; // 更新尾指针 list->size++; } ``` 4.2 遍历链表 从头节点后的第一个节点开始,依次访问每个节点。 ```c void Traverse(struct LinkedList *list) { struct Node *p = list->head->next; // 跳过头节点(哨兵节点) while (p != NULL) { printf("%d ", p->data); p = p->next; } printf("\n"); } ``` 4.3 在指定位置插入节点(例如在第pos个位置插入) 注意:pos从1开始,表示插入后成为第pos个节点(不包括头节点) ```c // 在位置pos插入节点(pos从1开始) int Insert(struct LinkedList *list, int pos, int data) { if (pos < 1 || pos > list->size + 1) { return 0; // 位置不合法 } // 找到插入位置的前一个节点 struct Node *p = list->head; for (int i = 1; i < pos; i++) { // 移动pos-1次 p = p->next; } // 创建新节点 struct Node *newNode = (struct Node*)malloc(LEN); newNode->data = data; newNode->next = p->next; p->next = newNode; // 如果插入在尾部,更新尾指针 if (p == list->tail) { list->tail = newNode; } list->size++; return 1; } ``` 4.4 删除指定位置的节点 ```c // 删除位置pos的节点 int Delete(struct LinkedList *list, int pos) { if (pos < 1 || pos > list->size) { return 0; // 位置不合法 } // 找到要删除节点的前一个节点 struct Node *p = list->head; for (int i = 1; i < pos; i++) { p = p->next; } struct Node *temp = p->next; // 要删除的节点 p->next = temp->next; // 如果删除的是尾节点,更新尾指针 if (temp == list->tail) { list->tail = p; } free(temp); list->size--; return 1; } ``` 步骤5:释放链表 使用完毕后,释放所有节点(包括头节点)。 ```c void DestroyList(struct LinkedList *list) { struct Node *p = list->head; while (p != NULL) { struct Node *temp = p; p = p->next; free(temp); } list->head = NULL; list->tail = NULL; list->size = 0; } ``` 步骤6:一个简单的应用示例 我们可以创建一个链表,插入一些数据,然后遍历和删除。 ```c int main() { struct LinkedList list; if (!InitList(&list)) { printf("初始化链表失败\n"); return 1; } // 尾部插入三个节点 Append(&list, 1); Append(&list, 2); Append(&list, 3); Traverse(&list); // 输出:1 2 3 // 在位置2插入4 Insert(&list, 2, 4); Traverse(&list); // 输出:1 4 2 3 // 删除位置3的节点 Delete(&list, 3); Traverse(&list); // 输出:1 4 3 DestroyList(&list); // 释放链表 return 0; } ``` 以上是一个简单的链表实现和操作教程。注意,这里我们使用了带头节点的链表(哨兵节点),这样可以在处理插入和删除时避免对头指针的特殊处理。 根据引用[4],链表还可以用于解决约瑟夫环问题(类似于引用[4]中的徒弟退局问题)。由于篇幅限制,这里不再展开,但读者可以尝试用上述链表实现约瑟夫环问题。 §§相关问题§§ 1. 如何用链表实现约瑟夫环问题? 2. 链表和数组在性能上有哪些区别? 3. 如何实现链表的反转? 4. 链表中的头节点(哨兵节点)有什么作用?</think>### C语言链表实现与操作教程 链表是一种动态数据结构,通过指针将零散的内存块串联起来。以下是基于黑马程序员教程的链表实现详解: #### 一、链表基本结构 ```c #include <stdio.h> #include <stdlib.h> // 定义链表节点结构体 struct Node { int data; // 数据域 struct Node *next; // 指针域(指向下一个节点) }; ``` - **数据域**:存储实际数据(如`int`、`char`或自定义结构体) - **指针域**:存储下一个节点的内存地址 - **头指针**:指向链表第一个节点(关键标识)[^2] #### 二、核心操作实现 1. **创建节点** ```c struct Node* createNode(int data) { struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); if (newNode == NULL) { printf("内存分配失败!"); exit(1); } newNode->data = data; newNode->next = NULL; return newNode; } ``` 2. **头部插入** ```c void insertAtHead(struct Node** head, int data) { struct Node* newNode = createNode(data); newNode->next = *head; *head = newNode; // 更新头指针 } ``` 3. **尾部插入** ```c void insertAtTail(struct Node** head, int data) { struct Node* newNode = createNode(data); if (*head == NULL) { *head = newNode; return; } struct Node* temp = *head; while (temp->next != NULL) { temp = temp->next; } temp->next = newNode; } ``` 4. **删除节点** ```c void deleteNode(struct Node** head, int key) { struct Node *temp = *head, *prev = NULL; // 删除头节点 if (temp != NULL && temp->data == key) { *head = temp->next; free(temp); return; } // 查找待删除节点 while (temp != NULL && temp->data != key) { prev = temp; temp = temp->next; } if (temp == NULL) return; // 未找到 prev->next = temp->next; // 跳过被删节点 free(temp); // 释放内存 } ``` 5. **遍历链表** ```c void printList(struct Node* node) { while (node != NULL) { printf("%d -> ", node->data); node = node->next; } printf("NULL\n"); } ``` #### 三、完整示例(学生管理系统节点) ```c // 学生结构体(引用[3]扩展) struct Student { char name[50]; int age; long studentID; float gpa; struct Student* next; // 链表指针 }; // 初始化链表(引用[1]优化) void initStudentList(struct Student** head) { *head = NULL; // 初始化为空链表 } // 添加学生记录 void addStudent(struct Student** head, const char* name, int age, long id, float gpa) { struct Student* newStudent = (struct Student*)malloc(sizeof(struct Student)); strcpy(newStudent->name, name); newStudent->age = age; newStudent->studentID = id; newStudent->gpa = gpa; newStudent->next = *head; *head = newStudent; } ``` #### 四、链表特性分析 1. **动态内存**:运行时分配内存,避免静态数组的大小限制 2. **高效插入/删除**:时间复杂度$O(1)$(头部操作)或$O(n)$(随机位置) 3. **内存开销**:每个节点需额外存储指针(通常4/8字节) 4. **应用场景**: - 动态数据集合(如学生管理系统[^3]) - 需要频繁插入/删除的场景 - 实现队列、栈等高级数据结构 - 约瑟夫环问题等算法(引用[4]) #### 五、调试技巧 ```c // 检查内存泄漏(开发阶段必备) void freeList(struct Node** head) { struct Node* current = *head; struct Node* next; while (current != NULL) { next = current->next; free(current); current = next; } *head = NULL; // 防止野指针 } ``` > **关键提示**:链表操作后务必更新头指针;每次`malloc`必须对应`free`防止内存泄漏[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值