- 写在前面
- 本套系列指在帮助自学C语言小白快速复习整理指针和数组相关知识,形成一套属于自己的理解体系。
本篇总结了我的自学后二次回顾的一些知识盲点、易错点,希望能帮助你更好地啃下C中的一个难点指针。
本套系列的设计思想是从概念出发,引入最基本例子,带你回顾概念,最后配上实战题目检测你的理解。
指针
1.C语言中内存是如何存放变量的
我们先举一个最简单的例子
int i = 1;
我们知道它的意思, 定义了一个整型变量 i ,它的值是1.
但是在编译器或者computer的眼里它是怎么样的呢?
编译器先为变量i申请一个地址,这个地址所存放的值是1.
也就是说如果要打印变量i的值是通过访问地址的值来读取变量i的值。
2.与指针相关的运算符
1.&地址运算符
一般后面加变量名,表示取地址的意思。
示例:
printf("%p\n",&i);
表示变量名i的地址
2.*取值运算符
首先不要与乘法运算符 *混淆,虽然他们的符号一样。
表示取指针所指向地址的值
示例:
printf("%d",*ptd);
3.指针
指针概念
根本上来讲,指针就是地址。
类型名 *p来表示
ps:
下面的例子帮你更好地理解。
char s[5] = "love";
char *p;
p = s;
这是字符串s的存放值图解,
一个地址代表一个字节,一个char类型字符所占空间为一个字节。
字符串类型最后一位C语言默认加一个\0来结束
地址 | 存放的值 |
---|---|
10000 | l |
10001 | o |
10002 | v |
10003 | e |
10004 | \0 |
对于字符串类型,指针解引用可以直接取得整个字符串的值。(原理是指针指向字符串首元素地址,C语言默认会通过首地址依次往后遍历直到’\0’结束)
代码示意:
#include <stdio.h>
int main()
{
char *ptd;
char s[5] = "love";
ptd = s;
printf("%s",ptd);
return 0;
}
打印结果如下
这里先带你回顾一个概念:
数组名是数组元素的首地址。
也就是说s与 &s[0]等价
具体后面讨论数组指针和指针数组会用代码详解。
看到这里你可能会有疑惑这里为什么不需要解引用。
就像上面写的那样,上述情况下,ptd实际上指向的是&s[0](s首元素的地址),C语言默认打印出字符串格式的话会自动遍历所有地址,可以理解为这种情况下ptd 和 s等价。
为了加深理解,你可以尝试先将ptd改为*ptd. 再将 s改为 &s[0]、&s[1]试试看打印结果。
避免访问未初始化指针
#include <stdio.h>
int main()
{
int *i;
*i = 1;
return 0;
}
这样的代码是很危险的,因为我们不知道指针变量 i 到底指向哪里。这个道理就跟访问未初始化的变量一样,它的值是随机的。
一般系统会直接报错,但是当指针变量存放一个合法的地址,那么系统就排查不到。
具体原因可以思考左值,后面博文会写到。
4.经典题目
1.两个值的交换 使用指针法
不用指针的方法 (如果你会自己写函数的话不妨试试将交换的过程写在一个叫swap函数中,这里先引入一下,后面传值传址会再讲)
#include <stdio.h>
int main()
{
int m = 2,n = 1;
int temp;
printf("%d %d\n",m,n);
temp = m;
m = n;
n = temp;
printf("%d %d",m,n);
return 0;
}
指针法
#include <stdio.h>
int main()
{
int m = 2,n = 1;
int temp;
int *p1;
int *p2;
p1 = &m;
p2 = &n;
printf("m: %d n: %d\n",m,n); //交换前
printf("*p1:%d *p2:%d\n",*p1,*p2);
temp = *p1;
*p1 = *p2;
*p2 = temp;
printf("m: %d n: %d\n",m,n); //交换后
printf("*p1: %d *p2: %d\n",*p1,*p2);
return 0;
}
相信到这里,指针概念用法你已经掌握了