char a[5] = {'a','A','c','C','d'};
//cout<<a<<endl;
printf("a=%d\n",a);
//cout<<&a<<endl;
printf("&a=%d\n",a);
//cout<<*(a+1)<<endl;
printf("*(a+1)=%c\n",*(a+1));
char (*p2)[5] = &a;
char (*p3) = a;
//cout<<p2<<endl;
printf("p2=%d\n",p2);
printf("(p2+1)=%d\n",p2+1);
// cout<<p3<<endl;
printf("p3=%d\n",p3);
printf("p3+1=%d\n",p3+1);
// cout<<*(p2)<<endl;
printf("*(p2)=%c\n",*(p2));
//cout<<*p3<<endl;
printf("*p3=%c\n",*p3)
数组指针与指针数组
从上面运行结果来看
声明一个数组a[5],数组名a代表数组第一个元素的地址,等价于&a[0],是常量指针(类型就是声明的数组每个元素的类型,比如上面的char),不会改变,a+1,a+2,前进的步长就是char,相应就访问了a[1],a[2];
对a取地址&a是对该数组取地址,虽然也是首元素的地址,但意义不同
char (*p2)[5] = &a;
char (*p3) = a;
上面的是数组指针,本质是一个指针(4字节),相应的+1就会前进步长5字节,
也就是说,指针是有类型的,会影响+1的时候内存前进的步长(字节数)
char * p3[5]
这个是指针数组,存了5个char类型指针
因为[]优先级高,先结合声明一个数组,数组类型是指针
char (*p2)[5]
因为()优先级高,先结合声明了指针,[]声明了指针的步长
这里有个有意思的话题值得探讨一下:平时我们定义指针不都是在数据类型后面加上指针变量名么?这个指针p2 的定义怎么不是按照这个语法来定义的呢?也许我们应该这样来定义p2:
int (*)[10] p2;
int (*)[10]是指针类型,p2 是指针变量。这样看起来的确不错,不过就是样子有些别扭。其实数组指针的原型确实就是这样子的,只不过为了方便与好看把指针变量p2 前移了而已。你私下完全可以这么理解这点。虽然编译器不这么想
struct Test
{
int Num;//4bytes
char *pcName; //4bytes
short sDate; //2bytes
char cha[2]; //2bytes
short sBa[4]; //8bytes
}*p;
假设p 的值为0x100000。如下表表达式的值分别为多少?
p + 0x1 = 0x___ ?
(unsigned long)p + 0x1 = 0x___?
(unsigned int*)p + 0x1 = 0x___?
我相信会有很多人一开始没看明白这个问题是什么意思。其实我们再仔细看看,这个知识点似曾相识。一个指针变量与一个整数相加减,到底该怎么解析呢?
还记得前面我们的表达式“a+1”与“&a+1”之间的区别吗?其实这里也一样。指针变量与一个整数相加减并不是用指针变量里的地址直接加减这个整数。这个整数的单位不是byte 而是元素的个数。所以:p + 0x1 的值为0x100000+sizof(Test)*0x1。至于此结构体的大小为20byte,前面的章节已经详细讲解过。所以p +0x1 的值为:0x100014。
(unsigned long)p + 0x1 的值呢?这里涉及到强制转换,将指针变量p 保存的值强制转换成无符号的长整型数。任何数值一旦被强制转换,其类型就改变了。所以这个表达式其实就是一个无符号的长整型数加上另一个整数。所以其值为:0x100001。
(unsigned int*)p + 0x1 的值呢?这里的p 被强制转换成一个指向无符号整型的指针。所以其值为:0x100000+sizof(unsigned int)*0x1,等于0x100004。
深入 char * ,char ** ,char a[ ] ,char *a[]
C语言中字符串常量(存储于常量区的,语言中"xxx")的本质表示其实是一个地址
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
int main()
{
char *c1 = "abc";
char c2[] = "ABC";
char *c3 = (char *)malloc(sizeof(char)*3);
//c3 = "def";
printf("c1 %p %p %s\n",&c1,c1,c1);
printf("c2 %p %p %s\n",&c2,c2,c2+2);
printf("c3 %p %p %s\n",&c3,c3,c3);
free(c3);
printf("c3 %p %p %s\n",&c3,c3,c3);
char *a[] = {"AME","CHin","Indi"};
printf("a0 %p %p %s\n",&a[0],a[0],a[0]);
printf("a1 %p %p %s\n",&a[1],a[1],a[1]);
int *p=new int;
printf("p here is stack address: %p\n heap address:%p %s\n",&p,p,p);
}
//两次的运行结果
c1 0x7ffd5c3f8f88 0x400a88 abc
c2 0x7ffd5c3f8fc0 0x7ffd5c3f8fc0 C
c3 0x7ffd5c3f8f90 0x1649c20
c3 0x7ffd5c3f8f90 0x1649c20
a0 0x7ffd5c3f8fa0 0x400ab3 AME
a1 0x7ffd5c3f8fa8 0x400ab7 CHin
p here is stack address: 0x7ffd5c3f8f98
heap address:0x1649c20
c1 0x7fff43219f58 0x400a88 abc
c2 0x7fff43219f90 0x7fff43219f90 C
c3 0x7fff43219f60 0x24afc20
c3 0x7fff43219f60 0x24afc20
a0 0x7fff43219f70 0x400ab3 AME
a1 0x7fff43219f78 0x400ab7 CHin
p here is stack address: 0x7fff43219f68
heap address:0x24afc20
这段代码运行,能帮助理解数组与指针:
第一列是这些指针变量的地址,都是栈区
第二列是变量指向的地址,
c1好分析是指向字符常量区,因为c1类型是一个指针,指到了字符常量区的一个字符串头。
c2指向了栈区,也就是说c2的声明方式实质还是数组声明,直接在栈区分配了数组然后把字符常量填充进去
c3是动态申请了堆内存,指向了堆内存(每次运行差得很多),但是要注意一点,如果中间内行没有加注释的话,会段错误,但是windos没有发生这个情况,这个分析是因为中间把c3指针的值改变了, 指向了常量字符串,free就会出问题,windows上可以看出来指向了字符常量区,相应申请出的内存可能没有delete成功,可能就此就产生了内存泄漏,注意一下以后申请了内存就不要动这个指针指向的地址,只动地址里面的内容,使用memcpy,strcpy等这种操作内存的方式。
a是指针数组,在栈区的连续一块区域,每个元素占四个字节,每个元素是一个指针变量,而存的内容指向了常量区
高维数组 多级指针
int a[2][3] = {{11,12,13},{21,22,23}};
int (*p)[2][3];
p = &a;
int (*p_hang)[3];
p_hang = a +1;//a,a+1,a+2代表第i个数组的首地址,所以可以用数组指针赋值
int (**p_sec)[2][3];//二级指针就是对相应类型的一级指针取地址
p_sec = &p;
int (**p_seco)[3];
p_seco = &p_hang;
return 0;
行地址:
a是第一行地址
a+1是第二行地址
a+2是第三行地址
列地址:
a[0]+0第一列地址
a[0]+1第二列的地址
a[0]+2第三列的地址
具体项目地址如:
a[1][2]的地址:为以下三种表示方式
1. a[1]+2;
2. *(a+1)+2
3. &a[1][2]
a[1][2]里面的值:为一下三种表示方式
4. *(a[1]+2)
5. *(*(a+1) +2)
6. a[1][2]
/二级指针就是对相应类型的一级指针取地址
不能赋值,编译器会提示指针类型不同