指针,结构体,动态内存分配

指针的声明

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;
}
相关推荐
慧都小项1 小时前
C/C++ 静态分析为什么要前移?Parasoft C/C++test 在 IDE 与 CI/CD 中的应用
c语言·c++·ide·功能测试·ai
qq_33776320191 小时前
国际期货资管系统开发方案|内外盘交易平台源码授权与二次开发支持
java·c语言·开发语言·c++·c#
openKylin14 小时前
与全球技术演进同频,openKylin 3.0从C迈向Rust
c语言·开发语言·rust·开源·开放原子·openkylin
迷茫、Peanut14 小时前
C语言sizeof和strlen
c语言
Bug退散师19 小时前
多路IO复用[select版TCP服务器与poll版TCP服务器]与常用网络编程函数详解
linux·服务器·c语言·网络·驱动开发·tcp/ip
天空'之城19 小时前
C 语言工业级通用组件手写 03:双向链表
c语言·嵌入式开发·双向链表
FREEDOM_X19 小时前
嵌入式——定时器工作原理
linux·c语言·单片机·嵌入式硬件·ubuntu
2401_8275012819 小时前
51单片机(四)DS18B20 数字温度传感器
c语言·51单片机
不会c+21 小时前
C语言:入门到精通(408考研版)系列七 派生的数据类型
c语言·开发语言
躺不平的理查德1 天前
SQLite C API 备忘录
c语言·数据库·sqlite