赞
踩
目录
思考题和解析
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)
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;
数组的指针跟二维数组的关联,下面会说到。
看下面一段代码,思考问题。
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. 当一个数组名作为函数参数时,数组名等同于指向数组第一个元素的指针,所以此时传递给函数的是指针的拷贝,指针作为形参和数组名作为形参的效果是一样的。
考虑下面的例子:
- 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,因此可以正确的输出字符串。
二维数组名同样代表数组首元素的地址,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;
- }
运行结果
对于形参来说,数组和指针的意义是一样的,多维数组的本质实际上还是一维数组只不过元素也是数组。
二重指针的用法
(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**类型的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。