基础指针学习笔记

这是一个变量 a

1
int a;

a 变量的地址的表达式为 &a

你想要把变量 a 的地址存下来,就用

1
int* p = &a;

或者

1
int *p = &a;

这两种是一样的,但是推荐第一种因为看着不会乱

想要表示 地址 p 对应的 变量,就用 *p,在这里 *p 完全相当于 a

这下面两行效果是完全一样的

1
2
printf("%d",*p);
printf("%d",a);

然后还有,注意这下面两种是不一样的

1
2
*p += 1;	//相当于 a += 1
*p++; //相当于 *(p++)

另外还有一个容易混淆的 引用,相当于给变量 a 起别名 b

1
int& b = a;

或者

1
int &b = a;

这两种也是一样的,但是依然推荐第一种因为看着不会乱

基础指针内容到此为止


简单概括指针内容:

已知变量 int a:

  • a; 这是变量

  • &a 这是 a 的地址

int* p = &a

  • p 相当于 &a

  • *p 相当于 a

也就是说

  • &变量 就相当于变量的地址

  • int* 地址 就是申请了一个地址变量

  • *地址 就相当于该地址对应的变量

另外

  • int& b = a 相当于给变量 a 起别名 b

上例题

例 1

输入 \(n\) 个变量,使用指针变量访问输出。

分析:数组的存储对应的地址是连续的,所以可以对地址以此加一来取数组的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<cstdio>
using namespace std;
int n;
int main() {
scanf("%d", &n);
int x[n];
for (int i = 0; i < n; i++)
scanf("%d", &x[i]);
int* p = x; // 相当于 int *p = &x[0];
for (int i = 0; i < n; i++) {
printf("%d ", *p);
p++;
}
return 0;
}


例 2

编写函数交换变量 a、b 的值。

(错误)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<cstdio>
using namespace std;
int a=1;
int b=2;
void swap(int x, int y)
{
int t = x;
y = x;
x = t;
}
int main() {
swap(a,b);
printf("%d %d", a, b);
return 0;
}

程序之所以错误,是因为在函数的入口处,int x 相当于 int x = ay 同理,所以 xy 交换并不会改变 ab 的值;

(正确,指针)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<cstdio>
using namespace std;
int a=1;
int b=2;
void swap(int* x, int* y)
{
int t = *x;
*y = x;
*x = t;
}
int main() {
swap(&a,&b);
printf("%d %d", a, b);
return 0;
}

程序在函数入口处传进 a、b 的地址,并在函数内对这两个地址对应的变量进行操作,所以正确。

(正确,引用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<cstdio>
using namespace std;
int a=1;
int b=2;
void swap(int& x, int& y)
{
int t = x;
y = x;
x = t;
}
int main() {
swap(a,b);
printf("%d %d", a, b);
return 0;
}

程序在函数入口处函,int& x 相当于 int& x = ay 同理,所以 xy 相当于 ab 的别名,并且对这两个数的值进行交换。


再谈字符数组与指针

我们读入并且输出字符数组的时候,用法如下:

1
2
3
char s[101];
scanf("%s",s);
printf("%s",s);
这里 scanfs 并不用且不能写成 &s,因为我们在例题一中其实就有提过,这里的 s 就相当于 &s[0]

读者可能会问,既然我们在 printf() 函数中传入的是 &s[0],printf() 函数怎么判断字符串的长度呢?

其实无论是使用 scanf() 函数读入字符数组,还是初始化字符数组,字符数组最后一位的下一位会有一个 '\0' 表示字符串已结束。

例 3

定义一个函数,使用指针传参来实现两个字符串的拼接,将结果存储在第一个字符串指针指向的内存空间中。要求第一个字符串有足够的空间来存放拼接后的结果。并在主函数中定义两个字符串,调用该函数,并输出拼接后的结果。

这道例题的代码是用网上的代码修改的,其思路和巧妙程度让我叹为观止。理解好这个程序,相信读者能对指针的应用能有更深入的理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
using namespace std;
void str(char *p1, char *p2) {
while (*p1++);
p1--;
while (*p1++ = *p2++);
}
int main() {
char s1[101] = "ABCD", s2[101] = "EFGH";
str(s1, s2);
printf("%s\n", s1);
return 0;
}

注意:这里的相当代码只是便于读者理解代码的执行顺序,其作用可能略有差别

这里第四行 while (*p1++); 相当于 while(*(p1++));,也相当于

1
2
3
4
5
while(*p1)
{
p1++;
}
p1++ //注意;尽管当 while 的条件 *p1 已经为 false 了,不再执行 while 循环括号里面的内容了,但是 p1++ 依然会执行最后一次

while(*p1)作用相当于又相当于 while(*p1 != '\0')'\0' 的 ASCII 码是 0),即将 p1 的地址移到 '\0' 处停止,然后即使停止了,仍然会执行一次 p1++,所以要在第五行,重新执行一次 p1--,让 p1 的地址保持在 '\0' 处;

然后再下一行相当于 while (*(p1++) = *(p2++) != '\0');

相当于依次执行下列代码

1
2
3
4
5
6
7
8
9
*p1=*p2;
while(*p1)
{
p1++;
p2++;
*p1=*p2;
}
p1++; // 注意:尽管当 while 的条件 *p1 已经为 false 了,不再执行 while 循环括号里面的内容了,但是 p1++ 和 p2++ 依然会执行最后一次
p2++;
即依次移动,将 p2 对应的字符赋值给 p1,直到遇到 '\0' 结束。


好耶,那么这期博客就到这里结束啦(错乱)

写这个写了三天呢,希望能赶紧回去读书吧!