提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
`关于变量的地址
首先不同的变量占据着不同大小的空间,例如double便占据了8个字节,我们可以通过以下代码检查每个变量的大小,例如求int的大小 int a = 0; printf("%lu", sizeof(&a));
可以得出int占据8个字节。而变量的字节,就存放在内存的某个位置,那个位置就是变量的地址。
如何获得变量的地址呢?
&为取地址符,我们可以通过int i; printf("%p", &i);
来输出地址的数值,使用了%p,且自带16进制的0x标志。
数组的地址
前面所学的数组的地址,由测试可得,由数组的第一个元素的地址决定,也就是说,数组的第一个元素的地址就是数组的地址,而之后地址增加的步长又由数组元素的字长决定。
一、指针的定义
如果在程序中声明一个变量并使用地址作为该变量的值,那么这个变量就是指针变量。指针是一种特殊的变量,它存储了一个内存地址。这个内存地址指向另一个变量的位置。通过指针,我们可以直接访问和修改被指向的变量的值。
定义指针变量的一般形式为:
*类型名 指针变量名;如:
int *p;
类型名指定指针变量所指向变量的类型,必须是有效的数据类型,如:int,float,char等。指针变量名是指针变量的名称,必须是一个合法的标识符。*注意这里,我们并不是把*加给了int,而是加给了p,因此,p是一个int。
二、指针的基本介绍
1.赋值
在定义指针后,我们还需要将其初始化为某个有效的内存地址,否则它会指向一个未知的位置或者空地址。可以使用取地址运算符&来获取变量的地址,并将其赋值给指针变量,如下所示:
int num = 10;
int *p = #
上述代码中,&num获取变量num的地址,并将其赋值给指针变量p。现在,指针p指向了变量num的内存地址。通过指针,我们可以对被指向的变量进行操作。例如,可以使用*来获取指针所指向变量的值,如下所示:
int value = *p;
上述代码中,*p表示获取指针p所指向的变量的值,并将其赋值给变量value。
2.0地址
0地址通常用于表示空指针。空指针指向内存中的无效或未定义位置,它不指向任何有效的对象或函数。空指针用于初始化指针变量、检查指针是否为空,或者作为函数的返回值。
三、函数与指针
1.修改变量
我们可以用函数调用指针参数,得到地址后,可以用指针直接修改指向的变量。如下所示:
#include <stdio.h>
void f(int *p)
{
printf("%p\n", p);
*p=4;
}
int main()
{
int i = 3;
f(&i);
printf("%d", i);
return 0;
}
在这里我们用函数取得了i的地址,将i的值从3通过函数调用指针改成了4,修改了i的值。
2.交换两个变量的值
要完成两个变量的值的交换,我们可以传两个变量的地址进去,将两个变量的地址进行交换
#include <stdio.h>
// 函数原型
void swap(int *a, int *b);
int main() {
int x = 5;
int y = 10;
printf("交换前:\n");
printf("x = %d\n", x);
printf("y = %d\n", y);
// 调用交换函数
swap(&x, &y);
printf("交换后:\n");
printf("x = %d\n", x);
printf("y = %d\n", y);
return 0;
}
// 定义交换函数
void swap(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
运行之后,x和y的值便交换过来了,变成了x=10,y=5。在函数内部,我们首先将指针所指向的值存储到临时变量t中,然后交换两个变量的值。通过将x和y的地址传递给swap函数,我们实现了变量值的交换。
3.帮助函数返回多个值
1.场景一
当传入的参数实际上是需要保存带回结果的变量时
#include <stdio.h>
void f(int a, int b, int *x, int *y);
int main(void)
{
int a = 1;
int b = 2;
int result1, result2;
f(a, b, &result1, &result2);
printf("%d %d", result1, result2);
return 0;
}
void f(int a, int b, int *x, int *y)
{
*x = a + b;
*y = b - a;
}
我们把其中不止一个要接收的结果的变量的地址传进去,让函数帮我把这些变量填好,把值传回来。这些参数的作用就是把的结果带出来。
2.场景二
当函数返回运算的状态,结果通过指针返回时
int f(int a, int b, int *result) {
*result = a + b; // 将计算结果通过指针result返回
return 0; // 返回计算状态,通常0表示正常完成
}
int main() {
int x = 5, y = 3, sum, status;
s = f(x, y, &sum); // 调用f函数,并将sum的地址传递给函数
if (s == 0) {
printf("The sum is: %d\n", sum);
} else {
printf("Error\n");
}
return 0;
}
我们可以让函数返回不属于有效范围内的值表示你所需要的意义(如-1或0等等),如上面返回计算状态,用0表示正常完成。
四、指针与const
1.当指针是const时
即int *const q = &i;
,q不能被改变,也就是指针指向某一个值的事实不能被改变了。这时候改变i的值是可以的,即*q=1;
,是可以的;但是改变q,即q++;
,是禁止的。
2.当所指是const时
即const int *p = &i;
,不能通过p去修改i,也就是说不能通过指针去修改变量,如*p=1;
是禁止的。
3.const和*的位置关系
const在*前面代表所指不能被修改,反之则指针不能被修改。
五、指针与数组
数组可以说是特殊的指针,在函数参数表里的数组其实就是指针,即sizeof(a[]) == sizeof(int*)
,数组本身便表达地址,但是数组的单元表达的是变量,需要用&取地址。
[]运算符可以对数组和指针做。
*运算符可以取出指针指向的值和列表的第一个值。
数组变量是const的指针,不能被赋值。
六、指针运算
1.指针+1
指针加一并不是实际数值在加一,而是存储单元加一。也就是表示指针指向下一个变量
#include <stdio.h>
int main()
{
int ai[]={0,1,2,3,4,5,6,7,8,9,};
int *q = ai;
printf("%p\n", q);
printf("%p\n", q+1);
return 0;
}
运行结果是
000000000062FDF0
000000000062FDF4
可以看出因为int的sizeof(int)=4,所以地址加4。因此通过指针和数组的关系可以知道,*(p+1)
等价于a[]
,如果指针不是指向一片连续分配的空间,那么这种运算没有意义。
2.指针计算
指针可以进行以下计算:
-加减整数
-递增递减(q++)
- 取出q所指的那个数据来,顺便把q移到下一个位置
- ++的优先级比*还要高
- 常用于数组类的连续空间操作
-两个指针相减
- 结果是地址的差除以sizeof大小
3.指针的比较
各种比较符都可以对指针做,去比较他们的地址,如<,>,<=,>=,==,!=。
七、指针的类型转换
-void*表示不知道指向什么东西的指针
- 计算时和char*相同但不相通
-指针也可以转换类型
int *q=&i; void*p = (void*)q;
这并没有改变q变量的类型,只是通过不同的眼光通过q看他所指的变量。