黑 马 程 序 员_视频学习总结----03 指针

 ---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------

 

一、什么是指针?

  • 用来存放变量地址的变量,就称为"指针变量"。

 

二、指针的定义

一般形式:类名标识符  *指针变量名;

int *p;

float *q;
  • "*"是一个说明符,用来说明这个变量是个指针变量,是不能省略的,但它不属于变量名的一部分
  • 前面的类型标识符表示指针变量所指向的变量的类型,而且只能指向这种类型的变量

 

三、指针的初始化

1.指针的初始化有:先定义后初始化、在定义的同时初始化

2.初始化的注意

指针变量是用来存放变量地址的,不要给它随意赋值一个常数。下面的写法是错误的

int *p; 
p = 200; // 这是错误的

 

四、指针运算符

1.给指针指向的变量赋值以及内存分布

 1 char a = 10;
 2 printf("修改前,a的值:%d\n", a);
 3 
 4 // 指针变量p指向变量a
 5 char *p = &a;
 6 
 7 // 通过指针变量p间接修改变量a的值
 8 *p = 9;
 9 
10 printf("修改后,a的值:%d", a);

当程序刚执行完第5行代码时,内存中大概的分布情况是这样的

黑 马 程 序 员_视频学习总结<c语言>----03 指针,a值是10,p值就是变量a的地址ffc3。

2.取出指针所指向变量的值

指针运算符除了可以赋值之外,还可以用于取值

1 char a = 10;
2  
3 char *p;
4 p = &a;
5 
6 char value = *p;
7 printf("取出a的值:%d", value); 

第6行中的*p的意思是:根据p值(即变量a的地址)访问对应的存储空间,并取出存储的内容(即取出变量a的值),赋值给value

3.使用注意

在指针变量没有指向确定地址之前,不要对它所指的内容赋值。下面的写法是错误的

int *p; 
*p = 10; //这是错误的

4.在同一种编译器环境下,一个指针变量所占用的内存空间是固定的。比如,在16位编译器环境下,任何一个指针变量都只占用2个字节,并不会随所指向变量的类型而改变。

黑 马 程 序 员_视频学习总结<c语言>----03 指针

 

五、用指针指向一维数组的元素

 1 // 定义一个int类型的数组
 2 int a[2];
 3 
 4 // 定义一个int类型的指针
 5 int *p;
 6 
 7 // 让指针指向数组的第0个元素
 8 p = &a[0];
 9 
10 // 修改所指向元素的值
11 *p = 10;
12 
13 // 打印第一个元素的值
14 printf("a[0] = %d", a[0]);

输出结果:黑 马 程 序 员_视频学习总结<c语言>----03 指针,说明已经通过指针间接修改了数组元素的值,跟指向一个普通int类型变量是一样的。

由于数组名代表着数组的首地址,即a == &a[0],因此第8行代码等价于:

// 让指针指向数组的第0个元素
p = a;

内存分析图如下,一个指针变量占用2个字节,一个int类型的数组元素占用2个字节

黑 马 程 序 员_视频学习总结<c语言>----03 指针

 

六、用指针遍历数组元素代码

1.最普通的遍历方式是用数组下标来遍历元素

1 // 定义一个int类型的数组
2 int a[4] = {1, 2, 3, 4};
3 
4 int i;
5 for (i = 0; i < 4; i++) {
6     printf("a[%d] = %d \n", i, a[i]);
7 }

2.接下来我们用指针来遍历数组元素

 1 // 定义一个int类型的数组
 2 int a[4] = {1, 2, 3, 4};
 3 
 4 // 定义一个int类型的指针,并指向数组的第0个元素
 5 int *p = a;
 6 
 7 int i;
 8 for (i = 0; i < 4; i++) {
 9     // 利用指针运算符*取出数组元素的值
10     int value = *(p+i);
11     
12     printf("a[%d] = %d \n", i, value);
13 }

 

七、指针与数组的总结

p是指针,a是一个数组

1. 如果p指向了一个数组元素,则p+1表示指向数组该元素的下一个元素。比如,假设p = &a[0],则p+1表示a[1]的地址

2. 对于不同类型的数组元素,p值的改变是不同的。如果数组元素为int类型,p+1代表着p的值加上2(16位编译器环境下)

4. 如果p的初值是&a[0],那么

  • p+i和a+i都可以表示元素a[i]的地址,它们都指向数组的第i个元素。a代表数组首地址,a+i也是地址,它的计算方法与p+i相同

  • *(p+i)和*(a+i)都表示数组元素a[i]

  • 虽然p+i和a+i都指向数组的第i个元素,但二者使用时还是有区别的。因为作为指针变量的p可以改变自身值,如p++,使p的值自增。而数组名a是一个代表数组首地址的常量,它的值是不能改变的,即a++是不合法的

4. 引用一个数组元素可以有两种方法:

  • 下标法: 如a[i]

  • 指针法: 如*(p+i) 或 *(a+i)

 

八、数组、指针与函数参数

1.用数组名作为函数实参时,是把实参数组的首地址传递给形参数组,两个数组共同占用同一段内存空间,这样形参数组中的元素值发生变化就会使实参数组的元素值也同时变化

 1 void change(int b[]) {
 2     b[0] = 10;
 3 }
 4 
 5 int main()
 6 {
 7     // 定义一个int类型的数组
 8     int a[4] = {1, 2, 3, 4};
 9 
10     // 将数组名a传入change函数中
11     change(a);
12     
13     // 查看a[0]
14     printf("a[0]=%d", a[0]);
15     
16     return 0;
17 }

change函数的形参是数组类型的,在第11行调用change函数时,将数组名a,也就是数组的地址传给了数组b。因此数组a和b占用着同一块内存空间。

输出结果:黑 马 程 序 员_视频学习总结<c语言>----03 指针

 

2.这种地址的传递也可以用指针来实现。函数的实参和形参都可以分别使用数组或指针。这样就有4种情况:

黑 马 程 序 员_视频学习总结<c语言>----03 指针

也就是说,如果一个函数的形参类型是一个数组,调用函数时,你可以传入数组名或者指针变量;

 1 void change(int b[]) {
 2     b[0] = 10;
 3 }
 4 
 5 int main()
 6 {
 7     // 定义一个int类型的数组
 8     int a[4] = {1, 2, 3, 4};
 9     
10     int *p = a;
11 
12     // 将数组名a传入change函数中
13     change(p);
14     
15     // 查看a[0]
16     printf("a[0]=%d", a[0]);
17     
18     return 0;
19 }

注意第1行的形参类型是个数组int b[],第10行定义了指针变量p,第13行将p当做实参传入函数

如果一个函数的形参类型是一个指针变量,调用函数时,你可以传入数组名或者指针变量。

 1 void change(int *b) {
 2     b[0] = 10;
 3     // 或者*b = 10;
 4 }
 5 
 6 int main()
 7 {
 8     // 定义一个int类型的数组
 9     int a[4] = {1, 2, 3, 4};
10 
11     // 将数组名a传入change函数中
12     change(a);
13     
14     // 查看a[0]
15     printf("a[0]=%d", a[0]);
16     
17     return 0;
18 }

注意第1行的形参类型是个指针变量int *b,第12行将数组名a当做实参传入函数。

由第2行可以看出,在很多情况下,指针和数组是可以相互切换使用的。但是,并不能说指针就等于数组。

 

 

九、用指针遍历字符串的所有字符代码以及内存分布

 1 // 定义一个指针p
 2 char *p;
 3 
 4 // 定义一个数组s存放字符串
 5 char s[] = "mj";
 6 
 7 // 指针p指向字符串的首字符‘m‘
 8 p = s; // 或者 p = &s[0];
 9 
10 for (; *p != ‘\0‘; p++) {
11     printf("%c \n", *p);
12 }

执行完第8行后,内存分布如右图:黑 马 程 序 员_视频学习总结<c语言>----03 指针

 

十、用指针直接指向字符串代码

 1 #include <string.h>
 2 
 3 int main()
 4 {
 5     // 定义一个字符串,用指针s指向这个字符串
 6     char *s = "mj";
 7     
 8     // 使用strlen函数测量字符串长度
 9     int len = strlen(s);
10     
11     printf("字符串长度:%D", len);
12     return 0;
13 }

 

十一、指针处理字符串的注意

现在想将字符串"lmj"的首字符‘l‘改为‘L‘,解决方案是多种的

1.第一种方案

1 // 定义一个字符串变量"lmj"
2 char a[] = "lmj";
3 
4 // 将字符串的首字符改为‘L‘
5 *a = ‘L‘;
6 
7 printf("%s", a);

 

2.第二种方案

1 char *p2 = "lmj";
2 *p2 = ‘L‘;
3 
4 printf("%s", p2);

看起来似乎是可行的,但这是错误代码,错在第2行。首先看第1行,指针变量p2指向的是一块字符串常量,正因为是常量,所以它内部的字符是不允许修改的。

有人可能搞蒙了,这里的第1行代码char *p2 = "lmj";跟第一种方案中的第2行代码char a[] = "lmj";不是一样的么?这是不一样的。

  • char a[] = "lmj";定义的是一个字符串变量!
  • char *p2 = "lmj";定义的是一个字符串常量!

 

十二、返回指针的函数

返回指针的函数的一般形式为:类型名 * 函数名(参数列表)

 

十三、指向函数的指针

1.函数作为一段程序,在内存中也要占据部分存储空间,它也有一个起始地址,即函数的入口地址。函数有自己的地址,那就好办了,我们的指针变量就是用来存储地址的。因此,可以利用一个指针指向一个函数。其中,函数名就代表着函数的地址。

2.指向函数的指针的一般形式:函数的返回值类型 (*指针变量名)(形式参数1, 形式参数2, ...);

注意:形式参数的变量名可以省略,甚至整个形式参数列表都可以省略

 

 

 

 

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------

详细请查看:www.itheima.com

黑 马 程 序 员_视频学习总结<c语言>----03 指针,布布扣,bubuko.com

黑 马 程 序 员_视频学习总结<c语言>----03 指针

上一篇:Java中的空值判断


下一篇:Java访问控制权限