数组指针,字符串和字符数组指针指针的几种定义方法

指针数组和数组指针
下面就简单说说这两个概念:
一:指针数组,顾名思义,就是说的首先是一个数组吧,然后数组的元素是指针而已。
说明形式为:type *pointer_array[constant1][constant2]...[constantn];
例如:int *pai[3];
由于&*&是自右向左结合,因此从右向左看,首先看到[4]说明是一个数组,是一个包含4个元素的数组,然后看到&*&,显然是指针类型,由此可以看出数组中存放的是指针而不是一般的类型。同理,char *pac[2][3]是包含有6个元素,每一个元素都是一个字符型指针。再来说说他们的初始化:
int *pai[3];既然是一个包含4个整形指针的数组那么其对应的将是一个二维整形数组,因为一个整形指针对应一个一维整形数组。
那我就用一个二维整形数组来初始化它,事实上一般也都是这么做的,这里有一个二维数组,int arr[3][2]={{1,2},{3,4},{5,6}},一个三行两列的整形数组,注意这里的行必须和你的指针数组的维数一致,否则是不允许的,不信你可以试试。
这个初始化有很多种选择,以下只列举常见的两中:
第一种也是很好理解的:
for(int i=0;i&3;i++)
& pai[i]=arr[i];
显然arr[i]是每一行的首地址,相当于一个一维数组的数组名,如是把它送给一个整形指针pai[i]是理所当然的了。
第二种方法:
在说明指针数组时就初始化:int (*ap)[2]={{1,2},{3,4},{5,6}};哈哈这个不用说了吧。
注意:不能将二维数组的数组名赋给指针数组的数组名,pai=arr(错),因为两者的类型不一致,二维数组名的类型是指向int[][]型的指针,而指针数组的的数组名是指向int *[]类型的指针。
在c/c++语言中,指针数组最常用的场合就是说明一个字符串数组。即说明一个数组,它的每个元素都是一个字符串。
二:数组指针:指向一个数组的指针。
说明形式为:type (*pointer_array)[constant1][constant2]...[constantn];
注意这里的圆括号是必须就将这是由于方括号[],较指针说明符&*&的优先级高,若无此圆括号,编译器将把上述说明解释成成了一个数组指针。
例如:int (*ap)[2];
这样就说明了一个指向包含有2个元素的整形数组的数组指针,听起来确实有点别扭。不过仔细分析应该还是能理解的,就是说ap是一个指针,而它指向的对象是一个指针,注意不要将它和一个指向一个整形变量的指针搞混了。
同样以一个二维数组来说明其初始化问题,
int arr[3][2]={{1,2},{3,4},{5,6}};注意这里的列数必须和数组指针所指的数组的列数相同。
第一种方法:
为什么这里能这样将二维数组名送给ap呢,你可以这样理解,二维数组不就可以看成是一维数组的数组吗,而一个数组指针它指向的内容就是一个一维数组,那么你就可以把这个数组指针当做是一个数组名,只不过这个数组里的元素不是像int,char之类型的,而是一个数组,这样你就可以把它和二维数组的数组名联系在一起了吧。文笔不行,不知道是否说清楚了。
第二种方法:
ap=&arr[0];
这里arr[0]其实就是一维数组的数组名,将它的地址给ap是很自然的,因为ap本来就是指向一个一维数组的。
注意这里不能这样初始化:int (*a)[2]={{1,2},{3,4},{5,6}};大家可以想想为什么。
当然他们也可以动态赋值,由于篇幅就不探讨了。
一 :关于指针和堆的内存分配
先来介绍一下指针: 指针一种类型,理论上来说它包含其他变量的地址,因此有的书上也叫它:地址变量。既然指针是一个类型,是类型就有大小,在达内的服务器上或者普通的PC机上,都是4个字节大小,里边只是存储了一个变量的地址而已。不管什么类型的指针,char * ,int * ,int (*) ,string * ,float * ,都是说明了本指针所指向的地址空间是什么类型而已,了解了这个基本上所有的问题都好象都变的合理了。
在C++中,申请和释放堆中分配的存贮空间,分别使用new和delete的两个运算符来完成:
指针类型 指针变量名 = new 指针类型 (初始化);
& & & & delete 指针名;
1、 int *p = new int(0);
& & &它与下列代码序列大体等价:
2、int tmp = 0, *p = &
区别:p所指向的变量是由库操作符new()分配的,位于内存的堆区中,并且该对象未命名。
下面是关于new 操作的说明 : 部分引自《C++面向对象开发》
1、new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有名字。
2、一般定义变量和对象时要用标识符命名,称命名对象,而动态的称无名对象(请注意与栈区中的临时对象的区别,两者完全不同:生命期不同,操作方法不同,临时变量对程序员是透明的)。
3、堆区是不会在分配时做自动初始化的(包括清零),所以必须用初始化式(initializer)来显式初始化。new表达式的操作序列如下:从堆区分配对象,然后用括号中的值初始化该对象。
下面是从堆中申请数组
1、申请数组空间:
指针变量名=new 类型名[下标表达式];
注意:&下标表达式&不是常量表达式,即它的值不必在编译时确定,可以在运行时确定。这就是堆的一个非常显著的特点,有的时候程序员本身都不知道要申请能够多少内存的时候,堆就变的格外有用。
2、释放数组空间:
delete [ ]指向该数组的指针变量名;
注意:方括号非常重要的,如果delete语句中少了方括号,因编译器认为该指针是指向数组第一个元素的,会产生回收不彻底的问题(只回收了第一个元素所占空间),我们通常叫它&内存泄露&,加了方括号后就转化为指向数组的指针,回收整个数组。delete [ ]的方括号中不需要填数组元素数,系统自知。即使写了,编译器也忽略。&&Think in c++&&上说过以前的delete []方括号中是必须添加个数的,后来由于很容易出错,所以后来的版本就改进了这个缺陷。
下面是个例子,VC上编译通过
#include&iostream&
//#include &iostream.h&  //for VC
#include &string.h&
void main(){
&&&&&char *p;
&&&&&cout&&&请输入动态数组的元素个数&&&
&&&&&cin&&n; //n在运行时确定,可输入17
&&&&&p=new char[n]; //申请17个字符(可装8个汉字和一个结束符)的内存空间strcpy(pc,&堆内存的动态分配&);//
&&&&&cout&&p&&
&&&&&delete []p;//释放pc所指向的n个字符的内存空间 
通过指针使堆空间,编程中的几个可能问题
1.动态分配失败。返回一个空指针(NULL),表示发生了异常,堆资源不足,分配失败。
& &data = new double [m]; //申请空间
&&&if ((data ) == 0)&& //或者==NULL
2.指针删除与堆空间释放。删除一个指针p()实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身,释放堆空间后,p成了空悬指针,不能再通过p使用该空间,在重新给p赋值前,也不能再直接使用p。
3.内存泄漏(memory leak)和重复释放。new与delete 是配对使用的, delete只能释放堆空间。如果new返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能已另分配,而这个时候又去释放的话,会导致一个很难查出来的运行时错误。所以必须妥善保存new返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。
4.动态分配的变量或对象的生命期。无名变量的生命期并不依赖于建立它的作用域,比如在函数中建立的动态对象在函数返回后仍可使用。我们也称堆空间为自由空间(free store)就是这个原因。但必须记住释放该对象所占堆空间,并只能释放一次,在函数内建立,而在函数外释放是一件很容易失控的事,往往会出错,所以永远不要在函数体内申请空间,让调用者释放,这是一个很差的做法。你再怎么小心翼翼也可能会带来错误。
类在堆中申请内存 :
通过new建立的对象要调用构造函数,通过deletee删除对象要调用析构函数。
pc=new CG &&&& //分配堆空间,并构造一个无名对象
& & & & & & & & & & & & & & & //的CGoods对象;
&//先析构,然后将内存空间返回给堆; & & & &堆对象的生命期并不依赖于建立它的作用域,所以除非程序结束,堆对象(无名对象)的生命期不会到期,并且需要显式地用delete语句析构堆对象,上面的堆对象在执行delete语句时,C++自动调用其析构函数。
正因为构造函数可以有参数,所以new后面类(class)类型也可以有参数。这些参数即构造函数的参数。
但对创建数组,则无参数,并只调用缺省的构造函数。见下例类说明:
class CGoods{
& & & & & char Name[21];
& & & & & int &A
& & & & & float P
& & & & & float Total_
&CGoods(){}; //缺省构造函数。因已有其他构造函数,系统不会再自动生成缺省构造,必须显式声明。 &
&CGoods(char* name,int amount ,float price){
& & & & & &strcpy(Name,name);
& & & & & &Amount=
& & & & & &Price=
& & & & & &Total_value=price*  }
& & & & & &&&};//类声明结束
下面是调用机制 :
void main(){
&&&&&&CGoods *pc,*pc1,*pc2;
&&&&&&pc=new CGoods(&hello&,10,118000);
&&&&&&//调用三参数构造函数 & pc1=new CGoods(); &//调用缺省构造函数 &cout&&&输入商品类数组元素数&&&
&&&&&&cin&&n;
&&&&&&pc2 = new CGoods[n];
&&&&&//动态建立数组,不能初始化,调用n次缺省构造函数 &
&&&&&&delete pc1;
&&&&&&delete []pc2; &
申请堆空间之后构造函数运行;
释放堆空间之前析构函数运行;
再次强调:由堆区创建对象数组,只能调用缺省的构造函数,不能调用其他任何构造函数。如果没有缺省的构造函数,则不能创建对象数组。
---------------------下面我们再来看一下指针数组和数组指针―――――――――――――
如果你想了解指针最好理解以下的公式 :
&&&&&(1)int*//指针所指向的类型是int
 & (2)char*//指针所指向的的类型是char
 & (3)int**//指针所指向的的类型是int* (也就是一个int * 型指针)
 &&(4)int(*ptr)[3];//指针所指向的的类型是int()[3] //二维指针的声明
(1)指针数组:一个数组里存放的都是同一个类型的指针,通常我们把他叫做指针数组。
比如 int * a[10];它里边放了10个int * 型变量,由于它是一个数组,已经在栈区分配了10个(int * )的空间,也就是32位机上是40个byte,每个空间都可以存放一个int型变量的地址,这个时候你可以为这个数组的每一个元素初始化,在,或者单独做个循环去初始化它。
int * a[2]={ new int(3),new int(4) }; & & //在栈区里声明一个int * 数组,它的每一个元素都在堆区里申请了一个无名变量,并初始化他们为3和4,注意此种声明方式具有缺陷,VC下会报错
int * a[2]={new int[3],new int[3]};
delete a[0];
delet a[10];
但是我不建议达内的学生这么写,可能会造成歧义,不是好的风格,并且在VC中会报错,应该写成如下 :
int * a[2];
a[0]= new int[3];
a[1]=new int[3];
delete a[0];
delet a[10];
这样申请内存的风格感觉比较符合大家的习惯;由于是数组,所以就不可以编译会出警告.delete &a[1];
注意这里 是一个数组,不能delete [] ;
( 2 ) 数组指针 : 一个指向一维或者多维数组的指针;
int * b=new int[10]; 指向一维数组的指针
注意,这个时候释放空间一定要delete [] ,否则会造成内存泄露, b 就成为了空悬指针.
int (*b2)[10]=new int[10][10]; 注意,这里的b2指向了一个二维int型数组的首地址.
注意:在这里,b2等效于二维数组名,但没有指出其边界,即最高维的元素数量,但是它的最低维数的元素数量必须要指定!就像指向字符的指针,即等效一个字符串,不要把指向字符的指针说成指向字符串的指针。这与数组的嵌套定义相一致。
int(*b3) [30] [20]; &//三级指针――&指向三维数组的指针;
int (*b2) [20]; & & //二级指针;
b3=new int [1] [20] [30];
b2=new int [30] [20];
& & & 两个数组都是由600个整数组成,前者是只有一个元素的三维数组,每个元素为30行20列的二维数组,而另一个是有30个元素的二维数组,每个元素为20个元素的一维数组。
& & & 删除这两个动态数组可用下式:
delete [] b3; &//删除(释放)三维数组;
delete [] b2; &//删除(释放)二维数组;
再次重申:这里的b2的类型是int (*) ,这样表示一个指向二维数组的指针。
b3表示一个指向(指向二维数组的指针)的指针,也就是三级指针.
( 3 ) 二级指针的指针
int (**p)[2]=new (int(*)[3])[2];
& & & &p[0]=new int[2][2];
& & & &p[1]=new int[2][2];
& & & &p[2]=new int[2][2];
& & & &delete [] p[0];
& & & &delete [] p[1];
& & & &delete [] p[2];
& & & &delete []
注意此地方的指针类型为int (*),碰到这种问题就把外边的[2]先去掉,然后回头先把int ** p=new int(*)[n]申请出来,然后再把外边的[2]附加上去;
p代表了一个指向二级指针的指针,在它申请空间的时候要注意指针的类型,那就是int (*)代表二级指针,而int (**)顾名思义就是代表指向二级指针的指针了。既然是指针要在堆里申请空间,那首先要定义它的范围:(int(*)[n])[2],n 个这样的二级指针,其中的每一个二级指针的最低维是2个元素.(因为要确定一个二级指针的话,它的最低维数是必须指定的,上边已经提到)。然后我们又分别为p[0],p[1],p[2]&在堆里分配了空间,尤其要注意的是:在释放内存的时候一定要为p[0],p[1],p[2],单独delete[] ,否则又会造成内存泄露,在delete[]p 的时候一定先delete p[0]; delete p[1],然后再把给p申请的空间释放掉 delete [] p &&这样会防止内存泄露。
(3)指针的指针;
int ** cc=new (int*)[10]; 声明一个10个元素的数组,数组每个元素都是一个int *指针,每个元素还可以单独申请空间,因为cc的类型是int*型的指针,所以你要在堆里申请的话就要用int *来申请;
看下边的例子 &(vc & GNU编译器都已经通过);
& & & &int ** a= new int * [2];     //申请两个int * 型的空间
& & & &a[1]=new int[3];        //为a的第二个元素又申请了3个int 型空间,a[1]指向了此空间首地址处
& & & &a[0]=new int[4];        ////为a的第一个元素又申请了4个int 型空间,a[0] 指向了此空间的首地址处
& & & &int *
& & & &a[0][0]=0;
& & & &a[0][1]=1;
& & & &b=a[0];
&&&&&&&delete [] a[0]       //一定要先释放a[0],a[1]的空间,否则会造成内存泄露.;
& & & &delete [] a[1];
&&&&&&&delete []
& & & &b++;
& & & &cout&&*b&& & & & //随机数
注意 :因为a 是在堆里申请的无名变量数组,所以在delete 的时候要用delete [] 来释放内存,但是a的每一个元素又单独申请了空间,所以在delete [] a之前要先delete [] 掉 a[0],a[1],否则又会造成内存泄露.
(4) 指针数组 :
我们再来看看第二种 :二维指针数组
int *(*c)[3]=new int *[3][2];
如果你对上边的介绍的个种指针类型很熟悉的话,你一眼就能看出来c是个二级指针,只不过指向了一个二维int * 型的数组而已,也就是二维指针数组。
&int *(*b)[10]=new int*[2][10];//
b[0][0]=new int[100];
b[0][1]=new int[100];
*b[0][0]=1;
cout &&*b[0][0]&& & &//打印结果为1
delete [] b[0][0];
delete [] b[0][1];
cout&&*b[0][0]&& & &//打印随机数
 这里只为大家还是要注意内存泄露的问题,在这里就不再多说了。
如果看了上边的文章,大家估计就会很熟悉,这个b是一个二维指针,它指向了一个指针数组
第二种 :
&&&&&&&&int **d[2];表示一个拥有两个元素数组,每一个元素都是int ** 型,这个指向指针的指针:)
   d不管怎样变终究也是个数组,呵呵,
   如果你读懂了上边的,那下边的声明就很简单了:
   d[0]=new int *[10];
   d[1]=new int * [10];
&&&&&delete [] d[0];
&&&&&delete [] d[1];
具体的就不再多说了 :)
二 : 函数指针 
关于函数指针,我想在我们可能需要写个函数,这个函数体内要调用另一个函数,可是由于项目的进度有限,我们不知道要调用什么样的函数,这个时候可能就需要一个函数指针;
int a();这个一个函数的声明;
ing (*b)();这是一个函数指针的声明;
让我们来分析一下,左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和由边圆括弧中的入口参数(本例中参数是空)。注意本例中还没有创建指针变量-只是声明了变量类型。目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小:
unsigned psize = sizeof (int (*) ()); 获得函数指针的大小
// 为函数指针声明类型定义
typedef int (*PFUNC) ();
PFUNC是一个函数指针,它指向的函数没有输入参数,返回int。使用这个类型定义名可以隐藏复杂的函数指针语法,就我本人强烈建议我们大内弟子使用这种方式来定义;
下面是一个例子,一个简单函数指针的回调(在GNU编译器上通过,在VC上需要改变一个头文件就OK了)
#include&iostream& & & & & & & &//GNU 编译器 g++ 实现
/* & & & & & & & & & & & & & & &//vc 的实现
#include &stdafx.h&
#include &iostream.h&
#define DF(F) int F(){ &cout&&&this is in function &&&#F&&\
& & & return 0; & & & \
//声明定义DF(F)替代 int F();函数;
DF(a); DF(b); DF(c); DF(d); DF(e); DF(f); DF(g); DF(h); DF(i); & & //声明定义函数 a b c d e f g h i
// int (*pfunc)(); & & & & & & &//一个简单函数指针的声明
typedef int(*FUNC)(); & //一个函数指针类型的声明
FUNC ff[] = {a,b,c,d,e,f,g,h,i}; & //声明一个函数指针数组,并初始化为以上声明的a,b,c,d,e,f,g,h,i函数
FUNC func3(FUNC vv){ & &//定义函数func3,传入一个函数指针,并且返回一个同样类型的函数指针
& & & vv();
/*FUNC func4(int (*vv)()){ & & &//func3的另一种实现
& & & vv();
int main(){
& & & for(int i=0;i&sizeof(ff)/sizeof (FUNC);i++){ &//循环调用函数指针
& & & & & & & FUNC r=func3(ff[ i ]);
& & & & & & & cout&&r()&& & & & & & & & &//输出返回值,只是返回了0
& & & return 0;
到目前为止,我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示其调用规范(默认为_cdecl)。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名,参数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及参数传递机制(堆栈,CPU寄存器等)。
好了,先到此为止吧,写这篇文章耗费了基本上快半天的时间了,很多事情还没有做,等改天有时间再回来整理,所有的源程序都放在openlab3服务器上我的目录下lib/cpp下,大家可以去拿。不知道的登陆openlab3 然后cd ~chengx/lib/cpp就可以看到了。
还有很复杂的声明可能也是一种挑战 比如&&Think in c++&&里的
int (*(*f4())[10]();的声明,f4是一个返回指针的函数,该指针指向了含有10个函数指针的数组,这些函数返回整形值;不是这个函数有特别之处,而是Bruce Eckel 说的&从右到左的辨认规则&是一种很好的方法,值得我们去学习,感谢他:)
最后我想应该跟大家说一下,写程序应该就象JERRY所说的:简单就是美;我们应该遵循一个原则 : KISS (Keep It Simple,Stupid ,尽量保持程序简单 出自 :《Practical C programming》),把自己的程序尽量的简单明了,这是个非常非常好的习惯。
& 2010 - 2018 苏州灵动帧格网络科技有限公司 版权所有.
ICP经营许可证 苏B2-&一、字符指针、字符数组
1. 字符指针:字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身是存放在以该首地址为首的一块连续的内存空间中并以&\0&作为串的结束。
& & & & char *ps="C Language"; & & &顺序是:1.分配内存给字符指针;2.分配内存给字符串;3.将字符串首地址赋值给字符指针;
& & & & 可以写为:
& & & & char * &#ps字符串指针,是指针,是一个变量,
& & & & ps="C Language"; #ps为字符串的首地址,利用ps++可遍历字符串,字符串存储在以ps为开始地址的地段连续的内存空间中,并以'\0'作为字符串的结束。
这里有两点需要考虑清楚的地方:①*a只是指向一个字符。举例如下:
1.#include&&stdio.h&
2.#include&&stdlib.h&
4.int&main(void){ &
5.char&*a=&"bcd"&; &
6.printf("输出字符:%c&/n",&*a); &/*输出字符,使用"%c"*/&br&&&&&
& &printf("输出字符:%c&/n",&*(a+1)&); &/*输出字符,使用"%c"*/
7.printf("输出字符串:%s&/n",&a);&/*输出字符串,使用"%s";而且a之前不能有星号"*"&&*/
8.system("pause"); &/*为了能看到输出结果*/
10./*运行结果如下:
11.输出字符:b&br&输出字符:c
12.输出字符串:bcd*/
②若字符串常量出现在在表达式中,代表的值为该字符串常量的第一个字符的地址。所以&hello&仅仅代表的是其地址。原声明方式相当于以下声明方式:
1.char&*a; &
2.a="hello";/*这里字符串"hello"仅仅代表其第一个字符的地址*/
2. 字符数组:字符数组是由于若干个数组元素组成的,它可用来存放整个字符串。(即用字符数组来存放字符串)。
& & & & & & & & &&在c语言中,将字符串作为字符数组来处理。(c++中不是)
& & & & & & & & &&字符数组初始化的方法:
& & & & & & & & & & & & & & 1). &可以用字符串常量来初始化字符数组:char &str[]={"Iamhappy"}; & 可以省略花括号
& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & char &str[]="Iamhappy"; &#系统自动加入&\0&
& & & & & & & & & & & & & & 注意:上述这种字符数组的整体赋值只能在字符数组初始化时使用,不能用于字符数组的赋值,字符数组的赋值只能对其元素一一赋值。
& & & & & & & & & & & & & & 下面的赋值方法是错误的:
& & & & & & & & & & & & & & &char str[20];
& & & & & & & & & & & & & & &str="Iamhappy"; &
& & & & & & & & & & & & & & &对字符数组的各元素逐个赋值。
& & & & & & & & & & & & & && char str[10]={'I','','a','m','',&h&,'a','p','p','y'};
在C语言中,可以用两种方法表示和存放字符串:
(1)用字符数组存放一个字符串& & & &char str[]="IloveChina";(2)用字符指针指向一个字符串& & & &char *str="IloveChina";
两种表示方式的字符串输出都用:printf(&%s\n&, str);
%s表示输出一个字符串,给出字符指针变量名str(对于第一种表示方法,字符数组名即是字符数组的首地址,与第二种中的指针意义是一致的),则系统先输出它所指向的一个字符数据,然后自动使str自动加1,使之指向下一个字符...,如此,直到遇到字符串结束标识符"\0"。
二、字符串指针
string* str可以赋值string* str = {"hello", "world"};//对比与char *name = "wang" = {'w','a','n','g'}& & & & & & & & & & & & & //?? &*(str) = "hello", *(str+1) = "world"& & & & & & & & & & & & & //?? &*(*(str)+1) = 'e',也就是说每个元素都是string类型的,跟char*是不一样的,不过string*可以用char**来代替//string = char*, string* = char**
三、(字符串)指针数组
#include &stdio.h&void main(){
char *str[] = {"Hello", "C++", "World"};//char (*str)[] = ...
for(i=0; i&3; i++)
printf("%s\n", str[i]);} #str[0]字符串&hello&的首地址,str[0]+1:字符串&hello&第二个字符&e&的地址,str[2]=str+2:第三个字符串&world&的首地址
#str[1]字符串&C++&的首地址
#str[2]字符串&world&的首地址
或#include &stdio.h&#include &string.h&void main(){
char *str[] = {"Hello", "C++", "World"};
for(p= p&str+3; p++)
puts(*p); #*p为字符串首地址,*p[0]为字符串的第一个字符地址}
#include&stdio.h&#include&stdlib.h&main(){
char *str[3]={"Hello","C++","World"};
printf("%s,%s,%c",str[0],str[0]+1,*(*(str+2)+1));
system("pause");
}结果为:Hello,ello,o
格式:char* na[N] = {"li", "zh", "li", "zh", "li"};
char *a[]:表示a是数组,数组中的元素是指针,指向char类型,(数组里面所有的元素是连续的内存存放的)
&数组名是数组第一个字节的内存地址,并且数组名a也表示指针。所以a 并不表示a地址存储的内容, 而是a地址本身。
a+1 表示a的第二个元素的内存地址, 所以是加8字节.( 因为a的元素是char 指针, 所需要的空间为8字节(64位内存地址). )&
*(a+1) 则表示a这个数组的第二个元素的内容 (是个char 类型的指针. 本例表示为world字符串的地址).
*(*(a+1)) 则表示a这个数组的第二个元素的内容(char指针)所指向的内容(w字符).
char * a[10] 表示限定这个数组最多可存放10个元素(char指针), 也就是说这个数组占用10*8 = 80字节.
#w: & & & &a+1 & =& & *(a+1) &=& & *(a+1)[0]
& & & & & &指针(地址) & 指针内容(字符串) & & &字符
char *argv:理解为字符串
char **argv:理解为字符串指针
char *argv[]:字符串指针数组
int main(int argc, char*argv[])
这是一个典型的数组名(或者说是指针数组)做函数参数的例子,而且还是没有指定大小的形参数组。
有时为了再被调用函数中处理数组元素的需要,可以另设一个形参,传递需要处理的数组元素的个数。而且用数组名做函数实参时,不是吧数组元素的值传递给形参,而是把实参数组的首元素的地址传递给形参数组,这样两个数组久共同占有同一内存单元。
和变量作函数参数的作用不一样。 可以去看看关于数组作为函数参数和指针数组作main函数形参方面的例子。谭浩强的那本书讲的很细,对这个有详细的解释。
1. 当char []作为函数的参数时, 表示 char *. 当作为函数的参数传入时, 实际上是拷贝了数组的第一个元素的地址 .&
& & 所以 void test (char a[]) 等同于 void test ( char * a )
& & char x[10] ; 然后调用 test(x) 则等同于把 x 的第一个元素的地址赋予给参数 a .&
2. char * a 和 char a[]&
相同点 : a都是指针, &指向char类型.
不同点 : char a[] 把内容存在stack .&
& & & & & & & char *a 则把指针存在stack,把内容存在constants.&
3. char * a[10] 和 char a[10][20]
相同点 : a 都是2级指针, *a 表示一级指针, **a 表示内存中存储的内容.
不同点 : &char * a[10], 数组由char * 类型的指针组成;&
& & & & & & & &char a [10][20] 表示一位放10个元素, 二维放20个元素, 值存放地是一块连续的内存区域, 没有指针.
4. 小窍门 : &[]和*的数量对应, 如 char a[][]的指针层数是2, 相当于char **a; char *a[]也是如此, 两层指针. 迷糊的时候数数到底有几个*几个[], 就知道什么情况下存储的是内容还是地址了? 如char a[][] 的情况里面: &a, a, *a 都是地址, **a 是内容.
阅读(...) 评论()

我要回帖

更多关于 指针数组输入字符串 的文章

 

随机推荐