二次指针与二维数组
- 格式:doc
- 大小:152.50 KB
- 文档页数:3
C语言数组参数与指针参数我们都知道参数分为形参和实参。
形参是指声明或定义函数时的参数,而实参是在调用函数时主调函数传递过来的实际值。
一、一维数组参数1、能否向函数传递一个数组?看例子:void fun(char a[10]){char c = a[3];}intmain(){char b[10] = “abcdefg”;fun(b[10]);return 0;}先看上面的调用,fun(b[10]);将b[10]这个数组传递到fun 函数。
但这样正确吗?b[10]是代表一个数组吗?显然不是,我们知道b[0]代表是数组的一个元素,那b[10]又何尝不是呢?只不过这里数组越界了,这个b[10]并不存在。
但在编译阶段,编译器并不会真正计算b[10]的地址并取值,所以在编译的时候编译器并不认为这样有错误。
虽然没有错误,但是编译器仍然给出了两个警告:warning C4047: 'function' : 'char *' differs in levels of indirection from 'char 'warning C4024: 'fun' : different types for formal and actual parameter 1这是什么意思呢?这两个警告告诉我们,函数参数需要的是一个char*类型的参数,而实际参数为char 类型,不匹配。
虽然编译器没有给出错误,但是这样运行肯定会有问题。
如图:这是一个内存异常,我们分析分析其原因。
其实这里至少有两个严重的错误。
第一:b[10]并不存在,在编译的时候由于没有去实际地址取值,所以没有出错,但是在运行时,将计算b[10]的实际地址,并且取值。
这时候发生越界错误。
第二:编译器的警告已经告诉我们编译器需要的是一个char*类型的参数,而传递过去的是一个char 类型的参数,这时候fun 函数会将传入的char 类型的数据当地址处理,同样会发生错误。
c语言二维数组体会-回复主题:C语言二维数组体会C语言中的二维数组是一种非常常用的数据结构,它可以理解为一个表格或矩阵,由多个行和列组成。
在此文章中,我将分享我对C语言二维数组的体会和心得。
一、二维数组的概念和定义二维数组是由多个一维数组组成的数据结构,它可以看作是一个表格,横向为行,纵向为列。
在C语言中,二维数组的定义方式为:`数据类型数组名[行个数][列个数]`。
例如:cint matrix[3][3]; 定义一个3行3列的整型二维数组二维数组的每个元素可以通过`数组名[行下标][列下标]`的方式进行访问和操作。
下标从0开始,如:`matrix[0][1]`表示第一行第二列的元素。
二、二维数组的内存结构在内存中,二维数组会被连续地存储。
对于上述的`matrix`数组来说,内存中的存储方式如下:首先会依次存储第一行的元素,然后是第二行的元素,以此类推。
这种连续的存储方式使得访问元素更加高效。
三、二维数组的初始化对于二维数组的初始化,可以使用`{}`的方式进行。
例如:cint matrix[3][3] = {{1, 2, 3},{4, 5, 6},{7, 8, 9}};上述代码将数组初始化为一个3行3列的矩阵,分别填充了1到9的数字。
如果只对数组的部分元素进行初始化,则没有被初始化的元素将被自动赋值为0。
四、二维数组的遍历和操作遍历二维数组可以使用嵌套的循环结构,外层循环控制行数,内层循环控制列数。
例如,打印上述的`matrix`数组可以使用如下代码:cfor (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {printf("d ", matrix[i][j]);}printf("\n");}对于二维数组的操作与一维数组类似,可以进行赋值、取值、运算等操作。
例如,为每个元素乘以2可以使用如下代码:cfor (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {matrix[i][j] *= 2;}}通过上述循环,`matrix`数组中的每个元素都会被乘以2。
C语言知识点总结8【二维数组】一、二维数组的定义●一个3行,4列的二维数组。
其行号:0,1,2;其列号:0,1,2,3●最大下标的元素为a[2][3],没有a[3][4]这个元素●数组共有3行,每一行都是:4个元素的一维数组,每一行的数组名分别为:a[0],a[1],a[2]●从整体看,任何一个二维数组都可以看成是一个一维数组,只不过其数组元素又是一个一维数组。
●二维数组定义同时若有初始化,可以省略行号不写:如int a[][3]={1,2,3,4,5,6};系统会按照数据的个数,和规定的列数,来确定数据分几行?●二维数组定义同时若有初始化,可以省略行号不写,但列号不能省略:如int a[3][ ]={1,2,3,4,5};系统无法按照数据的个数,和规定的行数,来确定数据分几列。
二、二维数组的存储及地址关系二维数组在计算机中的存储是按行连续存储。
先保存第一行,在第一行末尾开始存第二行,依此类推。
这里,a是a[0]的地址,a[0]是数组元素a[0][0]的地址,则a是地址的地址,即二级地址三、 二维数组的初始化1、 分行赋值:int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};2、 不分行赋值:全部数据写在一个大括号内:int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};3、 部分元素赋值4、如果对全部元素赋初值,则第一维的长度可以不指定,但必须指定第二维的长度。
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; 等价:int a[ ][4]={1,2,3,4,5,6,7,8,9,10,11,12};四、 二维数组的输出五、二维数组的输入六、二维数组的应用案例1:计算一个二维数组的主对角线元素之和主对角线元素的特点:行号与列号相同。
选择性求和。
反对角线元素的特点:?#include<stdio.h>void main(){int a[4][4]={{1,1,1,1},{2,2,2,2},{3,3,3,3},{4,4,4,4}};int i,j;int s=0;for(i=0;i<4;i++)for(j=0;j<4;j++)if(i==j)s=s+a[i][j];printf("%4d\n",s);}案例2:一共有5名同学,参加了3门课程的考试。
⼆维数组和⼆级指针的传递问题(转载)先看个⾃⼰的例⼦吧:Write an algorithm such that if an element in an M*N matrix is 0, its entire row and column are set to 0;//本作⽬的在于熟悉多维数组的传递# include<iostream># define ROW_N 3# define LINE_M 4using namespace std;//传递矩阵时候要说明维度……void print_matrix(int matrix_ptr[ROW_N][LINE_M]){for(int i=0;i<ROW_N;i++){for(int j=0;j<LINE_M;j++){//cout<<*(*(matrix_ptr+i)+j)<<' ';//*(*(matrix_ptr+i)+j) 和 matrix_ptr[i][j]等价cout<<matrix_ptr[i][j]<<'';}cout<<'\n';}}void clear_row(int matrix_ptr[ROW_N][LINE_M], int row){for (int i=0;i<LINE_M;i++){*(*(matrix_ptr + row)+i)=0;}}void clear_line(int matrix_ptr[ROW_N][LINE_M], int line){for (int i=0;i<ROW_N;i++){*(*(matrix_ptr+i)+line)=0;}}void checkzeropoint(int matrix_ptr[ROW_N][LINE_M]){int localmatrix[ROW_N][LINE_M];int i, j;for(i=0;i<ROW_N;i++){for(j=0;j<LINE_M;j++){localmatrix[i][j]=*(*(matrix_ptr+i)+j);}}print_matrix(localmatrix);for(i=0;i<ROW_N;i++){for(j=0;j<LINE_M;j++){if(*(*(localmatrix+i)+j)==0){cout<<"(i,j)="<<i<<','<<j<<endl;clear_row(matrix_ptr,i);clear_line(matrix_ptr,j);}}}}int main(){int matrix[3][4]={{1,2,3,4},{1,2,0,14},{9,8,6,5}};checkzeropoint(matrix);//cout <<matrix[0][0]<<endl;cout<<endl;print_matrix(matrix);return0;}原⽂链接:/?p=1822再次看这篇⽂章,感觉说的好多都是废话,在⽂章最前⾯补充⼀句话:“[]的优先级⾼于*”,⼤家可以带着这句话看下⾯的~~~========================再⼀次的见证了⾃⼰的基础不牢靠。
二重指针详解朱有鹏1.二重指针1.1、二重指针与普通一重指针的区别本质上来说,二重指针和一重指针的本质都是指针变量,指针变量的本质就是变量。
一重指针变量和二重指针变量本身都占4字节内存空间,1.2、二重指针的本质(1)二重指针本质上也是指针变量,和普通指针的差别就是它指向的变量类型必须是个一重指针。
二重指针其实也是一种数据类型,编译器在编译时会根据二重指针的数据类型来做静态类型检查,一旦发现运算时数据类型不匹配编译器就会报错。
(2)C语言中如果没有二重指针行不行?其实是可以的。
一重指针完全可以做二重指针做的事情,之所以要发明二重指针(函数指针、数组指针),就是为了让编译器了解这个指针被定义时定义它的程序员希望这个指针被用来指向什么东西(定义指针时用数据类型来标记,譬如int*p,就表示p要指向int型数据),编译器知道指针类型之后可以帮我们做静态类型检查。
编译器的这种静态类型检查可以辅助程序员发现一些隐含性的编程错误,这是C 语言给程序员提供的一种编译时的查错机制。
(3)为什么C语言需要发明二重指针?原因和发明函数指针、数组指针、结构体指针等一样的。
1.3、二重指针的用法(1)二重指针指向一重指针的地址(2)二重指针指向指针数组的(3)实践编程中二重指针用的比较少,大部分时候就是和指针数组结合起来用的。
(4)实践编程中有时在函数传参时为了通过函数内部改变外部的一个指针变量,会传这个指针变量的地址(也就是二重指针)进去1.4、二重指针与数组指针(1)二重指针、数组指针、结构体指针、一重指针、普通变量的本质都是相同的,都是变量。
(2)所有的指针变量本质都是相同的,都是4个字节,都是用来指向别的东西的,不同类型的指针变量只是可以指向的(编译器允许你指向的)变量类型不同。
(3)二重指针就是:指针数组指针2、二维数组2.1、二维数组的内存映像一维数组在内存中是连续分布的多个内存单元组成的,而二维数组在内存中也是连续分布的多个内存单元组成的。
CC++中多维数组做参数情况的详细解释我⼤⼆刚学完C语⾔,之后⽤来写矩阵分析的时候想把⼆维矩阵直接传到函数⾥,结果出现了问题:形参实参类型不⼀致,⽆法通过编译!随后我就尝试各种⽅法(改变形参或者实参的格式),虽然最后通过了不过当时还是没理解原理。
后来⾃⼰把原因分析出来了,现在把它写出来,希望对碰到同样问题的朋友有所帮助。
转载请注明出处,谢谢!⼏个跟参数有关的知识:C/C++的函数形参可以是普通类型、指针、引⽤。
传值⽅式有两种:值传递(包括指针)、引⽤。
传参时从左往右,结合时从右往左,这个很重要(函数默认值与此有关)。
参数是指针时,我们⼀般通过两种⽅式实现读写:①移动指针 p++ ② p+i(⽬标位置)或者 p[i],等同于寻址的⽅式实现,他们实现时在内存⾥的操作:⼀维 p+0(p[0]) p+1(p[1]) p+2(p[2]) ······ p+(n-1) (p[n-1]) 由于作图不太⽅便,下⾯的讲解就不附图了。
1、⼀维数组(指针)做参数⼀般指针做参数我就不多说了,专门搜这种问题的⼈应该都懂。
下⾯说⼀下⼀维数组:⼀般传参情况:字符串、整型数组(举个特例,实际上字符串是字符型数组)。
字符串,我们⼀般⽤下⾯这种⽅式:bool PrintStr(char* str)//char str[]也⼀样{if (NULL == str || "" == str){return false;}for (int i = 0; i < strlen(str);i++)//就不考虑效率了,注意不要⽤sizeof{cout << str[i] << " ";}while ('\0' != *str)//通过指针{cout << *str++ << " ";}return true;}2、⼆维数组做参数在⼀维中我们看到,遍历数组时必须有终⽌条件,可以是某种标志也可以规定移动次数。
C语言多维数组与多级指针多维数组与多级指针也是初学者感觉迷糊的一个地方。
超过二维的数组和超过二级的指针其实并不多用。
如果能弄明白二维数组与二级指针,那二维以上的也不是什么问题了。
所以本节重点讨论二维数组与二级指针。
一、二维数组1、假想中的二维数组布局我们前面讨论过,数组里面可以存任何数据,除了函数。
下面就详细讨论讨论数组里面存数组的情况。
Excel 表,我相信大家都见过。
我们平时就可以把二维数组假想成一个excel表,比如:char a[3][4];2、内存与尺子的对比实际上内存不是表状的,而是线性的。
见过尺子吧?尺子和我们的内存非常相似。
一般尺子上最小刻度为毫米,而内存的最小单位为1 个byte。
平时我们说32 毫米,是指以零开始偏移32 毫米;平时我们说内存地址为0x0000FF00 也是指从内存零地址开始偏移0x0000FF00 个byte。
既然内存是线性的,那二维数组在内存里面肯定也是线性存储的。
实际上其内存布局如下图:以数组下标的方式来访问其中的某个元素:a[i][j]。
编译器总是将二维数组看成是一个一维数组,而一维数组的每一个元素又都是一个数组。
a[3]这个一维数组的三个元素分别为:a[0],a[1],a[2]。
每个元素的大小为sizeof(a[0]),即sizof(char)*4。
由此可以计算出a[0],a[1],a[2]三个元素的首地址分别为& a[0],& a[0]+1*sizof(char)*4,& a[0]+ 2*sizof(char)*4。
亦即a[i]的首地址为& a[0]+ i*sizof(char)*4。
这时候再考虑a[i]里面的内容。
就本例而言,a[i]内有4个char 类型的元素,其每个元素的首地址分别为&a[i],&a[i]+1*sizof(char),&a[i]+2*sizof(char)&a[i]+3*sizof(char),即a[i][j]的首地址为&a[i]+j*sizof(char)。
二维数组传值二维数组传值是计算机程序设计中的重要概念之一。
在编程中,数组是一种可以存储多个数据类型的数据结构,而二维数组则是一种具有行和列的数组类型。
二维数组传值所指的是将一个二维数组作为参数传递给一个函数或一个对象,以实现数据处理与操作的功能。
在实际编程中,传递二维数组的方式有很多种,可以通过指针、引用或者直接传递数组来实现。
下面将介绍三种常用的传递二维数组的方式。
1. 通过指针传递二维数组通过指针传递二维数组是一种常用的方式,它可以直接传递二维数组的地址,然后在函数中通过解引用二维指针的方式来访问数组元素。
以下是一个使用指针传递二维数组的例子:void func(int **arr, int rows, int cols){for(int i = 0; i < rows; i++){for(int j = 0; j < cols; j++){printf("%d ", arr[i][j]);}printf("\n");}int main(){int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};func((int **)arr, 2, 3);return 0;}2. 通过引用传递二维数组通过引用传递二维数组是一种更加安全、简单的方式,它允许函数直接使用二维数组的名称来访问数组元素。
以下是一个使用引用传递二维数组的例子:void func(int (&arr)[2][3]){for(int i = 0; i < 2; i++){for(int j = 0; j < 3; j++){printf("%d ", arr[i][j]);}printf("\n");}int main(){int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};func(arr);return 0;}3. 直接传递二维数组直接传递二维数组是一种直接有效的方式,但其传递方式可能会导致数组元素在函数中被复制多次,从而浪费大量的内存空间。
[zt]C++⼆维数组讲解、⼆维数组的声明和初始化定义:int *pia = new int[10]; // array of 10 uninitialized ints此new表达式分配了⼀个含有 10 个int型元素的数组,并返回指向该数组第⼀个元素的指针,此返回值初始化了指针pia。
在⾃由存储区中创建的数组对象是没有名字的,只能通过其地址间接地访问堆中的对象。
注意:C++使⽤new和delete在堆(⾃由存储区)上分配和释放动态数组。
动态数组初始化:1. 元素只能初始化为元素类型的默认值,⽽不能像数组变量⼀样,⽤初始化列表为数组元素提供各不相同的初值。
2. 对于内置数据类型元素的数组,必须使⽤()来显⽰指定程序执⾏初始化操作,否则程序不执⾏初始化操作:int *pia = new int[10]; // 每个元素都没有初始化int *pia2 = new int[10] (); // 每个元素初始化为03.类类型元素的数组,则⽆论是否使⽤(),都会⾃动调⽤其默认构造函数来初始化:string *psa = new string[10]; // 每个元素调⽤默认构造函数初始化string *psa = new string[10](); // 每个元素调⽤默认构造函数初始化动态分配空数组:char *cp = new char[0];之后,可以动态改变cp的维数。
动态释放:delete [] pia;典型使⽤⽰例:const char *pc = "a very long literal string"; // 处理C风格字符串时使⽤const指针const size_t len = strlen(pc) +1; // size_t⽤于数组的⼤⼩和下标for (size_t ix = 0; ix != 1000000; ++ix) {char *pc2 = new char[len]; // pc2指向的存储空间的内容会动态改变,因此不使⽤conststrncpy (pc2, pc, len); // 使⽤strncpy⽐使⽤strcpy安全// do something;delete [] pc2;}参考:⼀、数组定义和初始化1: ⼀维数组初始化:2: 标准⽅式⼀:int value[100]; // value[i]的值不定,没有初始化3: 标准⽅式⼆:int value[100] = {1,2}; // value[0]和value[1]的值分别为1和2,⽽没有定义的value[i>1]4: // 则初始化为05: 指针⽅式:int* value = new int[n]; // 未初始化6: delete []value; // ⼀定不能忘了删除数组空间7:8: ⼆维数组初始化:9: 标准⽅式⼀:int value[9][9]; // value[i][j]的值不定,没有初始化10: 标准⽅式⼆:int value[9][9] = {{1,1},{2}};//value[0][0,1]和value[1][0]的值初始化,其他初始化为011: 指针⽅式⼀:int (*value)[n] = new int[m][n];12: delete []value; // n必须为常量,调⽤直观。
二维数组与指针(教程)二维数组与指针1.二维数组元素在内存中的存放方式在C++中,二维数组元素值在内存中是按行的顺序存放的。
若定义二维整型数组a[3][3],假设编译系统为数组a分配的内存空间从1000开始到1035为止,则数组中各元素a[0][0]~a[2][2]在内存中按行存放次序如图7.6所示。
因此,与一维数组类似,可用指针变量来访问二维数组元素。
【例7.7】用指针变量输出二维数组各元素的值。
# include <iostream.h>void main(void){ int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};int *p=&a[0][0]; //将二维数组首地址赋给指针变量pfor (int i=0;i<9;i++){ cout<<*p<<'\t'; //输出二维数组中第i个元素值p++; //指针变量p加1,指向下一个元素}}程序执行后输出结果为:1 2 3 4 5 6 7 8 9但要用上述指针变量p访问二维数组中任意指定元素a[i][j]就觉得很不方便,为此C++设计者提供另外几种访问二维数组元素的方法,为了了解访问二维数组元素的方法,必须了解三个地址概念,即:二维数组行首地址、行地址、元素地址,现介绍如下。
2.二维数组行首地址二维数组各元素按行排列可写成如图7.7所示矩阵形式,若将第i行中的元素a[i][0]、a[i][1]、a[i][2]组成一维数组a[i] (i=0,1,2),则二维数组a[3][3]可看成是由三个一维数组元素a[0]、a[1]、a[2]组成。
即:a[3][3]=(a[0],a[1],a[2]),其中:a[0]、a[1]、a[2]是分别表示二维数组a[3][3]的第0、1、2行元素。
即:a[0]=(a[0][0],a[0][1],a[0][2])a[1]=(a[1][0],a[1][1],a[1][2])a[2]=(a[2][0],a[2][1],a[2][2])因为数组名可用来表示数组的首地址,所以一维数组名a[i]可表示一维数组(a[i][0],a[i][1],a[i][2])的首地址&a[i][0],即可表示第i行元素的首地址。
CC++——⼆维数组与指针、指针数组、数组指针(⾏指针)、⼆级指针的⽤法1. ⼆维数组和指针要⽤指针处理⼆维数组,⾸先要解决从存储的⾓度对⼆维数组的认识问题。
我们知道,⼀个⼆维数组在计算机中存储时,是按照先⾏后列的顺序依次存储的,当把每⼀⾏看作⼀个整体,即视为⼀个⼤的数组元素时,这个存储的⼆维数组也就变成了⼀个⼀维数组了。
⽽每个⼤数组元素对应⼆维数组的⼀⾏,我们就称之为⾏数组元素,显然每个⾏数组元素都是⼀个⼀维数组下⾯我们讨论指针和⼆维数组元素的对应关系,清楚了⼆者之间的关系,就能⽤指针处理⼆维数组了。
设p是指向⼆维数组a[m][n]的指针变量,则有:int* p=a[0];//此时P是指向⼀维数组的指针。
P++后,p指向 a[0][1]。
如果定义int (*p1)[n];p1=a;p1++后,p1指向a[1][0];则p+j将指向a[0]数组中的元素a[0][j]。
由于a[0]、a[1]┅a[M-1]等各个⾏数组依次连续存储,则对于a数组中的任⼀元素a[i][j],指针的⼀般形式如下:p+i*N+j 相应的如果⽤p1来表⽰,则为*(p1+i)+j元素a[i][j]相应的指针表⽰为:*( p+i*N+j) 相应的如果⽤p1来表⽰,则为*(*(p1+i)+j)同样,a[i][j]也可使⽤指针下标法表⽰,如下:p[i*N+j]例如,有如下定义:int a[3][4]={{10,20,30,40,},{50,60,70,80},{90,91,92,93}};则数组a有3个元素,分别为a[0]、a[1]、a[2]。
⽽每个元素都是⼀个⼀维数组,各包含4个元素,如a[1]的4个元素是a[1][0]、a[1][1]、a[1]2]、a[1][3]。
若有:int *p=a[0];则数组a的元素a[1][2]对应的指针为:p+1*4+2元素a[1][2]也就可以表⽰为:*( p+1*4+2)⽤下标表⽰法,a[1][2]表⽰为:p[1*4+2]特别说明:对上述⼆维数组a,虽然a[0]、a都是数组⾸地址,但⼆者指向的对象不同,a[0]是⼀维数组的名字,它指向的是a[0]数组的⾸元素,对其进⾏“*”运算,得到的是⼀个数组元素值,即a[0]数组⾸元素值,因此,*a[0]与a[0][0]是同⼀个值;⽽a是⼀个⼆维数组的名字,它指向的是它所属元素的⾸元素,它的每⼀个元素都是⼀个⾏数组,因此,它的指针移动单位是“⾏”,所以a+i指向的是第i个⾏数组,即指向a[i]。
二次指针与二维数组
首先我们需要知道,什么是二次指针?下面的代码就定义了一个二次指针变量p2。
char **p2;
还是通过图来说明变量、指针变量、二次指针变量的关系吧。
对于代码:
char c = …h‟;
char *p1 = &c;
char **p2 = &p1;
其中c、p1、p2的关系如下图所示:
由于“指针生成”规则(参见教材P176)的存在,我们无法将一个数组传递给一个函数。
一个折中的解决办法是传递这个数组的第一个元素的地址,以及数组的各个维数,然后在函数中计算得到要操作的数组元素的地址来操作该数组元素。
例如:
void fun(char *p, int n, int m)//从传入指针指向元素开始,将m*n个元素赋值为5
{
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
*(p + i*n + j) = 5;
}
main()
{
char a[2][3];
fun(&a[0][0], 2, 3);
return 0;
}
显然,其中的fun(&a[0][0], 2, 3);对数组的操作相当于:
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
a[i][j] = 5;
在这段代码中,a[i][j]的形式显然更为浅显易懂,也*(p + i*n + j) = 5更为安全(不容易写错)。
接下来我们要做的,就是希望实现这样的功能:函数能够以a[i][j]这样的下标处理一个维度变化的二维数组。
通过指针数组,我们可以实现一个一维长度不同的二维数组。
当然,这也就意味着该二维数
组的元素在内存中的位置不一定连续了。
什么在指针数组呢?看如下代码:
char *pp[7];
这就是一个指针数组,它表示一个长度为7的数组pp,而数组pp的每一个元素的类型,都是char *。
也就是说,我们可以将数组pp的元素赋值给另一个指针变量,就像这样:
char *p = pp[3];
而联系到第一部分指针与一位数组之间的关系,我们可以有让数组pp的每个元素都指向不同的指针,比如说这样:
char a[13] = "ustc", b[17] = "1958";
char *t = a + 2;
char *p[5] = {a, b, t};//VC 6.0中可以用变量对数组的值进行初始化
执行以上代码之后,p[0]就指向了a数组,p[1]就指向了b数组。
而p[2]在这里的含义,也是指向一个数组。
我们可以看到,通过指针是无法获知数组的长度的。
这也就是为什么在C 语言中,字符数组后面会跟一个‟\0‟来作为结束标致。
而在操作其他类型的数组的时候,也是需要显示在代码中指定数组的长度。
printf(“%s\n”, p[2]);
上述代码执行后将输出
tc
因为printf函数在输出字符串的时候,会从传入的参数开始一直向外输出,直到遇到字符串结束标志‟\0‟。
好。
接下来我们要问了,char a[5]中,a的数据类型可以看做是一个字符指针(char *),那么char *pp[7]中,pp又是什么数据类型呢?这里pp的数据类型,就是我们本部分开始时提到的二次指针了。
我们可以通过如下代码来声明一个二次指针变量,并且并给它赋值:char *pp[7];
char **p2p = pp;
而就像我们可以对一个指针变量char *p进行指针运算p[2]或*(p + 2)获取对应的值一样,我们也可以通过如下代码获取一个指针。
char *p1, *p2;
p1 = p2p[3];
p2 = p2p[5];
而对于上述定义的p1、p2,我们还是仍然使用类似p[2]、*(p + 2)的指针运算。
于是,我们就能得到类似二维数组的使用方法了。
char a[13] = "ustc", b[17] = "1958";
char *p[5] = {a, b};//VC 6.0中可以用变量对数组的值进行初始化
char **p2p = pp;
printf(“%c\n”, pp[0][2]);
上述代码将输出一个字符‟t‟,以及换行。
好了,到此为止我们完成了对二次指针的分析。
在文章动态分配二维数组(或动态分配二维数组)当中,给出了利用二次指针动态分配二维数组的代码样例。
以及内存分配的示意图。
通过分析该文章的代码和配图,有助于我们更深入的理解二次指针。
而我们也需要在心中牢记的,是二次指针与二维数组名之间的区别。
它们是完全不同的两种数据类型。
在编程时,都可以通过类似*(*(p + 2) + 3)、p[2][3]的方式来访问一个具体的数组元素。
但是他们在计算上述表达式以获取数据时,进行的操作是不一样的。
总之,这种表达
形式上的一致性有利于我们进行编程,但同时也是让我们对他们的区别感到迷惑的根源所在。
C语言中,二维数组名与二次指针之间的关系着实令人头疼。
在进入下一步的讨论之前,先举例说明一下什么是二维数组名和二次指针。
char a2[5][8];
上述代码中,a2是一个二维数组名,而p2则是一个二次指针(指向指针的指针)
这两个概念之间错综复杂的关系之所以令人头疼,我以为主要来自以下两个方面。
其一是一维数组名与指针之间的对应关系,多少让人对二维数组与二次指针之间的关系产生亦真亦幻的遐想。
事实上,二维数组与二次指针之间没有关系,尝试编译以下代码你就知道这个想法不靠谱了:char a[5][8];
char **p;
p = a; //cannot convert from 'char [5][8]' to 'char ** '
编译这段代码,将得到一个提示为“cannot convert from 'char [5][8]' to 'char ** '”的错误。
意思是a和p的类型不一样,而且无法实现隐式转换。
在这句提示中,最让人头疼和摸不着头脑的,莫过于char [5][8]了,这是什么鬼东西?
另一个让人对二维数组与二次指针之间对应关系感到迷糊的,还在于我们确实可以通过二次指针来实现一个二维数组,也能像访问二维数组元素一样通过下标索引的方式访问二次指针指向的指向的内存空间。
具体的方法可参考这篇文章:动态分配二维数组(或动态分配二维数组)。
从这篇文章的代码极其配图讲解中,能够大致了解到二次指针与二维数组之间的关系并没有我们想想的那么简单。
至少用二次指针模拟二维数组涉及到复杂的malloc操作。
(幸好在C++中,定义了一个数组指针char (*p1)[8]之后,可以利用new操作符写出简洁明晰的表达式new int[5][8])。