指针的声明
c
int a;
int *p
//星号两边的空格无关紧要,下面的声明是等价的
int* p;
int *p;
int * p;
int*p;
例如
c
int main(int argc, char const* argv[]) {
int a = 5;
int* p;
p = &a;
printf("%p\n", &a);
printf("%p\n", p);
printf("%p\n", &p);
return 0;
}
输出
c
000000AD41CFFCF4
000000AD41CFFCF4
000000AD41CFFD18
*p解引用指针
c
int main(int argc, char const* argv[]) {
int a = 5;
int* p = &a;
printf("%d\n", *p);
*p = 100;
printf("%d\n", *p);
return 0;
}
输出
c
5
100
指针和数组
c
int main(int argc, char const* argv[]) {
int a[] = {15,22,67,43,87};
int* p;
p = a;
printf("%p\n", a);
printf("%p\n", p);
printf("%d\n", *p);
return 0;
}
c
000000F81F2FF5B8
000000F81F2FF5B8
15
当把某个数组的地址赋给指针时候,存入的是数组的首地址的值
c
int main(int argc, char const* argv[]) {
int a[] = {15,22,67,43,87};
int* p;
p = a;
for (int i = 0; i < sizeof(a) / sizeof(a[0]);i++){
printf("%d\n", a[i]);
}
printf("\n");
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
printf("%d\n", *(p+i));
}
return 0;
}
c
15
22
67
43
87
15
22
67
43
87
当给指针加上一个整数的时候,实际上加的是这个整数和指针数据类型的对应的字节数的乘积
c
p++
p = p + 1
p = p + 1×4
c
int main(int argc, char const* argv[]) {
int a = 5;
int* p;
p = &a;
printf("%p\n", p);
p++;
printf("%p\n", p);
return 0;
}
c
000000F2612FFC84
000000F2612FFC88

指针与函数
c
void swap(int* a, int* b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void swap1(int a, int b) {
int temp;
temp = a;
a = b;
b = temp;
}
void swap2(int *a, int *b) {
int *temp;
temp = a;
a = b;
b = temp;
}
int main(int argc, char const* argv[]) {
int a = 5;
int b = 10;
swap1(a, b);
printf("%d %d\n", a, b);
swap2(&a, &b);
printf("%d %d\n", a, b);
swap(&a, &b);
printf("%d %d\n", a, b);
return 0;
}
c
5 10
5 10
10 5
swap1(值传递):交换的是变量副本 → 无效
swap2(指针传递):交换的是指针本身 → 无效
swap(正确写法):传地址 + 解引用 → 交换变量原始值 → 有效
结构体
相当于自己造一个数据类型
c
#include<stdio.h>
#include<string.h>
struct student {
char stuId[20];
char stuName[20];
char sex;
char hometown[20];
char major[30];
};
int main(int argc, char const* argv[]) {
int a;
struct student b;
strcpy(b.stuId, "20389488");
strcpy(b.stuName , "小明");
strcpy(b.hometown , "中国");
strcpy(b.major, "核物理");
b.sex = '1';
printf("%s,%s,%c,%s,%s\n", b.stuId, b.stuName, b.sex, b.major, b.hometown);
return 0;
}
c
20389488,小明,1,核物理,中国
比如从数据库中读取了n个学生数据,把这些数据分别存进这个student数据类型中,然后每一个变量里面都带着这些人的信息了
c
#include<stdio.h>
#include<string.h>
struct student {
char stuId[20];
char stuName[20];
char sex;
char hometown[20];
char major[30];
};
int main(int argc, char const* argv[]) {
int a;
struct student b[4];
strcpy(b[0].stuId, "20389488");
strcpy(b[0].stuName, "小康");
strcpy(b[0].hometown, "中国");
strcpy(b[0].major, "核物理");
b[0].sex = '1';
strcpy(b[1].stuId, "20389488");
strcpy(b[1].stuName, "小名");
strcpy(b[1].hometown, "中国");
strcpy(b[1].major, "核物理");
b[0].sex = '1';
strcpy(b[2].stuId, "20389488");
strcpy(b[2].stuName, "小化");
strcpy(b[2].hometown, "中国");
strcpy(b[2].major, "核物理");
b[0].sex = '1';
strcpy(b[3].stuId, "20389488");
strcpy(b[3].stuName, "小四");
strcpy(b[3].hometown, "中国");
strcpy(b[3].major, "核物理");
b[0].sex = '1';
for (int i = 0; i < 4; i++) {
printf("%s,%s,%c,%s,%s\n", b[i].stuId, b[i].stuName, b[i].sex, b[i].major, b[i].hometown);
}
return 0;
}
c
20389488,小康,1,核物理,中国
20389488,小名,1,核物理,中国
20389488,小化,1,核物理,中国
20389488,小四,1,核物理,中国
类型定义
c
typedef struct {
char stuId[20];
char stuName[20];
char sex;
char hometown[20];
char major[30];
}student;//给结构体起别名叫student
int main(int argc, char const* argv[]) {
int a;
student b;//这里就不用struct student b,直接就可以省去struct
strcpy(b.stuId, "20389488");
strcpy(b.stuName, "小康");
strcpy(b.hometown, "中国");
strcpy(b.major, "核物理");
b.sex = '1';
printf("%s,%s,%c,%s,%s\n", b.stuId, b.stuName, b.sex, b.major, b.hometown);
return 0;
}
结构体指针
c
typedef struct {
char stuId[20];
char stuName[20];
char sex;
}student;//给结构体起别名叫student
void initialStu(student* p) {
strcpy((*p).stuId, "20389488");
strcpy((*p).stuName, "小康");
(*p).sex = '1';
}
int main(int argc, char const* argv[]) {
student b;
initialStu(&b);
printf("%s,%s,%c\n", b.stuId, b.stuName, b.sex);
return 0;
}
c
20389488,小康,1
传递参数这个事,为什么喜欢传指针而不去传结构体本身,传的是&s而不是s,这里是有一个内存空间节省的事
结构体 s = 一整本厚厚的书(里面有很多数据:名字、年龄、学号、成绩......)
传结构体本身 s = 把整本书复印一遍,把复印件递给函数
传结构体指针 &s = 只告诉函数:这本书放在【0x123456】这个位置
函数会完整拷贝一份结构体的所有数据,放到函数的栈内存中。
结构体越大,拷贝占用的内存越多!
例:一个学生结构体
c
struct Student {
char name[20]; // 20字节
int age; // 4字节
int id; // 4字节
float score; // 4字节
};
这个结构体总大小 = 32 字节,传值就要拷贝 32 字节到函数里。
如果是嵌套数组、大结构体(比如 1024 字节),传值就要拷贝 1024 字节!(当然这里结构体可能不是32字节,因为有一个叫内存对齐,可能不止32字节)
传指针(传地址 &s)
无论结构体多大,只传递一个「内存地址」:
32 位系统:地址占 4 字节
64 位系统:地址占 8 字节
哪怕结构体是 1MB、1GB,传指针永远只占 4/8 字节!
原因是:
内存拷贝是需要CPU 耗时的:
传结构体本身:CPU 要逐字节复制整个结构体,数据越大越慢;
传指针:CPU 只需要复制一个地址,瞬间完成。
如果函数被频繁调用(比如循环 10000 次),传值会让程序明显卡顿,传指针几乎无性能损耗
函数的栈空间是有限的(一般只有几 MB),如果传一个超大结构体(比如嵌套了大数组):
传值拷贝 → 瞬间占满栈空间 → 程序直接崩溃(栈溢出);
传指针 → 仅占 8 字节 → 永远不会栈溢出。
代码对比:
c
#include <stdio.h>
struct Student { char name[20]; int age; };
// 1. 传结构体本身(传值:拷贝整个结构体,费内存、改不动原数据)
void fun1(struct Student s) {
s.age = 20; // 改的是副本!
}
// 2. 传结构体指针(传地址:仅8字节,省内存、直接改原数据)
void fun2(struct Student *s) {
s->age = 20; // 改的是原件!
}
int main() {
struct Student s = {"张三", 18};
fun1(s); // 拷贝32字节,原age还是18
fun2(&s); // 拷贝8字节,原age变成20
printf("%d", s.age); // 输出 20
return 0;
}