L1D6 C语言指针(一)

C语言指针(一)

一、指针基本用法

1.1 指针用途

① 使程序简洁、紧凑、高效
② 有效地表达复杂的数据结构
③ 动态分配内存
④ 得到多余一个的函数返回值

1.2 地址和变量

在计算机内存中,每一个字节单元都有一个编号,称为地址。指针指向地址,所以指针可以理解为就是地址。(内存以字节为单位 int *p 假如p指向0x00000000 p+1指向0x00000004)

1.3 指针变量的定义

在c语言中,内存单元的地址称为指针。

<存储类型> <数据类型>  *<指针变量名>
				char  *p;

指针说明指定的数据类型不是指针的数据类型,而是指针目标(*p)的数据类型,比如上面(*p)的类型为字符型char。

1.4 指针变量的赋值

指针变量在使用前不仅要定义说明,还要赋予具体的值(地址),不然会出现段错误等情况。未经赋值的指针变量不能随意使用。
如下演示未赋值的情况:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int *q;
    scanf("%c",q);        //未赋值  没有初始地址,这里执行会出现段错误
    printf("%c",*q);
    return 0;
}

在这里插入图片描述

如何规避这个问题?
1、对指针进行初始化 2、使用指针前进行非空判断 3、指针使用完成后将指针置空避免出现野指针

C语言提供&地址符来表示变量的地址,我们可以int a = 0; int *p = &a;这样给p一个初始地址。当然也可以这样写,int a = 0; int *p; p = &a;因为只有在定义时需要加*,后面*p全是代表取指针(地址)中存放的内容。
我们对上面的示例进行优化:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    char i = 0;
    char *q = &i;        /*规避野指针要点1、指针使用前初始化*/
    if (NULL == q)      /*规避野指针要点2、指针使用前非空判断*/
    {
        printf("q is NULL\n");
        return -1;
    }
    
    scanf("%c",q);
    printf("%c\n",*q);

    q = NULL;           /*规避野指针要点3、指针使用完成赋空*/
    return 0;
}

在这里插入图片描述

1.5 指针变量的引用

指针指向的内存区域中的数据称为指针的目标。&是取地址运算符 *是指针运算符,两个运算符互为互逆操作,例如"&a"是取变量a的地址,*b就是取指针变量b所指向内存单元的内容。
如下程序演示:

#include <stdio.h>

int main(int argc, char** argv)
{
	int m = 10;
    int *p = &m;

    printf("&m:%p,p:%p\n",&m,p);                    //p = &m
    printf("m:%d,*p:%d,*(&m):%d\n",m,*p,*(&m));     // m = *p = *(&m)
	return 0;
}

在这里插入图片描述
注意:指针本身也需要地址存放的,&p也会占用一个地址。

二、指针的运算

指针运算是以指针变量所存放的地址量作为运算量而进行的运算; 指针运算的实质就是地址的计算; 指针运算的种类是有限的,它只能进行赋值运算、算术运算和关系运算;

2.1指针算术运算

在这里插入图片描述
在这里插入图片描述

px+n演示:

#include <stdio.h>

int main(int argc, char** argv)
{
	int m = 10;
    double n = 20.0;
    int *p = &m;
    double *q = &n;

    printf("m = %d , &m= %p , p = %p , p+2= %p\n",m,&m,p,p+2);
    printf("n = %f , &n= %p , q = %p , q+2= %p\n",n,&n,q,q+2);
    return 0;
}

在这里插入图片描述
如上所示,p为int型指针,p+2就是p移动42字节 q为float型指针,q+2就是移动82字节。

p-q是两指针只想地址位置直接相隔数据的个数,而非字节个数,如下所示:

#include <stdio.h>

int main(int argc, char** argv)
{
	int array[5] = {1 , 2, 3, 4, 5};
    int *p = &array[0];
    int *q = &array[4];
    printf("q-p = %ld\n",q-p);
}

在这里插入图片描述

2.1指针关系运算

两指针直接的关系运算,表示它们指向地址之间的关系运算,见下表。
在这里插入图片描述
注意以下两点:
①具有不同数据类型的指针之间的关系运算没有意义,指向不同数据区域的数据的两指针之间,关系运算也没有意义。
②指针与一般整数变量之间的关系运算没有意义。但可以和零进行等于或不等于的关系运算,判断指针是否为空。

关系运算演示如下,实现字符串的逆序输出:

#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char s[] = "welcome";
    char *p = NULL , *q = NULL , t = 0;

    printf("%s\n", s);

    p = s;
    q = s + strlen(s) - 1;
    while(p < q)
    {
        t = *p;
        *p = *q;
        *q = t;
        p++;
        q--;
    }
    printf("%s\n", s);
    return 0;
}

在这里插入图片描述

三、指针与数组

3.1 指针与一维数组

1、指针与数组
① 在C语言中,数组的指针是指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的起始地址
② 一维数组的数组名为一维数组的指针(起始地址),如a[8]的起始地址就是a
③ 设指针变量px的地址值等于数组指针x(即指针变量px指向数组的首元数),则:x[i] 、*(px+i)、*(x+i) 和px[i]具有完全相同的功能:访问数组第i+1个数组元素
在这里插入图片描述

指针与数组的异同:①指针变量和数组在访问数组中元素时,一定条件下其使用方法具有相同的形式,因为指针变量和数组名都是地址量。②但指针变量和数组的指针(或叫数组名)在本质上不同,指针变量是地址变量,而数组的指针是地址常量,也就是没有数组名+1这种写法,但是可以指针+1,如下演示情况。

#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    int array[10] = {0};
    array = array + 1;
    return 0;
}

在这里插入图片描述
指针与一维数组关系综合演示:

#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    int a[] = {9 , 1 , 34 , 7 , 3 , 10} , i = 0 , n = 0;
    int *p = a;

    n = sizeof(a)/sizeof(int);
    for ( i = 0; i < n; i++)
    {
        printf("%d %d %d %d\n" , *(a+i),a[i],*(p+i),p[i]);
    }
    
    return 0;
}

在这里插入图片描述
指针与数组常见等价操作:

在这里插入图片描述

3.2 指针与多维数组

3.2.1 相关概念

① 多维数组就是具有两个或两个以上下标的数组
② 在C语言中,二维数组的元素连续存储,按行优先存,基于这个特点,可以用一级指针访问二维数组

3.2.2 一级指针遍历二维数组

编程实现使用一级指针遍历二维数组:

#include <stdio.h>
int main(int argc, char *argv[])
{
	int a[2][3] = {{1,2,3},{4,5,6}};
	int *p = a[0];    //a[0]是第一行首地址  等价于&a[0][0]
	int i=0,j = 0;
	for(i = 0;i<2;i++)
	{
		for(j=0;j<3;j++)
		{
			printf("%d  ",*p++);
		}
		putchar('\n');
	}
	return 0;
}

在这里插入图片描述

3.2.3 行指针(数组指针)遍历二维数组

存储行地址的指针变量,叫做行指针变量。形式如下: <存储类型> <数据类型> (*<指针变量名>)[表达式]

例如,int a[2][3]; int (*p)[3]; 方括号中的常量表达式表示指针加1,移动几个数据。 当用行指针操作二维数组时,表达式一般写成1行的元素个数,即列数。二维数组名常被称为行地址。
指针和二维数组关系:
在这里插入图片描述
示例:

#include <stdio.h>
int main(int argc, char *argv[])
{
	int a[2][4] = {{1,2,3,4},{5,6,7,8}};
	int (*p)[4];
	p = a;
	printf("%p %p \n",p,p+1);        //p+1 行指针移动一行  一行4*4=16bytes 地址间隔16bytes
	printf("%p %p \n",*p,*p+1);       //*p = p+0  *p+1 = *(p+0)+1 = &a[0][1]  所以间隔1个数据 地址间隔4字节
	printf("%d\n",*(*(p+1)+1));			//p[1][1] = a[1][1] = 6
	printf("%d\n",*(*(a+1)+1));
	return 0;
}

在这里插入图片描述

四、综合练习

1、输入char型“123”,转为整数123输出
答:

#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char *s = "123";
    int num = 0;

    while ('\0' != *s)
    {
        num = num*10 + *s - '0';
        s++;
    }
    printf("num = %d\n", num);
    return 0;
}

2、编写一个函数,将整型数组中的n个数按反序存放
答:

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int a[] = {1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10};
    int n  = sizeof(a)/sizeof(int);
    int *p = a;
    int *q = a + n - 1;
    int temp = 0;
    for (int i = 0; i < n/2; i++)
    {
        temp = *q;
        *q = *p;
        *p = temp;
        p++;
        q--;
    }
    for (int j = 0; j < n ; j++)
    {
        printf("a[%d] = %d ", j , a[j]);
    }
    printf("\n");
    return 0;
}

在这里插入图片描述
3、编程实现,使用行指针遍历二维数组?
答:

#include <stdio.h>
int main(int argc, char *argv[])
{
	int a[2][4] = {{1,2,3,4},{5,6,7,8}};
	int (*p)[4];
	p = a;
	printf("%p %p \n",p,p+1);
	printf("%p %p \n",*p,*p+1);
	for(int i = 0;i<2;i++)
	{
		for(int j = 0; j<4;j++)
			printf("%d ",*(*(p+i)+j));
		putchar('\n');
	}
	return 0;
}

ps:欢迎大佬指正,共同学习进步。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值