字符指针?指针数组?数组指针?《C语言指针进阶第一重奏》

目录

一.字符指针

1.1字符指针的认识 

1.2字符指针存放字符串

1.3字符指针的使用

二.指针数组

2.1指针数组的认识

三.数组指针

3.1数组指针的认识

3.2数组名和&数组名的区别

3.3数组指针的使用

 3.4数组参数,指针参数

3.5一维数组传参

3.6二维数组传参

3.7一级指针传参

3.8二级指针传参



一.字符指针

1.1字符指针的认识 

字符指针的认识:其实很简单,跟其他类型类比一下就知道了,请看下面的代码。

#define _CRT_SECURE_NO_WARNINGS 1


#include<stdio.h>
#include<stdlib.h>

int main()
{
	int a = 10;        //定义一个整型变量
	printf("%d\n", a);
	int* p = &a;       //将变量a的地址存放的整型指针变量p中
	*p = 15;           //对p解引用改变a地址的内容,即把15换成10
	printf("%d\n", a);
	printf("-------------\n");
	char c = 'A';      //定义一个字符变量
	printf("%c\n", c); //将变量c的地址存放的字符指针变量pc中
	char* pc = &c;     对pc解引用改变a地址的内容,即把'A'换成'C'
	*pc = 'C';
	printf("%c\n", c);

	return 0;
}

 跟整型指针类型用法一样,都是指针用来存放变量的地址,解引用改变地址的内容,不过是把类型不同,整型指针存放的是整型类型变量的地址,字符指针存放的是字符类型变量的地址。

1.2字符指针存放字符串

字符指针我们知道它可以存放一个字符变量的地址,那如果是存放的字符串会怎么样呢?

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>

int main()
{
	char arr[] = "hello classmate!";
	printf("%s\n", arr);
	printf("%c\n",*arr);
	printf("----------------\n");
	const char* pstr = "hello classmate!";
	printf("%s\n", pstr);
	printf("%c\n", *pstr);
	return 0;
}

分析以上代码,你也会惊奇的发现,字符指针存放字符串变量的方式和数组的数组名表示首元素地址也很类似,根据上面代码我们具体分为上下两部分。

第一部分:定义了一个arr数组名的字符数组,数组名就是数组首元素地址,所以数组名arr就是字符‘h’的地址,而且我们字符串打印时一般都只需要输入数组名就可以打印字符串了,而这也说明了,字符串的打印只要给首元素的地址就可以进行字符串打印了。arr既然是首元素地址,对arr进行解引用就是首元素。

第二部分:跟第一部分极其类似,我们定义一个pstr的字符指针,用来存放字符串的地址,结果打印发现,pstr 存放的不是整个 "hello classmate" 的字符串的地址存放的就是首字符 'h'的地址

打印字符串和pstr解引用与上面的数组也是同一道理,就不赘述了。至于为什么要加const,是因为pstr存放的是常量字符串,既然是常量,所以就加const防止被修改。

1.3字符指针的使用

你先看一下代码,自己小小研究一番。

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

看完了?好的。首先,str1和str2我们都知道是数组首元素地址,然后你发现都是首字符‘ h ’的地址,可能会觉得相同str1 == str2,不要被这样的错觉蒙蔽你明亮的双眼。str1 和 str2 是两个数组,在内存开辟的是两块不同的空间,就算内容是一样的,地址也不可能一样,两者的首元素地址自然也不一样,所以str1 不等于str2。

再看字符指针str3,str4,存放都是字符' h '的地址,但因为str3和str4是指针。当指向同一个字符串的时候,他们实际会指向同一块内存,实际就是同一块地址,简单点讲就是,str3 和str4用的是同一块地址。

二.指针数组

2.1指针数组的认识

什么是指针数组呢,是数组呢还是指针呢,以后咱们就这样判断,谁放在后面就是谁指针数组,数组放在后面,所以指针数组是数组,是用来存放指针的数组。

#include<stdio.h>

int main()
{
	int arr1[10]; //arr1存放了10个元素,类型是整型
	char arr2[10];//arr2存放了10个元素,类型是字符型
	int* arr3[10];//arr3存放了10个元素,类型是整型指针
	char* arr4[10];//arr4存放了10个元素,类型是字符指针
	return 0;

}

 因为指针数组的内容很浅,所以这一点内容应该可以理解了,我们重点放在数组指针上。

三.数组指针

3.1数组指针的认识

数组是指针根据谁放在后面就是谁原则,所以数组指针是指针。那是用来干嘛的呢?我们知道

整形指针: int * pint ; 能够指向整形数据的指针。
浮点型指针: float * pf ; 能够指向浮点型数据的指针。
那数组指针应该是:int (*p)[10]能够指向数组的指针。
在此之前我们先判断一下以下代码:
int main()
{
  int *p1[10];
  int (*p2)[10];
   //p1, p2分别是什么?
  return 0;
}

我们要记得 [ ] 的优先级要高于 号的,所以p1是先和 [10]结合,类型是int*,意思就是p1是指针数组。

p2是先加上()来保证p2先和*结合。p先和*结合,说明p2是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p2是一个 指针,指向一个数组,叫数组指针。

3.2数组名和&数组名的区别

因为了解数组指针之前,能区分数组名和&数组名还是很有必要的。

对于下面的数组:
int arr[10];
arr &arr 分别是啥? 我们知道arr 是数组名,数组名表示数组首元素的地址。 那&arr 数组名到底是啥?
#include <stdio.h>
int main()
{
    int arr[10] = {0};
    printf("%p\n", arr);
    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;
}

运行结果如下:

首先,我们要对数组名理解:
数组名是数组首元素的地址
有2个例外:
1. sizeof(数组名),这里的数组名不是数组首元素的地址,数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节。
2. &数组名,这里的数组名表示整个数组, &数组名取出的是整个数组的地址。
除此之外,所有的地方的数组名都是数组首元素的地址。

所以根据上面的代码我们发现,其实 &arr arr ,虽然值是一样的,但是意义应该不一样,实际上&arr 表示的是整个 数组的地址 ,而不是数组首元素的地址。本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型,数组的地址+1 ,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是 40个字节.

3.3数组指针的使用

#include <stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
    return 0;
}

一般呢,数组指针更多是应用在二维数组传参。

#include <stdio.h>
	void print_arr1(int arr[3][5], int row, int col)
	{
		int i = 0;
		int j = 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;
		int j = 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);
        printf("-----------------\n");
		//数组名arr,表示首元素的地址
		//但是二维数组的首元素是二维数组的第一行
		//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
		//可以数组指针来接收
		print_arr2(arr, 3, 5);
		return 0;
	}

运行代码如下:

二维数组在传参时,函数既可以接收二维数组,也可以接收数组指针,因为二维数组的数组名时首元素地址,首元素不是一个变量,而是一个一维数组,所以可以用数组指针去接收。

如代码:

#include <stdio.h>

int main()
{
        int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
        Print(arr, 3, 5);
        
        return 0;
    
}

 3.4数组参数,指针参数

3.5一维数组传参

定义了一个数组和函数,在调用函数时放入数组名,函数接收时可以通过数组和指针的方式接收。因为数组在内存中时连续存放的,传入数组名,就可以知道首元素地址,再用指针接收首元素地址,就可以知道整个数组了,用数组接收数组名本质上也是指针接收

用一个简单的例子:

    void test(int arr[])//ok?
	{}
	void test(int arr[10])//ok?
	{}
	void test(int* arr)//ok?
	{}
	void test2(int* arr[20])//ok?
	{}
	void test2(int** arr)//ok?
	{}
	int main()
	{
		int arr[10] = { 0 };
		int *arr2[10] = { 0 };
		test(arr);
		test2(arr2);
	}

按顺序解答:

main函数调用test函数传递了arr数组名。

    void test(int arr[])//ok?  arr是数组,可以用数组接收。ok
    {} 

  
    void test(int arr[10])//ok? 同理,arr是数组,可以用数组接收。ok
    {}


    void test(int* arr)//ok?  首元素地址可以用指针接收。ok

    {}

main函数调用test2函数传递了arr2数组名。

     void test2(int* arr[20])//ok? arr2是指针数组,可以用指针数组接收。ok的
    {}


    void test2(int** arr)//ok? arr2首元素是一级指针,必须用二级指针接收。ok的
    {}

3.6二维数组传参

二维数组传参时,实际上传递的时首元素的地址,首元素素是一个一维数组。用数组指针去接收一维数组,也可以用二维数组接收(要记得本质上也是用指针接收)

void test(int arr[3][5])//ok?
{}
void test(int arr[][])//ok?
{}
void test(int arr[][5])//ok?
{}
void test(int *arr)//ok?
{}
void test(int* arr[5])//ok?
{}
void test(int (*arr)[5])//ok?
{}
void test(int **arr)//ok?
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);
}
void test ( int arr [ 3 ][ 5 ]) // 传过去的一维数组地址,接收也用可以二维数组接收。ok的
{}  
void test ( int arr [][]) // 二维数组可以省略行,但是不能省略列。不ok
{}
void test ( int arr [][ 5 ]) // 跟上面同理,ok的
{}
void test ( int * arr ) // 传过来的是一维数组的地址,所以要用数组指针接收,不ok
{}
void test ( int* arr [ 5 ]) //别被迷惑了兄弟们,这是指针数组,不是数组指针,不ok
{}
void test ( int ( * arr )[ 5 ]) //确认是数组指针,ok的
{}
void test ( int ** arr ) //二级指针能接收的是一级指针,不能直接接收地址/不ok
{}
3.7一级指针传参

当函数的参数是一级指针时,调用时能传入什么样的参数?

1.单一变量地址,一级指针接收的是地址没什么问题。

void my_printf(char* str)
{
	printf("%c\n", *str);
}


int main()
{
	char pc = 'K';
	my_printf(&pc);

	return 0;
}

2.数组名。因为数组名就是首元素地址;


void my_printf(char* str)
{
    printf("%s\n", str);
}
int main()
{
    char arr[] = "CSDN";
    my_printf(arr);
    return 0;
}

3.一级指针。用一个变量去接收数组名的地址,此时变量为一级指针,所以一级指针传参,一级指针接收。


void my_printf(char* str)
{
    printf("%c\n", *str);
}
int main()
{
    char cp = 'J';
    char* cpp = &cp;
    my_printf(cpp);
    return 0;
}

3.8二级指针传参

1.一级指针的地址。

void my_printf(char** str)
{
	printf("%c\n", **str);
}


int main()
{
	char pc = 'A';
	char* pcc = &pc;
	my_printf(&pcc);

	return 0;
}

2.二级指针。

void my_printf(char** str)
{
	printf("%c\n", **str);
}


int main()
{
	char pc = 'B';
	char* pcc = &pc;
	char** pccc = &pcc;
	my_printf(pccc);

	return 0;
}

3.指针数组。

void my_printf(char** str)
{
	printf("%s\n", *str);
}


int main()
{
	
	char* arr[] = {"abcdef"};
	
	my_printf(arr);

	return 0;
}

好了,今天的内容就到这里了,如果能够帮助到看官,就希望看官给个三连支持一下吧!!!、

  • 37
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 29
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值