自学内容网 自学内容网

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


原文地址:https://blog.csdn.net/ZJC744575/article/details/142826458

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!