C语言指针入门

本人系C语言新人,指针与数组也折腾了几日,下面把自己这几天学习的总结与体会与大家分享下,若有不足,敬请指正。
在这里,不就定义做过多的纠结,主要以程序示例为主。

一、指针简单指向一个int型的变量。
int *p;  //定义一个int型的指针变量
int a=3;
p=&a;
printf("%p",p);  //输出a的地址
printf("%d",*p);   //输出a的值
输出结果

二、指针与函数
1、指针作为函数参数实现swap函数
下面以一个数值交换函数来讲解指针作为函数参数的作按照如下用。众所周知,如果想写一个函数,实现两个int型数字交换。如下方式声明函数是行不通的
void swap(int a,int b);
应该按照如下方式声明:
void swap(int *a, int*b);
下面给出一个验证程序来比较这两个函数的差异所在:
#include<stdio.h>
void swap1(int m , int n);
void swap2(int *x ,int *y);
int main() 
{
    int a=3;
    int b=4;
    int c=5;
    int d=6;
    printf("%d%d\n",a,b);   //输出a,b初始值
    printf("%d%d\n",c,d);   //输出c,d的初始值
    swap1(a,b);             //利用普通的方法试图对a,b的值互换
    swap2(&c,&d);           //利用指针方法进行交换    
    printf("%d%d\n",a,b);    //输出结果
    printf("%d%d",c,d);      //输出结果
    return 0;
}
void swap1(int m , int n) 
{
    printf("%d%d\n",m,n);
    int k;
    k=m;
    m=n;
    n=k;
    printf("%d%d\n",m,n);   
}
void swap2(int *x ,int *y)
{
    int k;
    k=*x;
    *x=*y;
    *y=k;   
}
输出结果:

这里写图片描述

2、利用指针返回多个值
众所周知,函数只能返回一个返回值,利用指针我们可以返回多个返回值。下面给出程序示例:
#include<stdio.h> 
void cal(int a, int b, int *c, int *d);

int main()
{
    int c;
    int d;
    cal(3,2,&c,&d);
    printf("c:%d\n",c);
    printf("d:%d",d);
    return 0;
} 

void cal(int a, int b, int *c, int *d)
{
    *c=a+b; 
    *d=a-b;
} 
三、指针与一维数组的关系
如果要通过指针对数组进行操作,通常,我们先通过如下语句指针指向数组的首地址:
#include<stdio.h>
int main()
{
    int *p;
    int *p1;
    int a[]={1,2,3};
    p=a;
    p1=&a;
    printf("%X\n",p);
    printf("%X",p1);
    return 0;
}
接下来我们输出p1,p2:
1、指针对一维数组的基本操作

这里写图片描述

可以看到,p1,p2在数值上是相等的。但他们在意义上并不相同,p=&a取得是整个数组的首地址,而p=a取得是a[0]数组元素的地址,两者在数值上相等。但这一点并不影响我们利用指针对一维数组进行操作,但这一点在数组指针里将会产生影响。关于这一点我们将在后面讲到。
接下来我们通过程序示例演示指针对一维数组的一些基本操作。
#include<stdio.h>
int main()
{
    int *p;
    int *p1;
    int a[]={1,2,3};
    p=a;
    printf("%d\n",*p);     //等价于s输出a[0] 
    printf("%X\n",p);      // 输出a[0]地址 
    printf("%d\n",*(p+1));   //等价于s输出a[1] 
    printf("%X\n",p+1);     // 输出a[1]地址 
    printf("%d\n",*(p+2));   //等价于s输出a[2] 
    printf("%X\n",p+2);     // 输出a[2]地址 
    return 0;
}
值得注意的是,这里的p+1并不是单纯地+1,而是加一个int型变量所占的字节数
2、指针作为函数参数对一维数组的操作
指针作为函数参数,一定程度上可以与一维数组互相转换:
下面给出一个程序实现字符串的复制来讲解这个问题:
#include<stdio.h>
void str_copy(char *a, char *b);  
int main()
{
    int length;
    char b[]="original";
    length=sizeof(b)/sizeof(char); 
    char a[length];
    str_copy(a,b);
    puts(a);
    return 0;
} 

void str_copy(char *a,char *b)
{
    while(*a++=*b++);
}
这里实质上是传入了数组a与数组b的首地址给函数str_copy的形参。
四、指针操纵二维数组
首先我们来观察下二维数组中各个元素的地址在内存中排列的情况:
#include<stdio.h>
int main()
{
    int a[][3]=
    {
        {1,2,3}, 
        {1,2,3},
        {1,2,3},
    };
    for(int i=0;i<3;i++)
    {
        for(int j=0;j<3;j++)
        {
            printf("%X ",&a[i][j]);
        }
        putchar('\n');
    }
    return 0;
}
输出结果如下:

这里写图片描述
实质上,二维数组本质上也是一个一维数组:
这里写图片描述
其元素地址和一维数组一样在内存中也是顺序排列,所以,操纵二维数组的思路大体与操纵一维数组相同,下面给出一个程序示例,来判断一个矩阵(二维数组),是否为对称矩阵:

#include<stdio.h>
int sym(int *a);
int main()
{
    int a[5][5]={
                 {1,1,1,1,1},
                 {1,1,1,1,1},
                 {1,1,1,1,1},
                 {1,1,1,1,1},
                 {1,1,1,1,1}
                 };
    for(int i=0;i<5;i++)
    {
        for(int j =0;j<5;j++)
        {
            printf("%d ",a[i][j]);
        }
        putchar('\n');
    }

    int result=sym(&a[0][0]);

    if(result)
    {
        printf("是对称矩阵");        
    }
    else
    printf("不是对称矩阵"); 
    return 0;
} 

int sym(int *a)     // i  j 
{   
int judge[25];
int k=0;
for(int i =0;i<5;i++)
    {
        for(int j=0;j<5;j++)
        {
            if (*(a+5*i+j)==*(a+5*j+i))
            {
                judge[k]=1;
                k++;
            }
            else
            {
                judge[k]=0;
                k++;
            }                       
        }   
    }
    int n=1;
    for(int m=0;m<25;m++)
    {       
        n=n*judge[m];
    }
    if(n==1)
    return 1;
    else
    return 0;   
}
这里再补充一些指针与二维数组在表示上的联系。
首先我们先回顾一维数组里数组与指针的关系:
a[1]表示一维数组中第二个元素的值,其对应的指针表示的方法:*(p+1).此时表示的是
我们知道,对于一个二维数组,a[0],a[1],表示的是第一行,第二行数组第一个元素的地址。所以,在二维数组里,*p,*(p+1)表示的是第一行和第二行数组的地址.即&a[0][0],&a[1][0].
如果要求二维数组中第二行第二列元素的地址和值我们该怎么做呢?(假设这是一个3*3的矩阵)
地址:&a[1][1],a[1]+1,*(a+1)+1或者按照对称矩阵里的写法:&a[0][0]+4.
    printf("%X\n",&a[1][1]);
    printf("%X\n",a[1]+1);
    printf("%X\n",*(a+1)+1);
    printf("%X\n",&a[0][0]+4);
输出结果为:

这里写图片描述

值:
    printf("%d\n",a[1][1]);
    printf("%X\n",*(a[1]+1));
    printf("%X\n",*(*(a+1)+1));
    printf("%X\n",*(&a[0][0]+4));
输出结果:

这里写图片描述

这里给出来自谭浩强主编的《C程序设计》的一个程序再次来区分这几个看似类似的表示方法(第四版):
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
printf("%d,%d\n",a,*a);
printf("%d,%d\n",a[0].*(a+0));
printf("%d,%d\n",&a[0],&a[0][0]);
printf("%d,%d\n",a[1],a+1);
printf("%d,%d\n",&a[1][0],*(a+1)+0);
printf("%d,%d\n",a[2],*(a+2));
printf("%d,%d\n",&a[2],*(a+2));
printf("%d,%d\n",&a[2],a+2);
printf("%d,%d\n",a[1][0],*(*(a+1)+0));
printf("%d,%d\n",*(*(a+2)+0)); 
请读者自己读程序写结果自己做验证。
这里指针表示二维数组与上面对称矩阵程序中所示的表示法都可以,其原理都是一样,都是对地址的操作。
*五、
1、指针数组与二维数组的关系
首先,我们需要明确的是,什么是指针数组。首先我们来看看指针数组是如何声明的。
int *p[3];
“[]”的优先级比“ * ”要高。p1 先与“[]”结合,构成一个数组的定义,数组名为p,int *修饰的是数组的内容,即数组的每个元素。
所以,这里int *p[3]的意思是,定义了一个长度为3的数组,其中的每个元素都为指针。
,由于其每个元素都是指针,指针数组通常用来操纵二维数组和字符串数组下面,我们通过具体的程序来讲解指针数组。
( 1 )如何输出每个元素?
#include<stdio.h>
int main()
{
    char *p[] ={"dfd","fddf","fdf"};
    for(int i=0;i<3;i++)
    {
        printf("%s\n",p[i]);  //或者*(p+i)
    }
    return 0;
}
输出结果

这里写图片描述

注意:这里需要理解为什么要用p[i]输出。在这里,p[i]表示的数组里第i个元素.表示的即为对应的变量。既然如此,我们只要在之前加上&就可以取出地址。
#include<stdio.h>
int main()
{
    char *p[] ={"dfd","fddf","fdf"};
    for(int i=0;i<3;i++)
    {
        printf("%X\n",&p[i]);
    }
    return 0;
}
输出结果:

这里写图片描述

每个指针占两个字节,所以这里输出的是每个指针所指元素的地址。
接下来,我们演示一个取出字符串数组的每个字符的程序来展示数组指针具体怎么用:
#include<stdio.h>
int main()
{
    char *p[] ={"dfd","fddf","fdf"};
         //p[0]--->char "dfd"
    for(int i=0;i<12;i++)
    {
        printf("%c ",*(&p[0][0]+i));
        if(i==3||i==7||i==11)
        {
            putchar('\n');
        }
    }
    return 0;
输出结果:

这里写图片描述

这里我也不清楚为什么会自动填充一个字符a.

2、指针数组与一维数组的关系
*六、数组指针
首先,我们得要弄清一个问题,什么是数组指针?和指针数组又是有什么区别。当然了,二者的区别是很明显的。指针数组,前面已经提到,本质上是数组,只不过数组里的每个元素是一个指针。而数组指针本质上是一个指针,而这个指针是指向一个数组的。
首先,我们来看一个程序,此时指针指向一个一维数组。
    int (*p)[6];
    int *k; 
    int a[]={176,2,3,4,5,6}; 
    p=&a; 
    printf("%d\n",*(*p+1));
    printf("%d\n",*(p[0]+1));
    printf("%X\n",p);
输出结果:

这里给出的示例程序是输出一维数组中的第二个元素,大部人遇到的问题是不知该怎么用指针去对应的表示这个数组,这里主要讲一下这个问题。
首先,我们看一下数组指针的定义,int (*p)[6],首先()的优先级更高,所以p首先是一个int型的指针。所以int ( *p)[6]意为指向一个有四个元素的int型数组。
接下来,来看看为什么要写成p=&a, 而不是p=a.&a 意为&a是整个数组的首地址,a是数组首元素的首地址.这个,p只要指向整个数组,而不是指向某个元素,所以如果在程序中写成p=a,编译器会给出类型不匹配的警告。
现在我们来讨论,怎么输出一维数组里面每个值的问题。这里我们已经明确的是p的值就是这个数组的地址,在数值上是等于&a[0]的。有人会给出这样的问题,a[0]可以表示成(p+0),那么对应的a[1]是否可以表示成 (p+1),答案是否定的。因为p是指向整个数组,所以p+1意为指向下一个数组,这里, 有六个元素,+1就会增加6 *sizeof(int)这么多的字节。所以这种方法是不对的。
这个我们应该怎么做呢,正确的做法是如果 * (* (p+0)+i).让我们来一层一层的理解这个式子的意思 * (p+0)表示的是a[0],即表示,第0行第0列的地址,所以+i就表示第i个元素的地址,最后,在用 *括起来,即表示这个值。
又因为,a[0]是等价于* (p+0),所以,同样也可以表示为 *(p[0]+1).
接下来,我们来讨论一下二维数组的情况。当我们理解了一维数组之后,二维数组理解起来会容易的多。这里也给出一个示例程序。
    int (*m)[3];
    int b[][3]={{1,27,3},{4,5,6}};
    m=&b[0]; //或者m=b;
    printf("%d\n",*(m[0]+1));
    printf("%d",*(*(m+0)+1));  
这里,值得注意的一点是,为什么这个又要用m=b或者是m=&b[0],答案很明确,这里b[0],b[1]不再像一维数组那样代表某个元素,而是代表b数组第0行、第1行的首地址。所以,为了表示m指向整个数组,所以这里这样写。同样,我们知道b表示b[0]的地址,所以这里可以用m=b.接下来,输出某个具体元素的思想与一维数组相同,故不再赘述。
输出结果:

这里写图片描述

关于多级指针和指向函数的指针,这里不做讲解,有兴趣的读者可以查阅其他资料自己做了解。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、MATLAB、python、web、C#、EDA、proteus、RTOS等项目的源码。 【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。 【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。【项目资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值