C语言 | 第十四章 | 指针运算 指针数组 多重指针

P 131 断点调试应用案例(2)2023/2/3

一、应用案例

案例一:用调试查看数组越界异常。

#include<stdio.h>
void main(){
		int arr[] = {1,2,3,4,5};
		int i = 0;
		int len = sizeof(arr) / sizeof(int);
		for(i = 0; i <= len; i++) {  //  这里不应该=,用调试看一下
			printf("\narr[%d]=%d", i, arr[i]);
		}
	}

image-20230203165634917

案例二:演示如何进入到调用的函数体内,f11(进入到函数体),shift + f11 (跳出函数)。

#include<stdio.h>
#include<stdlib.h>
#include"myfun.h"  // 此时引用了函数
void main(){

		int n1 = 10;
		int n2 = 40;
		char oper = '+';
		double res = cal2(n1,n2,oper);
		printf("res=%.2f", res);
		system("pause");
	}

image-20230203171612405

P 132 断点调试应用案例(3)2023/2/4

一、应用案例

image-20230204111505838

二、总结

理解逐过程和逐语句的操作,灵活使用相应的快捷键。(可以使用断点调试查看冒牌排序的运行过程)

P 133 指针回顾 2023/2/4

一、基本介绍

  1. 指针是C语言的精华,也是C语言的难点。

  2. 指针,也就是内存的地址;所谓指针变量,也就是保存了内存地址的变量。关于指针的基本使用,在讲变量的时候做了入门级的介绍。

  3. 获取变量的地址,用&,比如: int num = 10, 获取num的地址:&num。

  4. *指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值比如:int ptr = & num; ptr 就是指向 int 类型的指针变量, 即 ptr 是 int * 类型。

  5. 获取指针类型所指向的值,使用:(取值符号),比如:int * ptr , 使用ptr获取ptr指向的值。

二、什么是指针

指针是一个变量,其值为另一个变量的地址(前示意图已经说明),即,内存位置的直接地址。就像其他变量或常量一样,在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:

int *ip; /* 一个整型的指针 */    // 相当于这个ip的空间里面存放的地址必须是一个int变量的地址!!!
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */

P 134 指针的自增和自减运算 2023/2/5

一、指针的算术运算

介绍:指针是一个用数值表示的地址。可以对指针执行算术运算。可以对指针进行四种算术运算:++、–、+、-

案例演示:自增

#include<stdio.h>

	const int MAX = 3;  // 常量
	int main () {
		int var[] = {10, 100, 200}; //  int 数组
		int i, *ptr; //  ptr是一个int*指针

		ptr = var;  // ptr指向了var数组的首地址
		for ( i = 0; i < MAX; i++) {
			printf("var[%d] 地址= %p \n", i, ptr );
			printf("存储值:var[%d] = %d\n", i, *ptr );  // * 取值符,通过指针也可以把值取出来
			ptr++; // ptr++ = ptr +1;ptr存放的值会加4个字节(int)
						// +1(1个int的字节数),而是加一个单位,不是加一个字节
		}
		getchar();
		return 0; 
	}

结果:

image-20230205162421789

内存图:

image-20230205162700595

案例演示:自减(同理++)

#include<stdio.h>
const int MAX = 3;
int main () {
	int var[] = {10, 100, 200};
	int i, *ptr;
	/* 指针中最后一个元素的地址 */
	ptr = &var[MAX-1];  // &var{2}
	for ( i = MAX; i > 0; i--) {  // 反向遍历
		printf("ptr存放的地址=%p\n", ptr);
		printf("存储值:var[%d] = %d\n", i-1, *ptr );
		ptr--;
	}
	getchar();
	return 0;
}

总结:

  1. 数组在内存中是连续分布的。

  2. 当对指针进行–时,指针会按照它指向的数据类型字节数大小减少,比如 int * 指针,每-- , 就减少4个字节。

P 135 指针加减运算 2023/2/6

一、指针+、- 操作

#include<stdio.h>

int main () {
	int var[] = {10, 100, 200};
	int i, *ptr;

	ptr = var; // 将var的首地址赋给ptr
	ptr += 2; // ptr的存储的地址 + 2 个int的字节(8个字节)
//  ptr = &var[2];  将var[2]的首地址赋给ptr (加减同理)
// 	ptr -= 2;   ptr的存储的地址 - 2 个int的字节(8个字节)
	printf("var[2]=%d var[2]的地址=%p ptr存储的地址=%p ptr指向的值=%d", var[2], &var[2], ptr, *ptr);
	getchar();
	return 0;
}

image-20230206131442723

P 136 指针的课堂练习 2023/2/6

一、课堂练习

案例一:最终输出?

int main ()
{
	int var[] = {10, 100, 200, 400, 9, 12};
	int i, *ptr;
	ptr = &var[2]; // ptr 指向了 第3个元素
	ptr -= 2; 
	printf("ptr指向的值=%d", *ptr); // 10
	getchar();
	return 0;
}

P 137 指针的比较 2023/2/14

一、指针的比较

指针可以用关系运算符进行比较,如 ==、< <= 和 > >=。如果 p1 和 p2 指向两个变量,比如同一个数组中的不同元素,则可对 p1 和 p进行大小比较, 看下面代码,说明输出什么?

案例一:

#include<stdio.h>

int main () {
	int var[] = {10, 100, 200};
	int *ptr;
	ptr = var;//ptr 指向var 首地址(第一个元素)
	if(ptr == var[0]) {//错误,类型不一样,因为前面是个地址(int*),后面是int
							// int* 和 int 是不一样的

		printf("ok1");
	}
	if(ptr == &var[0]) { // 可以
		printf("\nok2"); //输出
	}
	if(ptr == var) { // var等价于 &var[0],可以 
		printf("\nok3"); //输出
	}
	if(ptr >= &var[1]) { //可以比较【根据布局图】,但是返回false
		printf("\nok4");//不会输出
	}
	getchar();
}

案例二:

#include<stdio.h>

const int MAX = 3; 
int main () {
	int var[] = {10, 100, 200};
	int i, *ptr;
	ptr = var;
	i = 0;
	while ( ptr <= &var[MAX - 2] )//&var[1]
	{
		printf("Address of var[%d] = %x\n", i, ptr );  // %x是以十六进制输出
		printf("Value of var[%d] = %d\n", i, *ptr );
		ptr++;
		i++;
	} //会输出 10 , 100
	getchar();
	return 0; }

P 138 指针数组介绍和应用 2023/2/14

一、指针数组

基本介绍:要让数组的元素 指向 int 或其他数据类型的地址(指针)。可以使用指针数组。

指针数组定义:数据类型 *指针数组名[大小];

比如: int *ptr[3];

  1. ptr 声明为一个指针数组
  2. 由 3 个整数指针组成。因此,ptr 中的每个元素,都是一个指向 int 值的指针。

二、内存布局图和案例

案例一:

#include<stdio.h>
const int MAX = 3;
int main (){
	int var[] = {10, 100, 200};
	int i, *ptr[3];

	for ( i = 0; i < MAX; i++){
		ptr[i] = &var[i]; /* 赋值为整数的地址 */
	}

	for ( i = 0; i < MAX; i++){
		printf("Value of var[%d] = %d ptr[%d]本身的地址 = %p\n", i, *ptr[i],i,&ptr[i]); 
        // 通过ptr[i]取出地址,* 取值
		// 这样就取出了这个地址对应的值
	}

	getchar();
	return 0;

}

内存布局图:

image-20230214210836290

image-20230214213243493

案例二:请编写程序,定义一个指向字符的指针数组来存储字符串列表(四大名著书名), 并通过遍历 该指针数组,显示字符串信息 , (即:定义一个指针数组,该数组的每个元素,指向的是一个字符串)

#include<stdio.h>

void main(){

	// 定义一个指针数组,该数组的每个元素,指向的是一个字符串!!!
	char *books[] = {   // 代表指针数组
		"三国演义",
		"西游记",
		"红楼梦",
		"水浒传"
	};
	char *pStr = "abc"; // 字符串是直接把字符串常量就给到了它了(无需加*)
    
	int num =10;
    int *pNum = &num; // 而这里要把地址赋给它(需要加*)
    
	// 遍历
	int i,len = 4;
	for(i = 0; i < len; i++){
		printf("\nbooks[%d]指向字符串是=%s pStr = %s",i,books[i],pStr);
// 这里其实是不能加*号的,%s本身就是从指针位置输出,知道'\0',所以传的是指针
// 指向字符串的指针不需要取值(可以理解为:字符串本身就是字符数组,*无法一次取数组的多个值)
	}
	getchar();
}

P 139 多重指针数组应用 2023/2/15

一、基本介绍

多重指针(指向指针的多重指针):指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置(如下图)

image-20230215165248242

二、案例说明

  1. 一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。例如,下面声明了一个指向 int 类型指针的指针: int ** ptr;
int ** ptr;   // ptr 的类型是 int **
  1. 当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符, 比如 **ptr。

  2. 案例演示+内存布局图

#include<stdio.h>

int main () {
	int var;
	int *ptr; // 一级指针
	int **pptr; //  二级指针
	int ***ppptr; //  三级指针

	var = 3000;  
	ptr = &var;  // var变量的地址赋给ptr

	pptr = &ptr; // 表示将ptr存放的地址,赋给pptr

	ppptr = &pptr;  // 表示将pptr存放的地址,赋给ppptr

	printf("var的地址=%p var = %d \n", &var, var );
	printf("ptr 的本身的地址=%p ptr存放的地址=%p*ptr = %d \n", &ptr, ptr, *ptr );
	printf("pptr 本身地址 = %p pptr存放的地址=%p **pptr = %d\n", &pptr, pptr, **pptr);
	printf("ppptr 本身地址 = %p ppptr存放的地址=%p ***pptr = %d\n", &ppptr, ppptr, ***ppptr);
	getchar();
	return 0;
}

结果:

image-20230215171823928

内存布局图:

image-20230215171048147

P140 传递指针(地址)给函数 2023/2/15

一、基本介绍

当函数的形参类型是指针类型时,是使用该函数时,需要传递指针,或者地址,或者数组给该形参,举例说明:

二、案例说明

案例一:传地址或者指针给指针变量

#include<stdio.h>

void test2(int *p);   // 函数声明,接收int*

void main() {
	int i, num=90;
	int *p = &num;  // 将num地址赋给p
	test2(&num); //传地址
	printf("\nmain() 中的num=%d", num);  // nun=91,调用了函数,因为为地址传递

	// 直接将p传入进去,同样可以,因为p就是一个指针,里面存放的是指针保存的地址
	test2(p); //传指针
	printf("\nmain() 中的num=%d", num);  // 再次打印就变成了92
	getchar();
}

// 函数写在main函数之前就不需要声明,写在main函数之后就需要在main函数之前声明
void test2(int *p) {
	*p += 1;  // *p就访问num的值,* 取值符,直接指向了实际值
}

案例二:传数组给指针变量

  • 数组名本身就代表该数组首地址,因此传数组的本质就是传地址。
#include<stdio.h>

/* 函数声明 */
double getAverage(int *arr, int size);
double getAverage2(int *arr, int size);

int main ()
{
	/* 带有 5 个元素的整型数组 */
	int balance[5] = {1000, 2, 3, 17, 50};
	double avg;
	/* 传递一个指向数组的指针作为参数 */
	avg = getAverage( balance, 5 ) ; 
	// balance就是数组首地址,在函数里面操作就是在操作数组

	/* 输出返回值 */
	printf("Average value is: %f\n", avg );
	getchar();
	return 0;
}

// 说明
// 1.arr是一个指针
double getAverage(int *arr, int size)
{
	int i, sum = 0; 
	double avg; 
	for (i = 0; i < size; ++i)
	{
		printf("\narr存放的地址%p",arr);
		// 这里输出地址是不会改变的,但是使用arr++会改变地址
		sum += arr[i]; // arr[0] -->数组第一个元素的地址
		// 可以通过arr[i]和arr++来访问不同的值(一个遍历地址,一个遍历数组下标)
		// 但是arr++可以改变里面的地址
	}
	avg = (double)sum / size;
	return avg;
}

double getAverage2(int *arr, int size)
{
	int i, sum = 0; 
	double avg; 
	for (i = 0; i < size; ++i)
	{
		sum += *arr;  // * 取值符的,然后取出里面值累加,下面进行指针运算值改变
		printf("\narr存放的地址%p",arr);
		// 这里地址会被做修改
		arr++;  // 指针的++运算,增加一个int单位
	}
	avg = (double)sum / size;
	return avg;
}
  • 思考题:如果在getAverage()函数中,通过指针修改了数组的值,那么main函数的balance数组的值是否会相应变化?
  • 解答:会的因为getVerage函数中的指针,指向的就是main函数的数组。

三、内存布局图

image-20230215210200130

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一颗星星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值