内存是一个线性的字节数组,每个字节都有唯一的地址。程序运行时,变量存储在内存中,编译器为每个变量分配内存空间。指针就是存储内存地址的变量,通过指针可以直接访问和操作内存。
int *p; // 声明一个指向int类型变量的指针
int a = 10;
p = &a; // 初始化:将a的地址赋值给p
int *q = &a; // 声明并初始化
& 取地址运算符:获取变量的内存地址* 解引用运算符:通过地址访问对应的内存值(void*)0void* 是通用指针类型,可以指向任意类型的数据| 运算符 | 名称 | 作用 | 示例 |
|---|---|---|---|
& |
取地址 | 获取变量地址 | &a |
* |
解引用 | 通过地址访问值 | *p |
-> |
箭头 | 通过指针访问成员 | p->member |
int *p; // 指向int的指针
int **pp; // 指向int指针的指针(二级指针)
int *arr[10]; // 指针数组:包含10个int指针的数组
int (*p)[10]; // 数组指针:指向包含10个int的数组的指针
#include <stdlib.h>
void *malloc(size_t size); // 分配内存
void *calloc(size_t n, size_t size); // 分配并清零
void *realloc(void *ptr, size_t new_size); // 重新分配
void free(void *ptr); // 释放内存
#include <stdio.h>
int main() {
int a = 42;
int *p = &a;
printf("变量a的值: %d\n", a);
printf("变量a的地址: %p\n", (void*)&a);
printf("指针p存储的地址: %p\n", (void*)p);
printf("通过指针解引用访问的值: %d\n", *p);
// 通过指针修改变量值
*p = 100;
printf("修改后a的值: %d\n", a);
// 指针大小
printf("指针大小: %zu字节\n", sizeof(p));
printf("int大小: %zu字节\n", sizeof(int));
return 0;
}
编译命令:gcc -o pointer_basics pointer_basics.c
#include <stdio.h>
#include <stdlib.h>
int main() {
// NULL指针
int *p = NULL;
if (p != NULL) {
printf("p指向的值: %d\n", *p);
} else {
printf("p是空指针,不能解引用\n");
}
// void指针
int a = 10;
double b = 3.14;
void *vp;
vp = &a;
printf("通过void*访问int: %d\n", *(int*)vp);
vp = &b;
printf("通过void*访问double: %f\n", *(double*)vp);
// 动态内存分配中的void*
int *arr = (int*)malloc(5 * sizeof(int));
if (arr == NULL) {
fprintf(stderr, "内存分配失败\n");
return 1;
}
for (int i = 0; i < 5; i++) {
arr[i] = i * 10;
}
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
free(arr);
return 0;
}
编译命令:gcc -o pointer_types pointer_types.c
#include <stdio.h>
int main() {
int a = 100;
int *p = &a;
// 类型匹配赋值
int *q = p; // 正确:相同类型指针
printf("q = %d\n", *q);
// void指针中转
void *vp = p; // 正确:任何类型指针都可以赋给void*
int *r = (int*)vp; // 正确:void*可以赋给任何类型指针
printf("r = %d\n", *r);
// 错误的类型赋值(需要强制转换)
// int *s = (double*)&a; // 警告:类型不匹配
// 指针与0
int *t = 0; // 正确:0可以赋给指针,表示NULL
// int *u = 100; // 错误:非零整数不能赋给指针
printf("t = %p\n", (void*)t);
return 0;
}
编译命令:gcc -o pointer_assignment pointer_assignment.c
Q1: 指针和数组有什么区别? A: 数组是连续内存块,指针是存储地址的变量。数组名在大多数情况下退化为首元素指针,但sizeof和&操作符对数组有特殊行为。数组大小固定,指针可以指向动态分配的内存。
Q2: 什么是野指针?如何避免? A: 野指针是未初始化或指向已释放内存的指针。避免方法:1) 指针声明时初始化为NULL或有效地址;2) 释放内存后将指针设为NULL;3) 使用前检查指针有效性。
Q3: NULL指针和void指针有什么区别? A: NULL指针是特殊的空指针,表示不指向任何内存;void*是通用指针,可以指向任意类型数据,但不能直接解引用。NULL指针主要用于错误检查,void*主要用于泛型编程。
Q4: 为什么指针大小与数据类型无关? A: 指针存储的是内存地址,地址的长度由系统位数决定(32位系统4字节,64位系统8字节)。不同数据类型的指针只是指向的内存大小不同,但存储地址的指针本身大小相同。
Q5: 指针赋值时需要注意什么? A: 1) 类型匹配:相同类型指针可以直接赋值;2) void*可以与其他类型指针相互赋值;3) 不同类型指针赋值需要强制转换;4) 只有0或NULL可以赋给指针表示空指针;5) 不能将非地址整数值赋给指针。
#include <stdio.h>
#include <stdlib.h>
int main() {
// 1. 基本指针操作
int a = 10;
int *p = &a;
printf("a = %d, *p = %d\n", a, *p);
// 2. 通过指针修改值
*p = 20;
printf("修改后: a = %d\n", a);
// 3. 指针大小
printf("指针大小: %zu字节\n", sizeof(p));
// 4. NULL指针
int *np = NULL;
if (np == NULL) {
printf("np是空指针\n");
}
// 5. void指针
void *vp = &a;
printf("通过void*访问: %d\n", *(int*)vp);
// 6. 动态内存分配
int *arr = (int*)malloc(3 * sizeof(int));
if (arr != NULL) {
arr[0] = 100;
arr[1] = 200;
arr[2] = 300;
for (int i = 0; i < 3; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
free(arr);
}
return 0;
}
编译命令:gcc -o pointer_demo pointer_demo.c