C++的数组和指针

目录

1、指针的5个要素

2、数组名的意义

3、指针和数组的存储的区别

4、指针作为返回值

5、二维数组

6、二重指针(指针的指针)

思考题和解析​​​​​​​


1、指针的5个要素

int num = 5;

int *ptr = #

变量的值num(5)

变量的地址&num(例如0x00006f23)

指针的值ptr(所指向的内存的地址,0x00006f23)

指针的地址&ptr(指针自己的存储地址)

指针指向的值*ptr(变量的值,即5)

通过指针修改所指向的变量的值:

*ptr = 100;

修改后:

变量的值num(100)

变量的地址&num(仍然是0x00006f23)

指针的值ptr(所指向的内存的地址,0x00006f23)

指针的地址&ptr(指针自己的存储地址)

指针指向的值*ptr(变量的值,即100)

2、数组名的意义

int a[10];

a是数组名,a的值是数组的首地址,例如a的值是0x00000001,那么a+1表示首地址加int大小的地址,即0x00000005,对a进行解引用才是数组首个元素的值,*a=a[0],*(a+1)=a[1].

&a是数组的指针,加一意味着数组首地址加上整个数组的长度(10个int型变量的长度)后的地址,即末尾元素的后一个元素的地址。

声明一个数组的指针:

int (*p)[10] = &a;

数组的指针跟二维数组的关联,下面会说到。 

3、指针和数组的存储的区别

看下面一段代码,思考问题。

1.四个指针的存储的空间在哪里?它们的值(所指向内存的地址)相等吗?

2.初始化的对象是什么?

3.字符串Hello World是常量吗? 

int main()
{
	char p1[] = "Hello World!";
	char p2[] = "Hello World!";
	
	//在栈上创建两个数组,分别存放字符串,首地址不一样
	if(p1 == p2) {
		
	} else {
		
	}

	char *p3 = "Hello World!";
	char *p4 = "Hello World!";

	//在栈上创建两个指针,分别指向存储在静态数据区的字符串,地址一样	
	if(p3 == p4) {
		
	} else {
		
	}
}

【1.存储的空间在哪】

第一种存储在栈上,第二种存储在静态数据区,p1和p2的值不相等,p3和p4的值相等。

【2初始化的对象是什么】

第一种初始化数组对象,第二种初始化指针对象。

【3.Hello World字符串是常量吗】

第一种不是常量,而是存储在数组中的局部变量,随着函数的返回就会被销毁,第二种是字符串常量,存储在静态数据区。事实上,第二种初始化的正确写法是 const char *p = "hello",将常量字符串的地址赋给指向char常量的指针。

《c和指针》上有一段:指针和数组并不是相等的,当声明一个数组时,它同时也分配了一段内存空间,用于存储数组元素,但当声明一个指针时,它只分配了用于容纳指针的空间(32位中4个字节的大小)。

p1、p2、p3和p4都具有指针值,都可以进行间接访问(解引用)和下标引用操作。但是它们还是存在区别和联系:

1. char p[] = “Hello World” 的意义是在栈上创建数组,数组直接存放Hello World字符串,数组名p代表这段内存的首地址,是一个常量,p++操作是错误的!而char * p = "Hello World"的意义是在栈上创建指针,指向存储在静态数据区的字符串,指针p是一个指针变量,p++操作是正确的!

2. 当一个数组名作为函数参数时,数组名等同于指向数组第一个元素的指针,所以此时传递给函数的是指针的拷贝,指针作为形参和数组名作为形参的效果是一样的。

4、指针作为返回值

考虑下面的例子:

char *ptr1() {
	char p[] = "hello";
	return p;
}

char *ptr2() {
	char *p = "hello";
	return p;
}

int main() {
	char *p1 = ptr1();
	cout << p1 << endl;  //输出乱码,而且每次输出都不一样
	char *p2 = ptr2();
	cout << p2 << endl;  //正确的输出字符串
}

p2可以正确的输出hello字符串,而每次运行时,p1都输出一个新的随机的乱码。

原因在于,ptr1()函数在栈上申请一段内存存放一个数组p,数组中存放字符串hello,p是该段内存的首地址,函数返回后,栈空间被回收,该字符串也不复存在,因此输出的内容是未知的。ptr2()函数在静态数据区申请一段内存,存储字符串hello,在栈上创建指针p,指向该段静态数据区的首地址,函数返回后,虽然指针本身被销毁了,但是静态数据区内的资源未销毁,而且通过函数的返回,该静态数据区的首地址被返回给p2,因此可以正确的输出字符串。

5、二维数组

二维数组名同样代表数组首元素的地址,int a[3][5],a的类型为int(*)[5]不是char**,二维数组名等同于数组的指针。

二维数组名的本质:数组的指针,即指向一个数组的指针

int a[3][5]

(a+i) 代表是i行的首地址。

*(a+i)就表示i行首元素的地址。

*(a+i) + j  等于 a[i][j]元素地址。

*( *(a+i) + j) 等于 a[i][j]元素的值。

一维数组名做函数参数:会退化为指针

int func(char buf[60]) //60无作用

int func(char buf[])和int func(char *buf)的意义相同

二维数组名做函数参数:第一维的参数可以省略

int func(char a[10][30]) //10无作用,30确定其步长

int func(char array[][30])//和上一个效果相同

int func(char (*array)[30])//和上一个效果相同,数组指针

void Test1(char a[10][30], int n)
{
	for (int i = 0; i < n; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
}

void Test2(char a[][30], int n)
{
	for (int i = 0; i < n; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
}

void Test3(char(*a)[30], int n)
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
}

int main()
{
	char array[5][30] = { "tttt", "eeee", "ssss", "tttt", "11111" };
	Test1(array, 4);
	Test2(array, 4);
	Test3(array, 4);
	system("pause");
	return 0;
}

运行结果

对于形参来说,数组和指针的意义是一样的,多维数组的本质实际上还是一维数组只不过元素也是数组。

6、二重指针(指针的指针)

 二重指针的用法

(1)二重指针指向指针的地址

(2)二重指针指向指针数组的首地址

int main()
{
	const char *str[] = { "abcde", "aqw", "ulk" }; //指针数组
	char **p = const_cast<char **>(str); //不可以直接定义 **p = { "abcde", "aqw", "ulk" }; 指针数组指针
		
	cout << "sizeof(str): " << sizeof(str) << endl;
	cout << "sizeof(p): " << sizeof(p) << endl;
	cout << "sizeof(str[0]): " << sizeof(str[0]) << endl;
	for (int i = 0; i < sizeof(str) / sizeof(str[0]); i++)
	{
		cout << p[i] << endl; //输出一行数据
	}

	system("pause");
	return 0;
}

运行结果:

思考题和解析

若有函数声明void f(char ** p),则使得函数调用f(var)不正确的var定义是?

A char var[10][10]

B char *var[10]

C void *var = NULL

D char *v = NULL, **var = &v

解析:

A. char var[10][10]; var的类型是 char (*)[10],传参后var的类型是char **

B. char *var[10]; var数组是存放char *类型的数组,数组名var是数组var元素的首地址,所以传参后var的类型是char **

C. void * 是定义没有指针类型的指针,void *可以指向任何类型的数据,但是不能转换成多重指针。

D. v是char*类型的,那么取v的地址肯定是char**类型的,所以var是char**类型的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值