// 指针
#include <iostream>
#include <string>
using namespace std;
//1. 指针的概念
//1.1 数据在内存中如何存取?
//系统根据程序中定义变量的类型,给变量分配一定的内存空间。字符型占1个字节,整型数占4个字节.....。内存区的每个字节都有编号,称之为地址。
//访问有两种方式:
// 重要的话说三遍:变量的地址称为指针 变量的地址称为其指针
(数组中,多个元素占据连续一块内存,第一个元素的地址,被用来当作数组名)
//1.2 变量的指针与指针变量
把指针变量当成是一个 long 类型的数。本质上,任何指针变量在内存中,都和普通 long 类型变量没啥区别。
int main(){
int a =10;
int * p = &a; //&:取出a的地址,地址是一个数,只不过,我们叫它为指针罢了。
// 符号 * 在定义语句中只表示:变 量 p 的 类 型 是 指针。 理解:
(int *) p = & a ;
*p = 200;//p抓住了a的地址,直接对其原值操作。就像是java里的Integer类,抓住引用
cout<<*p<<endl;
p++;//p里面装的是地址、指针,数组中 p++就是指向下一个元素,比如原来装的 2000H,加一后变成 2004H(因为 int 4 Byte)
cout<<*p<<endl;
// 猜猜这个打印多少? 我们是无法知道的,同时也可能造成程序错误!示例:
const int test = 9;
char buffer[3];
buffer[3] = 10;
cout<<test;
// 这样 test 的值就变成10了!因为通过 buffer 偏移量为 3 处是 test 的地址(栈向低地址扩展),所以直接把那个内存位置上的值给改了!
这个例子也说明了 const 值是可以通过某些手段修改的。(我们本意不想让它变)
// 我们访问了一个内存中可能完全不了解的值,那么也能修改某个位置上的值,尽管我们不知道这个位置上的东西重要与否,但是可以修改,程序因此可以产生错误结果。
// 补充点东西 ++ -- *都是右结合的右结合的右结合的示例
// 数组名就是数组的起始地址!再强调一遍,就是第一个元素的地址!
int a1[10] = {1,2,3,45,6,7,8,96,4};
int * p1 = a1;
* p1 = 10;
// 检验理解:*(a1+1)与 a1[1] 是同一个值。
// 现在 a1[0] 就是 10。
// 注意,数组和指针还有一点区别:
上面的错误是什么意思呢?a1 是一个数组。指针可以 ++,但是,数组不可以 ++,也就是说,*++p1 = 2 是正确的,但是,*++a1 是错误的。前面不是说过数组名就是指针吗?确实,数组名,在计算机里就是指针,但是在这里,编译器还同时认可 a1 是一个数组类型,看这段:
char buffer[3];
buffer = {1,1,1}; 这就会报错,也就是说,数组类型,本身是不能再赋值的,但是,数组的内容是可以修改的。
我可能说的不清楚,但是就是这样的,hhhhhhh
// Demo:用指针的操作输出数组内容
for (p1 = a1; p1 < a1+10; p1++) {
cout<<*p1<<"\t";
}
for(int i=0;i<10;i++){
cout<<*p++<<'\t';
}
再看个demo加深理解,再次提醒,运算符右结合,*x 很特殊。
下面是几个关于形参与实参的 demo,提醒,参数中的数组,实际被降级为指针。原理是参数压栈操作实际只能传递指针。
图中函数 f 中,可以修改 arr = 0,因为 arr 已经被“降级”为了指针。
第二个:
之前提到过:
再来demo:
再来demo:
// 再次提醒,数组名是常量,数组内容是可变的。数组名定义时必须指明占用的空间大小。
练习题巩固一下:
其中一个解:
void copy(char*p1,char *st2, int start); // 先声明,后调用的习惯
int main()
{
char st1[100] ="w?o?c?a?o?a!a!a!a!a!123a!!!!";
char st2[100];
int m=10;
copy(st1, st2, m);
cout<<st2;
}
void copy(char*p1,char *p2,int start) {
p1+=start;
while((*p2++ = *p1++));
}