C语言指针学习记录

C语言指针学习记录

虚拟内存地址

在指针的学习之前,我们必须先了解内存,在我们设备的内存条中,有着自身的存储地址空间,操作系统将它的存储地址空间抽象成了一个巨大的一维数组空间,并且对于这个空间中的每一个字节都会分配一个编号,这个编号就叫做内存地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main(){
char x1 = 'a'; // 声明了一个字符变量x1
char x2 = 'b';
char x3 = 'c';
char x4 = 'd';

int y1 = 1;
int y2 = 2;
int y3 = 3;
}

例如在上述代码当中,我声明了4个字符型的变量,并给他们赋了值,这时候,系统就会为他们分配空间来存放数据。假设当我们声明了这些变量之后,如果a的内存地址是101,那么下面b的就是102,以此类推,因为每个字符只占一个字节。那么如果里面存放的不是字符,而是整型呢?

QQ_1743324943229

我们知道,一个int类的整形占4个字节,那么在内存空间中的地址则是这样的:即每个int类的整形占了4个编号,也就是四个地址,但我们一般如果说y2的地址是多少?我们会说是它的首地址,也就是105

b8c5cba6e28f962b38071934cb4a9b4b

接下来实际操作一下:)

QQ_1743326124823

在上述代码中,我声明了4个字符型的变量,并给他们赋了值,接下来打印出他们的地址,(在变量名前面的”&”符号是取地址符,可以获得这个变量的地址, %p代表的是我们要输出地址类型的数据,就像%d是输出整数类型的数据一样)我们可以看到,这个地址是16进制的,每个字符只占了一个字节。

QQ_1743326719532

接着我们看这一段代码,我声明了2个int型的变量,并给它们赋了值,这时候让我们注意这两个地址的末尾,“8”和“C”,不就是相差了5吗,也就印证了我们之前的说法(一个int占5个字节)。

指针的含义

接下来就来到指针了。一般来讲,指针是用来存放内存地址的变量。也就是说,指针指向的就是一个变量的地址,创建一个指针变量就是在内存中分配了一块空间,通过指针就能找到这块空间的位置。

其实通俗地讲,指针就是地址,当我们声明一个指针变量时,也就是声明了一个存放地址的变量。

指针的创建与基本使用

指针的创建与赋值

1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main(){
int a = 3;
int *b = &a;

printf("address a: %p, value a: %d\n\n", &a, a);
printf("address b: %p, value b: %p\n", &b, b);
}

在上述代码中,a是一个整形变量,当你声明了它之后,系统就会从内存中为它开辟一块空间,当你对a赋值的时候,是给这个空间中的数据赋值。但你不知道这个空间的位置(也就是地址);

b是一个指针变量(也可以叫做地址变量?),当你声明它之后,系统也会从内存中为它开辟一块空间,并且b代表的就是这个空间的位置(地址),当你对b赋值时,赋值的是一个地址,例如上述代码中,我把a的地址赋值给了b,这时候b的值就是a的地址。

09507DD6F039B7749A597598BF9AFBBC

让我们观看上面的运行代码,我们分别输出了a和b的地址和值,可以看到,b的值就跟我们上面说的一样,是a的地址,这也就是指针的作用之一

那么我们用指针b存放了a的地址,这时候,我们可以通过输出*b,来获得a的值,因为b中存放的是a的地址

ps: 当你创建了一个指针变量之后,输出&b是输出这个变量本身的地址,输出b是输出这个变量里面存放的地址,输出*b是到达这个变量里面存放的地址的空间,然后输出里面的值。

QQ_1743404862976

28169bd238672677d236e8be995a8b42

那么使用指针还有什么作用呢?拿上述的代码距离,我们可以通过修改*b的值,来改变我们定义的变量a的值,因为我们已经把a的地址存到了b里面了

间接引用操作符

当我们定义指针变量时,使用的是b,里面存放的是地址,当我们想要改变里面的地址时,我们是给b赋值(赋的是地址),而不是给*b赋值, 因为除了声明指针这一场景之外,其他地方的*b代表的是*指针变量里面存的地址指向的值,如下图所示,我们可以直接在定义int *b时给指针变量赋值,例如int *b = &a 也可以像下图一样给b赋值。

当我们使用printf输出*b时,因为现在b中存放的是a的地址,所以*b指的是a的地址里面存放的值,也就是变量a的值

这里我们可以得到指针的另一个作用,改变其他变量的值, 如下图所示,当我们将*b赋值为5时,同时变量a的值也会跟着变化

QQ_1743494592499

指针与数组

当我们获取一个数组的地址时,返回的是数组中第一个数据的地址QQ_1743499668424

结构体与指针

结构体是一个或多个变量的集合,这些变量可以是不同的类型(跟java当中的类有点类似,只是没有类中的各种方法)

结构体的定义

1
2
3
4
struct 结构体名字{
数据类型 变量名;
数据类型 变量名;
};
1
2
3
4
struct tagtax{
int a;
char b;
};

结构体的初始化

1
struct 结构体名 变量名;
1
struct tagtax t1;

简化结构体的初始化

当我们每次初始化结构体时,都要写struct,但我们可以在定义结构体时使用typedef关键字来解决

可以写成:

1
2
3
4
typedef struct 结构体名字{
数据类型 变量名;
数据类型 变量名;
}结构体别名;

也可以写成: (为什么我们可以省略结构体名字呢?因为之后我们初始化结构体都是用的它的别名,不会用到结构体名字)

1
2
3
4
typedef struct{
数据类型 变量名;
数据类型 变量名;
}结构体别名;

例如:

1
2
3
4
typedef struct{
int a;
char b;
}tagtax;

这时候的初始化: (没有前面的struct嘞)

1
tagtax t1;

声明结构体类型的指针

当我们定义了一个结构体,并声明(初始化)一个结构体指针, 那么我们如何给它里面的变量赋值呢?

如下面的代码,我们可以用(*t1).a = 1;来进行赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef struct{
int a;
char b;
}tagtax;

int main(){
tagtax *t1;
(*t1).a = 1;
(*t1).b = 'k';
}


在C语言中,我们可以简写这一赋值方式,使用箭头进行赋值

1
2
t1->a = 1;
t1->b = 'k';

动态分配内存

为什么我们需要动态分配内存呢?

平常我们写代码时,例如声明一个int a = 1;这时候系统会为我们这个变量自动分配一块空间,用完了不用之后自动给我们释放,我们自身不能控制什么时候给我们这个变量分配一块空间,什么时候释放这块空间,但有了动态分配内存,我们就能自己来决定什么时候分配和什么时候释放

在C语言中,我们会使用malloc函数来动态分配内存,使用free函数来释放内存

QQ_1743519512638

我们通过malloc分配内存空间,因为这里存的是int类型的数据,所以要在前面进行一次强制转换,这样就能给a指针变量分配一份4个字节的int类型的地址


C语言指针学习记录
http://example.com/2025/04/01/C语言指针小记/
作者
Tagtax
发布于
2025年4月1日
许可协议