• 『C语言进阶』指针进阶(一)


    在这里插入图片描述
    🔥博客主页 小羊失眠啦
    🔖系列专栏 C语言
    🌥️每日语录无论你怎么选,都难免会有遗憾。
    ❤️感谢大家点赞👍收藏⭐评论✍️


    在这里插入图片描述

    前言

    C语言初阶中,我们对指针有了一定的了解,指针是个变量,是用来存放地址的,指针的大小是固定的4/8个字节,指针是有类型的,指针的类型决定了指针的±整数的步长以及指针的运算,小羊最近已经开学,博客可以正常更新了,好啦,接下来让我们再进一步的了解指针!!


    一、字符指针

    我们可以定义一个字符指针,指向一个字符变量,并通过指针来修改这个字符变量

    例1

    #include
    int main()
    {
    	char ch = 'x';
    	char* p = &ch;//pc是字符指针
    	ch = 'a';
    	*p = 'a';
    	return 0;
    }
    //ch是字符变量,可以直接改变ch的值,
    //pc这时是字符指针,存放的是ch的地址,也可以通过指针来改变变量ch的值。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    例2

    #include
    int main()
    {
    	char arr[] = "abcdefg";//创建数组,用字符串来初始化
    	char* p = "abcdefg";//常量字符串,"abcdefg"存放在"常量区",只读,不允许被修改
    	//指针变量p存放的是字符串首元素的地址
    	*p='a';//erro 这种情况会报错,pa不能改变为w
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    例3:这段代码运行结果是什么?

    int main()
    {
    	char* s = "abcdefg";
    	for (int i = 0; i < 4; i++)
    	{
    		*(s + i) = '0';
    	}
    	printf("%s\n", s);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    结果:

    运行错误,原因是"abcdefg"是常量字符串,它们存放在"常量区",只读,不允许被修改,所以一般写成const char* s="abcdefg"

    笔试题

    int main()
    {
    	char str1[] = "hello bit.";
    	char str2[] = "hello bit.";
    	const char* str3 = "hello bit.";
    	const char* str4 = "hello bit.";
    	if (str1 == str2)
    		printf("str1 and str2 are same\n");
    	else
    		printf("str1 and str2 are not same\n");
    	if (str3 == str4)
    		printf("str3 and str4 are same\n");
    	else
    		printf("str3 and str4 are not same\n");
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    结果分析:

    str1和str2是两个字符数组,两个数组是相互独立的,在创建时,会各自向空间申请空间,所以地址是不一样的,只是内存中两块不同内存区域存放着相同内容而已,故str1!=str2
    str3和str4里面存放的地址是同一个,都是h的地址,所以是一样的


    二、指针数组

    2.1 认识指针数组

    C语言初阶里的初识指针中,铁汁们对指针数组也有一定的了解,指针数组是一个存放指针的数组。

    整形数组--存放整形的数组
    字符数组--存放字符的数组
    指针数组--存放指针的数组

    #include
    int main()
    {
    	int arr1[3];  //存放了三个整形的数组 int int int
    	int* arr2[3]; //存放了三个整形指针的数组 int* int* int*
    	char* arr3[3];//存放了三个一级字符指针的数组 char* char* char*
    	char** arr4[3];//存放了三个二级字符指针的数组 char** char** char**
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    牛刀小试

    int main()
    {
    	char* arr[] = { "abcd","efgh","igkl" };
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	for (int i = 0; i < sz; i++)
    		printf("%s ", arr[i]);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    arr是首元素地址

    2.2 使用指针数组模拟二维数组

    #include
    int main()
    {
    	int arr1[4] = { 1,2,3,4 };
    	int arr2[4] = { 2,3,4,5 };
    	int arr3[4] = { 3,4,5,6 };
    	int arr4[4] = { 4,5,6,7 };
    	int* arr[4] = { arr1,arr2,arr3,arr4 };
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	for (int i = 0; i < sz; i++)
    	{
    		for (int j = 0; j < 4; j++)
    		{
    			printf("%d ", *(arr[i] + j));
    		}
    		printf("\n");
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述
    结果分析:

    经过调试内存可得:
    	int arr1[4] = { 1,2,3,4 };              //arr1的地址是:0x0000001C526FF7F8
    	int arr2[4] = { 2,3,4,5 };              //arr2的地址是:0x0000001C526FF828
    	int arr3[4] = { 3,4,5,6 };              //arr3的地址是:0x0000001C526FF858
    	int arr4[4] = { 4,5,6,7 };              //arr4的地址是:0x0000001C526FF888
    由此可见,arr1,arr2,arr3,arr4数组内存中有独立的内存空间,并不是连续的四个内存空间,
    我们将这四个一维数组的首元素的地址放在指针数组arr中,通过指针数组来访问这四个一维数组,效果和二维数组是一样的,但是并不是真正的二维数组!!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    三、数组指针

    3.1 数组指针的定义

    定义:指向数组指针被称为数组指针
    由于指针数组和数组指针对于初学者很容易混肴,所以我们先通过类比的方法来认识数组指针。

    字符指针–指向字符变量的指针,即存放字符变量地址的指针变量。
    整型指针–指向整型变量的指针。即存放整型变量地址的指针变量
    数组指针–指向数组变量的指针。即存放数组变量地址的指针变量

    #include
    int main()
    {
    	int a = 2;
    	int* p = &a; 
    	int arr[10]; 
    	int* arr[10];  //arr先和[10]结合,所以arr是一个数组,里面存的都是指针变量,即为指针数组
    	int(*arr)[10];//arr先和*结合所以arr是一个指针变量,又指向数组,即为指针数组
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    解释:arr先和*结合,说明arr是一个指针变量,然后指向一个大小为10个整型的数组(指向的类型 int [10])。所以arr是一个指针,指向一个数组,叫数组指针。

    注意:[ ]的优先级要高于*号的,所以必须加上()来保证arr先和*结合。

    3.2 &数组名VS数组名

    • 通常情况下,数组名是首元素地址
    • sizeof(数组名):计算的是整个数组的大小,sizeof内部单独放一个数组名,数组名表示整个数组
    • &数组名:取出的是数组的地址,&数组名,数组名表示整个数组
    #include
    int main()
    {
    	int arr[5] = { 0,1,2,3,4 };
    	printf("  arr=%p\n", arr);
    	printf("arr+1=%p\n", arr + 1);
    	printf("\n");
    	printf("  &arr[0]=%p\n", &arr[0]);
    	printf("&arr[0]+1=%p\n", &arr[0] + 1);
    	printf("\n");
    	printf("  &arr=%p\n", &arr);
    	printf("&arr+1=%p\n", &arr + 1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    3.3 数组指针的使用

    数组指针指向的是数组,那数组指针中存放的数组的地址

    #include
    int main()
    {
    	int arr[5];
    	int(*p)[5]=&arr;//p的类型是int(*)[5],存放的是存放int类型的数组
    	int* pp[5];//指针数组,pp是数组,存放的是int*类型
    	int* (*ppp) = &pp;//ppp的类型是int*(*)[5],存放的是存放int*类型的数组
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.3.1 打印数组元素

    #include
    int main()
    {
    	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }, i = 0;
    	int* p = arr;
    	int(*pp)[10] = &arr;
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	for (i = 0; i < sz; i++)
    	{
    		printf("%d ", *(p + i));
    	}
    	printf("\n");
    	for (i = 0; i < sz; i++)
    	{
    		printf("%d ", *((*pp) + i));
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.3.2 打印二维数组元素

    #include
    
    void Print1(int arr[3][5], int row, int col)
    {
    	for (int i = 0; i < row; i++)
    	{
    		for (int j = 0; j < col; j++)
    		{
    			printf("%d ", arr[i][j]);
    		}
    		printf("\n");
    	}
    }
    
    void Print2(int(*p)[5], int row, int col)
    {
    	for (int i = 0; i < row; i++)
    	{
    		for (int j = 0; j < col; j++)
    		{
    			printf("%d ", *(p[i] + j));
    		}
    		printf("\n");
    	}
    }
    int main()
    {
    	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
    	Print1(arr, 3, 5);
    	printf("\n");
    	Print2(arr, 3, 5);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    3.4 总结:

    在这里插入图片描述
    数组名arr,表示首元素的地址,二维数组的首元素是二维数组的第一行所以这时传递的arr,其实相当于第一行的地址,是一维数组的地址可以数组指针来接收。

    补充:

    一维数组传参,形参的部分可以是数组,也可以是指针
    二维数组传参,形参的部分可以是数组,也可以是指针

    注意:形参写成数组形式是为了让人更容易理解,本质上是指针


    四、数组传参和指针传参

    4.1 一维数组传参

    数组传参的时候,形参可以写成同样数组形式,若是用数组作形参,[ ]里面的值可以省略,也可以随意赋值。传过来的arr是数组首元素地址,所以也可以用一级整形指针来接受。

    #include
    void test1(int arr[]);//正确
    void test2(int arr[10]);//正确
    void test3(int* arr);//正确
    int main()
    {
    	int arr1[10] = { 0 };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    实战演练

    #include
    
    void test1(int arr[],int sz)
    {
    	for (int i = 0; i < sz; i++)
    	{
    		printf("%d ", arr[i]);
    	}
    }
    
    void test2(int arr[10],int sz)
    {
    	for (int i = 0; i < sz; i++)
    	{
    		printf("%d ", arr[i]);
    	}
    }
    
    void test3(int* arr, int sz)
    {
    	for (int i = 0; i < sz; i++)
    	{
    		printf("%d ", *(arr+i));
    	}
    }
    
    int main()
    {
    	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    	int sz = sizeof(arr) / sizeof(arr[0]);
    	test1(arr, sz);
    	printf("\n");
    	test2(arr, sz);
    	printf("\n");
    	test3(arr, sz);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    4.2 二维数组传参

    void test(int arr[3][5])//ok?
    {}
    OK
    void test(int arr[][])//ok?
    {}
    NO  二维数组传参,函数形参的设计只能省略行数,不能省略列数
    二位数组传参,传的是二维数组第一行的地址,而不是第一行第一个元素的地址
    void test(int arr[][5])//ok?
    {}
    OK
    void test(int* arr)//ok?
    {}
    NO
    void test(int* arr[5])//ok?
    {}
    NO  这是一个存放int* 类型的数组
    void test(int(*arr)[5])//ok?
    {}
    OK  这是一个存放数组的一级指针
    void test(int** arr)//ok?
    {}
    NO
    int main()
    {
        int arr[3][5] = { 0 };
        test(arr);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    4.3 一维指针传参

    void test(int* p)
    {}
    
    int main()
    {
    	int n = 10;
    	test(&n);
    
    	int* p = &n;
    	test(p);
    
    	int arr[5] = { 0 };
    	test(arr);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4.4 二维指针传参

    void test(int** p)
    {}
     
    int main()
    {
    	int n = 10;
        int* p=&n;
    	test(&p);
     
    	int** pp = &p;
    	test(pp);
     
    	int* arr[5] = { 0 };
    	test(arr);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位铁汁们的支持。文章有问题可以在评论区留言,小羊一定认真认真修改,以后写出更好的文章。
    在这里插入图片描述

  • 相关阅读:
    C#基础总结三
    计算机毕业设计——基于html汽车商城网站页面设计与实现论文源码ppt(35页) HTML+CSS+JavaScript
    [人工智能-综述-13]:第九届全球软件大会(南京)有感 -2-新型的云服务:AI即服务,传统的云服务:IaaS,PaaS,SaaS, DaaS
    微服务系列文章之 Nginx负载均衡
    java - 类和对象
    WinForm的前世今生
    Redis系列8:Bitmap实现亿万级数据计算
    《MySQL 数据库(二)》
    《006.Springboot+vue之旅游信息推荐系统》【有文档】
    RabbitMQ-简述
  • 原文地址:https://blog.csdn.net/hsjsiwkwm/article/details/132670804