12天学好C语言——记录我的C语言学习之路(Day 9)

时间:2023-12-14 13:56:50

12天学好C语言——记录我的C语言学习之路



Day 9:



函数部分告一段落,但是我们并不是把函数完全放下,因为函数无处不在,我们今后的程序仍然会大量运用到函数



//转入指针部分的学习,了解指针是什么



/*//program 9.1

 #include<stdio.h>

 int main()

 {

 int c=123,d=345;

 int *a,*b;//定义两个整型的指针变量(int 是指针变量的基类型,我们必须在定义指针变量的时候指定基类型,因为变量的类型不同决定了他们在内存中所占字节的不同,我们只知道地址2000(起始地址),但是我们不知道是在2000一个字节中取一个char型数据,还是在2000~2003 四个字节中取一个int 型数据)

 a=&c;

 b=&d;

 printf("变量c的起始地址为%d,变量d的起始地址为%d\n",a,b);//format specifies type 'int' but the argument has type 'int *' 警告是什么意思。

 //这里输出的是a,b表示的变量(c,d)的起始地址(变量c的起始地址为1606416376,变量d的起始地址为1606416372)

 printf("*a=%d,*b=%d",*a,*b);//这里输出的是指针变量a,b指向的 地址 中的 值 (*a=123,*b=345),加星号*说明这个变量具有指向性,所以他表示的应该是指向的那个地址中变量的值

 return 0;

 }

 */



///这个比较大小,不是直接比较的变量a、b,而是比较的指针变量*p1、*p2指向的a、b的地址中的值



/*//program 9.2

 #include "stdio.h"

 int main()

 {

 int *p1,*p2;

 int a,b,*temp;

 scanf("%d%d",&a,&b);

 p1=&a;

 p2=&b;

 if(a>b)

 {

 temp=p1; //地址互相交换

 p1=p2;

 p2=temp;

 }

 printf("较大的值为%d",*p2);

 return 0;

 }

 */



//比较大小的时候完全不用通过第三个值交换,可以通过互相交换地址得到,这样改变的是指针变量的值(a与b的地址),而不变两个整型变量的值。      当然下面的解法更简单,一共两个变量a、b,只要比较完之后让指针指向较大的,就ok了。



/*//program 9.3

 #include <stdio.h>

 int main()

 {

 int a,b;

 int *p1;

 p1=&a;

 scanf("%d%d",&a,&b);

 if (a<b) {

 p1=&b;

 }

 printf("较大的数是%d",*p1);

 }

 */



//用指针变量作为函数参数,然后将输入的两个整数输出最大值最小值



/*//program 9.4

 #include "stdio.h"

 int main()

 {

 int a,b;

 scanf("%d%d",&a,&b);

 int *p1=&a;

 int *p2=&b;

 void maxmin(int *m,int *n);

 maxmin(p1,p2);

 return 0;

 }

 void maxmin(int *m,int *n)

 {

 int *temp;

 if(*m<*n)//这个程序交换的是指针变量的值,也就是交换的地址,地址改变了,但是&a存的还是a原本赋给他的值,&b中存的也是b原本的值。如果a>b则不执行交换,输出max是*p1(&a中的值),min是*p2(&b中的值),这没错,但是如果a<b,那么执行了函数中的地址交换,指针变量p1中存的值变成了&b,那么输出max是*p1(&b中的值),也是正确的。所以说改变指针变量的值(地址)也是可行的办法

 {

 temp=m;

 m=n;

 n=temp;

 }

 printf("max is %d,min is %d",*m,*n);

 }

 

 */



数组名也就是数组的首地址,即首元素地址



/*//program 9.5

 #include<stdio.h>

 int main()

 {

 int a[10]={1,2,3,4,5,6,7,8,9,10};

 void fun(int *arr);//这里就可以等于 void fun(int a[]);  因为*arr表示的是数组的首元素,也就是a[0]。  当数组名作为函数参数的时候,实参数组名表示该数组首元素地址,而形参是用来接受传递过来的数组元素的受地址的。

 fun(a);

 return 0;

 }

 void fun(int *arr)

 {

 printf("%d\n",*arr);

 arr++;

 printf("%d\n",*arr);

 }

 */



//逆序输出一个整型数组,用指针变量作为实参(指针变量作为实参的时候,必须先让指针变量具备确定的值)

//int f(int a[],int n)  与  int f(int *a,int n)  是等价的



/*//program 9.6

 #include <stdio.h>

 int main()

 {

 int a[10]={1,2,3,4,5,6,7,8,9,0};

 int *p;

 p=a;//给指针变量赋上确定的值

 void abc(int *p,int n);

 abc(p,10);

 for(p=a;p<a+10;p++)

 {

 printf("%d\n",*p);

 }

 return 0;

 }

 void abc(int *p,int n)

 {

 int *i,*j,temp;

 for (i=p,j=p+n-1; i<=p+(n-1)/2; i++,j--) {//这个地方i与j的初始值一定别错,还有就是i循环到什么地方结束?(也就是for循环的表达式2),等确定好这些值的时候再写程序,快速还不容易出错

 temp=*i;

 *i=*j;

 *j=temp;

 }

 }

 */



当然上面的程序未免太复杂,是可以优化的(program 9.7)



/*//program 9.7

#include <stdio.h>

int main()

{

    int a[10]={1,2,3,4,5,6,7,8,9,0};

    int *p=a+9;

    for (; p>=a+0; p--) {

        printf("%d\n",*p);

    }

    return 0;

}

*/



//用指针方法对10个整数进行排序(选择排序法)



//指针的选择排序法。其实就是用指针变量作为函数的形参和实参,然后在函数中交换值的时候用的是指针变量的地址中的值



/*//program 9.8

#include "stdio.h"

int main()

{

    int a[10]={2,42,34,65,76,887,69,80,1,23};

    int *p=a;

    void abc(int *q,int n);

    abc(a,10);

    for (p=a; p<a+10; p++)//用p,也就是地址来控制整个输出,其中p=a是将数组a的首地址赋给了p,作为p的初始地址(也就是a[0]的地址)

    {

        printf("%d\n",*p);

    }

    return 0;

}

void abc(int *q,int n)

{

    int temp,i,j,k;

    for (i=0; i<n-1; i++) {

        k=i;

        for (j=i+1; j<n; j++) {//j只是控制了循环的次数

            if (*(q+k)<*(q+j)) {

                k=j;

            }

        }

        temp=*(q+k);

        *(q+k)=*(q+i);

        *(q+i)=temp; //k指代的是需要和i换的值,k和i进行交换

    }

}

*/



下面再用指针方法写一下冒泡排序法。其实都是换汤不换药的。



//用指针变量做函数形参和实参,对十个整数进行排序,冒泡排序法



/*//program 9.9

 #include "stdio.h"

 int main()

 {

 int a[10]={23,121,24,1,243,65,77,6,87,8};

 int *p=a;

 void bcd(int *b,int n);

 bcd(p,10);

 for (p=a; p<a+10;p++) {

 printf("%d\n",*p);

 }

 return 0;

 }

 void bcd(int *b,int n)

 {

 int i,j,temp;

 for (j=0; j<n-1; j++) { //j表示第几趟循环

 for (i=0; i<9-j; i++) { //i表示每趟循环需要哪些数(i就是指代元素的下标)往下进行比较,因为一次循环将有一个数字沉底,所以说再从第一个元素a[0]开始与下面的每个数进行比较时,比较的数量随着循环一次次的结束而减少(下面沉底的数不需要比较了)

 if(*(b+i)<*(b+i+1))

 {

 temp=*(b+i);

 *(b+i)=*(b+i+1);

 *(b+i+1)=temp;

 }

 }

 }

 }

 */



对于表示二维数组有关指针的格式,确实有很多,比较复杂,记起来不容易,下面我为大家整理了一下:



//对指针变量表示二维数组的格式小探究



/*//program 9.10

#include <stdio.h>

int main()

{

    int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};

    printf(“%d\n",*(*a+1));    //*a+1是0行1列元素的地址

    printf(“%d\n",*(*(a+1)));    //*(a+1)是1行0列的元素的地址

    return 0;

}

*/



/*                                

                   二维数组的有关指针的表示形式

 有这么一个数组:  a[3][3]

 ①一般我们看到二维数组名,那么表示的就是该二维数组的首地址(该二维数组的首地址就是该二维数组首行的地址,也就是0行的首地址)                        a 代表的就是0行的地址

 ②我们看到二维数组中的一维数组名,那么表示的就是该行的首地址(该行的首地址就是该行首元素的地址,可以直接把它看成一维数组来理解,一维数组的数组名不就是表示的是该一维数组首元素的地址么,在循环输出一维数组的时候,我们常用for语句将p的初始值定为a,p=a)     a[0]代表的就是0行0列的元素地址

 ③还有一种形式,比如说 a[1]+2 ,显然这是1行两列的元素a[1][2]的地址

 ④这种形式是最直观的    &a[1][2]  ,显然这是表示元素a[1][2]的地址

 ⑤然后就是一种通用的格式,这种格式往往省略某一部分,一定要把你看到的地址形式带到通用式子里去,看看究竟表示什么。

 这个式子就是   *(a+i)+j   这个形式非常的普遍,a是0行的地址,在a的基础上变化i也就是对行的变化,另外一个j肯定就是对列的变化了。我们平时做题的时候一般这种形式是给不全的,一般都是没有i,或者是没有j。如下面这几种形式

 *a+1  这显然可以看成 *(a+0)+1 ,所以这表示的是0行1列元素的地址

 *(a+1)  这显然可以看成 *(a+1)+0 ,也就是表示1行0列元素的地址

 *a  0行0列元素的地址

 *(a+2)+1   2行1列的元素的地址

 ⑥最后一种表示形式是对元素的值的表示(这次不是地址了),如下:

 *(*(a+1)+1)   在表示1行1列元素地址的外面加星号和括号,表示1行1列的元素的值,当然也可以写成 *(a[1]+1)

★★★通用格式为:    *(*(a+i)+j)或者*(a[i]+j)  都表示的是i行j列元素的值★★★

⑦最后一句话,二维数组名 是指向 行 的,而 一维数组名 是指向 元素的

*/



写一个程序:



//输入一个二维数组,然后有指针变量输出二维数组里面的元素的值



/*/program 9.11

#include "stdio.h"

int main()

{

    

    printf("请输入二维数组的维度:\n");

    int m,n;

    scanf("%d%d",&m,&n);

    int a[m][n];

    printf("请为这个二维数组赋值:\n");

    for (int i=0; i<m; i++) {

        for (int j=0; j<n; j++) {

            scanf("%d",*(a+i)+j);

        }

    }

     

    int *p;

    for (p=*a; p<*a+m*n; p++) {//将p依次指向下一个元素,显然一个用m*n个元素

        if ((p-a[0])%n==0) {

            printf("\n");

        }

        printf("%5d",*p);//因为二维数组中的元素地址是连续的,所以我们完全可以直接从第一个输出到最后一个,不用嵌套两个i、jfor循环

    }

    return 0;

}

*/



下面我们将认识二维数组做函数的形参,怎么声明定义,好好看这里。



//3个学生,四门课的成绩,求总平均成绩和第n名学生的所有成绩

/*//program 9.12

#include "stdio.h"

int main()

{

    int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};

    void abc(int (*b)[4],int n);//这个地方二维数组作为函数形参的声明和一维数组作为函数形参时的声明相仿,还是将二维数组看成多个一维数组的组合,声明的时候代表 列 的位置还是照样写上面,而代表 行 的位置要用指针变量表示,函数的int *b[4]这个地方要求我们传入的实参必须是一维数组的地址

    void average(int *b,int n);//求所有元素的平均值,自然要从第一个元素开始,把所有的元素加起来然后除以元素个数,而我们声明的这个函数正是如此,*b我们知道就代表*(b+0)+0,也就是a[0][0]的地址,从这个地址开始,一直到最后一个的地址(第n个),取里面的数据相加。所以这个函数应该如此声明。换一种考虑,一维数组怎么声明来着?void a(int *p,int n); 因为传入的是首元素的地址,对元素的地址进行操作,要用的是元素地址里面的值,所以说多维数组用指针变量求平均值,其实和一维数组求平均值一样,都是对元素进行操作,看作一维数组就好了

    abc(a,3);//实参是首行的地址

    average(*a,12);//实参传入a[0][0]的地址,这个地方就不能和一维数组一样传入数组名了,因为这里a毕竟是二维数组的数组名

    return 0;

}

void abc(int (*b)[4],int n)

{

    int i=0;

    for (i=0; i<4; i++) {

        printf("%-4d",*(*(b+n-1)+i));

    }

    printf("\n");

}

void average(int *b,int n)

{

    int *p=b;

    int sum=0,aver;

    for (p=b; p<b+12; p++) {

        sum+=(*p);

    }

    aver=sum/n;

    printf("average=%d",aver);

}

*/



今天对指针的理解就到此为止,一开始可能觉得挺难理解的,但是晚上睡觉的时候多想想,就差不多了。记住指针也是变量的一种类型,指针变量存的是地址!

版权声明:本文为博主原创文章,未经博主允许不得转载。