【详解C语言指针】(关于字符指针,数组指针,指针数组等)

前言:关于指针的一些概念

1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。
3.指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。
4. 指针的运算。

提示:以下是本篇文章正文内容,下面案例可供参考

一、字符指针

在指针的类型中我们知道有一种指针类型为字符指针char*

1. 一般使用

#include<stdio.h>
int main()
{
	char ch = 'w';
	char* pc = &ch;
	*pc = 'a';
	printf("%c", ch);
	return 0;
}

上述代码使用,char *指针把这个字符变量的地址放到这个char *指针中,然后通过对 *pc解引用,找到字符ch ,然后改变其中的值为a,我们来看一下打印的结果。这就是我们的一般用法.
在这里插入图片描述

2. 另一种方法使用

我们来看一个简单的代码

#include<stdio.h>
int main()
{
	char* p = "abcdef";
}

这段代码的意思是什么呢?这里是把字符串"abcdef"放到这个char*p的指针变量中了吗?呢我们就需要去想它能放的下吗?现在是在32位环境下,p指针变量是4个字节而"abcdef"是6个字节,在加上/0是7个字节,所以答案是存不下。所以这个代码的意思绝对不是把"abcdef"放到指针变量p中。其实这个代码的意思是把这个常量字符串"abcdef"首字符a的地址存放到指针变量p中。 下面画图来说明(假设首字符地址是0x0012ff40)
在这里插入图片描述
如果你还是不敢相信,呢么我们可以使用 *p解引用访问这个字符串,其实访问的就是字符a 我们可以打印看看是不是.结果和我们想的一样
在这里插入图片描述
在有些初学者,不正确理解上述代码,就会出现下列问题。下面这个程序崩了,这是为什么呢?这是因为,这个字符串是一个常量字符串,常量字符串是不允许被修改的。
在这里插入图片描述
其实我们对这个代码,稍做优化可以防止出现这个错误。 这里就提示你编译错误。

#include<stdio.h>
int main()
{
	const char* p = "abcdef";
	*p = 'w';
}

在这里插入图片描述

3. 经典面试题

我们观察下列代码,会输出说明结果呢?

#include <stdio.h>
int main()
{
	char str1[] = "hello world.";
	char str2[] = "hello world.";
	const char* str3 = "hello world.";
	const char* str4 = "hello world.";
	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;
}

这里最终输出的是:
在这里插入图片描述
这是因为: 这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会 开辟出不同的内存块。所以str1和str2不同,str3和str4不同。

二、指针数组

指针数组是一个存放指针的数组

1. 一种使用

来看下面的一段代码 : 这里是3个变量,我们只需创建3个指针变量就可,但是如果变量10个 20个甚至更多呢?我们也需要创建对应的指针变量吗?呢我们就想到创建一个整型指针的数组,来存放变量的地址。

#include<stdio.h>
int main()
{
	//int* arr[10];//存放整型指针的数组
	//char* ch[5];//存放字符指针的数组

	int a = 10;
	int b = 20;
	int c = 30;

	int* p1 = &a;
	int* p2 = &b;
	int* p3 = &c;
	int* arr[3] = { &a, &b, &c };//arr就是一个指针数组
	int i = 0;
	for (i = 0; i < 3; i++)
	{
	 printf("%d ", *(arr[i]));//找到变量对应的地址,然后解引用就拿到了对应的元素
	}

	return 0;
}

2. 第二种使用

来看下面的一段代码 :

#include<stdio.h>
int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	
	int* parr[3] = { arr1, arr2, arr3 };//parr是一个指针数组
	int i = 0;
	for (i = 0; i < 3; i++)//找到每一行
	{
		int j = 0;
		for (j = 0; j < 5; j++)找到每一个元素
		{
			//printf("%d ", parr[i][j]);
			printf("%d ", *(parr[i]+j));依次加j,相当于找到每个元素地址,然后在解引用,找到并打印这个元素
		}
		printf("\n");
	}

	return 0;

我们画图来解释:

在这里插入图片描述
我们分别有三个数组arr1 arr2 arr3,数组名表示首元素地址。然后我们把这三个数组名也就是首元素地址,分别存放到parr数组中,每一个类型都是int*
parr[0]–>arr1、parr[1]–>arr2、parr[2]–>arr3 分别对应。这时我们发现好像模拟出一个二维数组,先使用parr找到每一行,然后又可以找到每一个元素,然后就得出下面的打印结果
在这里插入图片描述

三、&数组名vs数组名

我们知道arr是数组名,数组名表示首元素地址。呢么&arr是什么呢?
来看下面的一段代码 :

#include <stdio.h>
int main()
{
    int arr[10] = {0};
    printf("%p\n", arr);//%p打印地址
    printf("%p\n", &arr);
    return 0; }

运行结果如下:
在这里插入图片描述
可见数组名和&数组名打印的地址是一样的。难道这个两个意义相同吗?

我们在看下面的一段代码 :

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("arr = %p\n", arr);
	printf("&arr= %p\n", &arr);
	printf("arr+1 = %p\n", arr + 1);
	printf("&arr+1= %p\n", &arr + 1);
	return 0;
}

运行结果如下:
在这里插入图片描述
在这里arr类型是int* 当我们对其+1时由00B6FC70–>00B6FC74 整型指针+1跳过4个字节而&arr类型是int(*)[10]数组指针类型,而数组的地址+1,00B6FC74–>00B6FC98 跳过了40个字节,跳过了整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40.
通常情况下,我们说的数组名都是数组首元素的地址但是有2个例外:
1. sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小
2. &数组名,这里的数组名表示整个数组,&数组名,取出的是整个数组的地址

四、数组指针

既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址

一个数组指针的使用:

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col) {
    int i = 0;
    for(i=0; i<row; i++)
   {
        for(j=0; j<col; j++)
       {
            printf("%d ", arr[i][j]);
       }
        printf("\n");
   }
}
void print_arr2(int (*arr)[5], int row, int col) {
    int i = 0;
    for(i=0; i<row; i++)
   {
        for(j=0; j<col; j++)
       {
            printf("%d ", arr[i][j]);
       }
        printf("\n");
   }
}
int main()
{
    int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
   print_arr1(arr, 3, 5);
    //数组名arr,表示首元素的地址
    //但是二维数组的首元素是二维数组的第一行
    //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
    //可以数组指针来接收
    print_arr2(arr, 3, 5);
    return 0; }

学了指针数组和数组指针我们来一起回顾并看看下面代码的意思

1 int arr[5]; arr是一个整型数组,有5个元素,每个元素是int类型的。
2 int *parr1[10];parr1是一个数组,数组有10个元素,每个元素的类型是int *;所以parr1是指针数组。
3 int (*parr2)[10];parr2先和 * 结合,说明parr2是一个指针,该指针指向一个数组,数组是10个元素,每个元素是int类型的,parr2是数组指针。
4 int (*parr3[10])[5];parr3和[]结合,说明parr3是一个数组,数组是10个元素,数组的每个元素是什么类型呢?是一种数组指针,类型是int( * )[5],该类型的指针指向的数组有5个int类型的元素。
以上就是指针的一部分内容, 🌟Hello world 我们下期见!

  • 27
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@小辉灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值