1.什么是指针(指针的定义)
要点:指针就是地址,地址就是指针,通过指针可以间接访问内存中存储的数据。
生活中,一栋宿舍楼每个房间都有门牌号,这样就可以很快地找到一个想要找的房间,计算机中的内存也是如此,我们把内存划分为一个个的内存单元,每个内存单元也都有一个编号,计算机中我们叫它为地址,C语言中也叫指针。所以我们认为内存单元=地址=指针。
2.变量的指针和指向变量的指针变量
要点:变量的指针就是变量的地址,存放变量地址的变量叫指针变量。
2.1取地址操作符--&
2.2指针变量
C语言中,我们通过取地址操作符(&)取出地址存放到的地方叫做指针变量,指针变量也是变量,所以指针变量的定义与变量的语法格式基本相同,只是指针变量是专门用来存地址的。
指针变量一般形式
类型说明符* 变量名[=地址表达式]
例如
#include <stdio.h>
int main()
{
int a = 1;
int* p = &a;//取出a的地址并存放在指针变量a中
return 0;
}
怎么理解指针的类型呢?我们看到p的类型是int*, p的左边是int*,*在说明p是指针变量,而*前面的int是在说明p指向的是整型对象。
2.3解引用操作符(*)
生活中,我们通过门牌号可以找到一个房间,可以在房间里拿想要拿的东西,C语言当中也是如此,我们通过地址(指针)可以找到地址指向对象,那以后想要使用地址指向的这个对象,就得使用解引用操作符来获取。
例如
#include <stdio.h>
int main()
{
int a = 1;
int* p = &a;
*p = 0;
}
上述代码, *p的意思就是通过p中存放的地址,来找到地址所指向的对象,*p其实就是变量了,*p=0的意思就是给a赋值为0了。
3.指针的运算
3.1 指针+-整数
指针变量的值是可以进行运算的,但运算规律比较特殊,不是简单的加减法,而是运算对象数据类型相关的运算,直观上可以理解为数组元素访问位置的变化,也就是+代表指向元素后面,-代表指向元素前面。
例如
创建一个10个元素的整型数组,通过指针+-运算符就可以访问数组中各个元素了。
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i++)
{
printf("%d ", *(p + i));//指针+-整数
}
}
这里p+i就是指针+-整数,*(p+i)就是访问数组中的元素了。
3.2 指针-指针
我们在模拟实现strlen库函数的时候就运用到了指针-指针
//指针-指针
#include <stdio.h>
int my_strlen(char* s)
{
char* p = s;
while (*p != '\0')
p++;
return p - s;
}
int main()
{
printf("%d", my_strlen("abc"));
return 0;
}
3.3 指针的关系运算
指针也可以比较大小的,例如
//指针的关系运算
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
while (p < arr + sz)//指针比较大小
{
printf("%d ", *p);
p++;
}
return 0;
}
4.指针的使用和传址调用
4.1 指针的使用
我们可以通过指针来模拟实现strlen库函数,strlen函数的功能是统计一个字符串\0之前的字符个数。如果要模拟实现这个功能,就只需从起始地址遍历整个字符串,只要不是\0,计数器就加一,这样就模拟出该功能了,假设参数s接收字符串的起始地址,然后遍历\0之前的字符串,返回该长度即位字符串个数。
代码如下:
#include <stdio.h>
int my_strlen(char* s)
{
int count = 0;
while (*s)
{
s++;
count++;
}
return count;
}
int main()
{
int len = my_strlen("abcd");
printf("%d ", len);
return 0;
}
4.2 传值调用和传址调用
我们学习了指针,那指针是非用不可吗?答案是错,指针并非非用不可,我们可以使用指针对一些复杂的问题设计出更加优异且灵活的算法,更好地实现任务需求。
例如,写一个函数,实现两个整型变量的交换。
代码如下:
#include <stdio.h>
void Swap(int x, int y)
{
int temp = 0;
temp = x;
x = y;
y = temp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d,b=%d\n", a, b);
Swap(a, b);
printf("交换后:a=%d,b=%d\n", a, b);
}
结果如上,显然该代码并未实现两个整型变量的交换,那是为什么呢?我们通过调试可以发现:
主函数中,创建了变量a和b,a的地址是0x0000001cc033f8f4,b的地址是0x0000001cc033f914,通过Swap函数将主函数里的实参a和b传给了Swap函数里的形参x和y,但是从调试结果看到x和y都有相应的地址,分别是0x0000001cc033f8d0,0x0000001cc033f8d8,x和y确实接收到了a和b的值,但是x和y都有各自独立的空间,在Swap函数里对x和y进行替换,自然不会影响到a和b,Swap函数在使用的时候直接把变量传给了函数本身,这种叫做传值调用。
要点:使用函数时,实参传递给形参,形参会单独会创建一个临时空间来接收实参,对形参的改变并不会影响实参。
那我们如果想实现该任务,我们该怎么做呢,这就得运用到指针了。 在主函数里,将a和b的地址传给Swap函数,Swap函数里通过地址间接地访问a和b,这样就可以实现两整型变量的替换了。
代码如下:
#include <stdio.h>
void Swap(int* x, int *y)
{
int temp = 0;
temp = *x;
*x = *y;
*y = temp;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a=%d,b=%d\n", a, b);
Swap(&a, &b);
printf("交换后:a=%d,b=%d\n", a, b);
}
通过结果我们可以看到该代码完成了任务,这里调用Swap函数是将变量的地址传给了函数,这种函数调用方式叫做传址调用。