C语言 简单学习 指针
指针的概念:
是变量 存储内存地址 可以通过指针修改内存地址对应 单元中的数据
内存地址:
简单理解 门牌号 ,
内存是一条街 内存地址就是门牌号 内存单元就是门牌号对应的房子
用途:
- 直接获取 或者 修改内存中数据
- 动态分配和释放内存
- 通过指针可以传递大块数据地址
- 实现链表 树等数据结构的基础
声明:
类型 *指针变量名 (* 表示这是一个指针变量)
初始化:
可以通过&指定某个变量的地址
int a=8; //声明并初始化a=8
int *p=&a;//声明并指向变量a的地址
指针和数组的关系:
数组本身就是指向数组第一个元素的指针
int a[5]={0,1,2,3,4};
int *p=a; //声明并指向数组的第一个元素
指针与数组的运算:
1.加减运算 移动 指针的位置
int a[5]={0,1,2,3,4};
int *p=a; //声明并指向数组的第一个元素
加减运算 移动 指针的位置
*(p+1)指向数组的第二个元素
*(p+2)指向数组的第三个元素
2.交换两个变量的值
void swap(int *a, int *b) {int temp = *a; //先把指针a指向地址单元中的数据取出到局部变量中*a = *b;//把a指向的地址中数据 更改为 b指向地址数据 *b = temp;//将b指向的地址数据更改为temp中储存的数据
}本质上指针指向 没有更改 只是数据的传递
3.遍历数组
int a[5]={0,1,2,3,4};
int *p=a; //声明并指向数组的第一个元素
for(int x=0;x<5;x++)
{*(p+x) //从第一个元素到最后一个元素 遍历
}
动态分配和释放内存
4.动态内存分配
#include <stdio.h>://包含标准输入输出库,用于输入输出操作。
#include <stdlib.h>://包含标准库函数,用于动态内存分配和释放。
int main()
{
int n;//声明一个整数变量 n,用于存储用户输入的数组长度。
printf("请输入数组长度: ");//提示用户输入数组长度。
scanf("%d", &n);//从标准输入读取一个整数,并存储在 n 中。int *arr = (int *)malloc(n * sizeof(int));//使用 malloc 函数动态分配一块内存,
//大小为 n * sizeof(int) 字节。
//malloc 返回一个指向这块内存的指针,类型为 void *,需要强制转换为 int *。if (arr == NULL)
{
//检查内存是否成功分配。
//如果 malloc 返回 NULL,表示内存分配失败。
printf("内存分配失败\n");//打印错误信息。
}return 1;:返回 1,表示程序异常退出。}
scanf的用法(
scanf
是 C 语言中用于从标准输入(通常是键盘)读取数据的函数)int scanf(const char *format, ...);
format
是一个格式化字符串,包含格式说明符和其他字符。(例如:
%d
或%i
: 读取十进制整数。)
...
是可变参数,通常是一些变量的地址,用于存储读取到的数据。
malloc
是一个用于动态分配内存的函数。(void *malloc(size_t size);)
size_t size
:需要分配的内存量,以字节为单位。- 返回值:成功时返回一个指向分配内存的指针,失败时返回
NULL
sizeof
是一个运算符,用于获取数据类型或变量的大小(以字节为单位)
sizeof(int)
返回int
类型的大小(通常是 4 字节)。sizeof(float)
返回float
类型的大小(通常是 4 字节)。sizeof(char)
返回char
类型的大小(通常是 1 字节)。
int *arr = (int *)malloc( n * sizeof(int) );
定义一个指针arr , (int *) <<这是一个类型转换操作
C语言中,
void *
类型的指针可以隐式转换为任何其他类型的指针。也就是说,即使你不进行显式的类型转换,代码也是可以编译和运行的。malloc 是如此定义的 void *malloc(size_t size);
显式的类型转换可以增加代码的可读性和明确性,更容易理解意图将 malloc 返回的指针转换为 int * 类型(int *) 仅用于指针
如果非指针使用
例: char a='A'; int b=(int)a; //正确做法
free
函数用于释放之前通过malloc
、calloc
、realloc
等函数分配的动态内存。释放内存是一个重要的步骤,可以防止内存泄漏,确保程序的高效运行。
函数原型 void free(void *ptr);
ptr
:指向之前分配的内存的指针。
动态分配的过程
- 分配内存:使用
malloc
、calloc
或realloc
分配内存。 - 使用内存:对分配的内存进行读写操作。
- 释放内存:使用
free
释放分配的内存
通过指针可以传递大块数据地址
1.避免数据复制:
传递大块数据时,如果直接传递数据本身,会导致数据的复制,消耗大量的内存和时间。通过传递数据的地址,只需传递一个指针,大大减少了内存开销和时间开销。
2.修改原始数据:
通过指针可以修改原始数据,这对于需要在函数中修改数据的情况非常有用。
传递结构体的地址(传递结构体的地址,而不是复制整个结构体)
#include <stdio.h> #include <string.h>// 定义结构体 typedef struct {char name[50];int age; } Person;// 函数声明 void printPerson(Person *p);int main() {Person person = {"Alice", 30};// 传递结构体的地址 printPerson(&person);return 0; }// 函数定义 void printPerson(Person *p) {printf("Name: %s, Age: %d\n", p->name, p->age); }
int n = sizeof(arr) / sizeof(arr[0]);
int arr[] = {1, 2, 3, 4, 5};//声明一个数组int n = sizeof(arr) / sizeof(arr[0]);
// 获取数组元素个数
//sizeof(arr)= n(个数)x sizeof(int)
//sizeof(arr[0]) 计算数组中第一个元素 占用 1x sizeof(int)也就是说
sizeof(arr)=5x4=20
sizeof(arr[0])=1x4=4
20/4=5
n=5 表示现在有5个元素
实现链表 树等数据结构的基础
解释
- 定义节点结构:
Node
结构包含一个整数data
和一个指向下一个节点的指针next
。- 创建新节点:
createNode
函数分配内存并初始化节点。- 插入节点:
insertAtEnd
函数将新节点插入到链表的末尾。- 打印链表:
printList
函数遍历链表并打印每个节点的数据。- 释放链表:
freeList
函数释放链表中所有节点的内存。- 主函数:在主函数中创建链表,插入节点,打印链表,最后释放链表。
#include <stdio.h>
#include <stdlib.h>//定义一个节点结构,包含数据和指向下一个节点的指针。
typedef struct Node {int data;struct Node *next;
} Node;//创建一个新节点的函数,用于分配内存并初始化节点。
Node *createNode(int data) {Node *newNode = (Node *)malloc(sizeof(Node));if (newNode == NULL) {printf("内存分配失败\n");exit(1);}newNode->data = data;newNode->next = NULL;return newNode;
}//插入节点到链表的末尾。
void insertAtEnd(Node **head, int data) {Node *newNode = createNode(data);if (*head == NULL) {*head = newNode;} else {Node *current = *head;while (current->next != NULL) {current = current->next;}current->next = newNode;}
}
//遍历链表并打印每个节点的数据。
void printList(Node *head) {Node *current = head;while (current != NULL) {printf("%d -> ", current->data);current = current->next;}printf("NULL\n");
}
//释放链表中所有节点的内存。
void freeList(Node *head) {Node *current = head;while (current != NULL) {Node *next = current->next;free(current);current = next;}
}int main() {Node *head = NULL;// 插入节点 insertAtEnd(&head, 1);insertAtEnd(&head, 2);insertAtEnd(&head, 3);// 打印链表 printList(head);// 释放链表 freeList(head);return 0;
}