动态二维数组new,typedef的几个陷阱,数组名与指针

本文详细介绍了C++中二维数组new的五种常见使用方式及优缺点对比,同时揭示了类型别名typedef在使用时可能遇到的陷阱。包括内存分配、释放、内存布局和内存对齐等问题,旨在帮助开发者更深入地理解和运用C++的内存管理机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


二维数组new小结 


1. 
A (*ga)[n] = new A[m][n]; 
... 
delete []ga; 
缺点:n必须是已知 
优点:调用直观,连续储存,程序简洁(经过测试,析构函数能正确调用) 

2. A** ga = new A*[m]; 
for(int i = 0; i < m; i++) 
ga[i] = new A[n]; 
... 
for(int i = 0; i < m; i++) 
delete []ga[i]; 
delete []ga; 
缺点:非连续储存,程序烦琐,ga为A**类型 
优点:调用直观,n可以不是已知 

3. A* ga = new A[m*n]; 
... 
delete []ga; 
缺点:调用不够直观 
优点:连续储存,n可以不是已知 

4. vector > ga; 
ga.resize(m); //这三行可用可不用 
for(int i = 1; i < n; i++) // 
ga[i].resize(n); // 
... 

缺点:非连续储存,调试不够方便,编译速度下降,程序膨胀(实际速度差别不大) 
优点:调用直观,自动析构与释放内存,可以调用stl相关函数,动态增长 

5. vector ga; 
ga.resize(m*n); 
方法3,4的结合 


6. 2的改进版
A** ga = new A*[m]; 
ga[0] = new A[m*n]; 
for(int i = 1; i < m; i++) 
ga[i] = ga[i-1]+n; 
优点:连续存储,n可以不是已知,析构方便,只需delete [] ga和delete ga[0];


7.typedef int array[COL];       //使用typedef定义一个具有COL个元素的数组类型

           array *a;  //定义二维数组,与一维数组相同
           a=new array[row]; //为该一维数组(实际上二维)申请空间
                   
      该方法定义的动态二维数组的释放只需以下语句即可:
           delete[] a;

           a=NULL; 



-------------------
指向数组的指针 int (*p)[3]
指针数组 int *p[3]
指向指针的指针 int **p
int ptr[4][4]中定义的ptr本身是一个常量.这个常量用来使编译器明白你所引用的是那个你已经为其分配过内存的数组,ptr这个引用的有效性依赖于 编译器,编译器会直接把它替换成某个常量——在运行时,根本没有任何内存空间来把这个值当作变量存放,它存在于代码中,是在连接时已经指定好的,一般的, 程序本身没有权利再去改变它。
函数指针  int (*P) ()          首先P与*结合,说明是一个指针,然后与()结合,说明该指针指向一个函数,然后与INT结合,说明函数的返回值为INT


typedef int (*funcptr)();    // funcptr is synonym for "pointer
                                       // to function returning int"
funcptr table[10];            // Equivalent to "int (*table[10])();"


数组名大小与指针大小问题
数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。不论数组a的容量是多少,sizeof(a)始终等于sizeof(type  *)。 
if         数组名直接作为参数压栈
then     sizeof(数组名) = 一个指针所占字节数
else     sizeof(数组名) = 数组所占字节数


int *arr[6][3];                      //二维指针数组;  不等于数组指针 typedef int(*arr)[3] B;  B  b[6];
int e=(unsigned)&arr[5][0]-(unsigned)&arr[4][0]; //转换成数值后就不带类型信息了,整数加减运算
printf("%d\n",e);                  //12   3*sizeof(int *)=3*4
printf(“%d”,arr[5]);              //地址值与&arr[5][0]相同,0xbf97c27c,但arr[5]表示的是一个指向int(*)[3]的指针,
                                             &arr[5][0]表示的是第五行第一个元素的地址,两者地址相同,指向不同
printf(“%d”,arr[4]);              //地址值与&arr[4][0]相同,0xbf97c270,
printf(“%d\n”,arr[5]-arr[4]);  //3   带类型信息的加减, d=arr[5]-arr[4],d为 int *类型

arr表示arr[0][0]的地址,&arr表示以整个数组作为一个整体的数组指针,指向int(*)[6][3],&arr+1=&arr[19]


int a=1; &a此时应该是int* 类型
------------------



typedef

陷阱一:

记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如:
先定义:
typedef char* PSTR;
然后:
int mystrcmp(const PSTR, const PSTR);

const PSTR实际上相当于const char*吗?不是的,它实际上相当于char* const。
原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char* const。
简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。

陷阱二:

typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如:
typedef static int INT2; //不可行
编译将失败,会提示“指定了一个以上的存储类”。

陷进三:

通常讲,typedef要比#define要好,特别是在有指针的场合。请看例子:

typedef char *pStr1;

#define pStr2 char *;

pStr1 s1, s2;

pStr2 s3, s4;

在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字。

陷进四:

typedef char * pStr;

char string[4] = "abc";

const char *p1 = string;

const pStr p2 = string;

p1++;

p2++;

是p2++出错了。这个问题再一次提醒我们:typedef和#define不同,它不是简单的文本替换。上述代码中const pStr p2并不等于const char * p2。const pStr p2和const long x本质上没有区别,都是对变量进行只读限制,只不过此处变量p2的数据类型是我们自己定义的而不是系统固有类型而已。因此,const pStr p2的含义是:限定数据类型为char *的变量p2为只读,因此p2++错误。

### C/C++ 中二维数组指针二级指针的区别 #### 定义差异 在 C/C++ 编程语言中,二维数组指针和二级指针虽然都涉及双重间接寻址的概念,但是两者有着本质上的不同。对于二维数组指针而言,其本质上是指向特定维度大小已知的一维数组指针;而二级指针则是指向另一个指针变量的指针。 - **二维数组指针**:表示的是一个固定结构的数据集合访问方式,即该类型的指针知道它所指向的对象是由多少个相同类型元素构成的一个连续内存块的一部分[^4]。 - **二级指针**:则更加灵活多变,它可以用来动态管理多个独立分配或者关联起来使用的单个对象或子集[^1]。 #### 使用场景对比 当处理静态定义好的矩形形状数据表(如图像像素矩阵)时,通常会选择使用二维数组指针来简化操作逻辑并提高性能效率。因为在这种情况下,编译器能够利用额外的信息优化存储布局以及索引运算过程。 相反,在面对更为复杂的应用需求——比如链表节点之间的连接关系维护或是实现某种形式的稀疏表格映射机制,则更适合采用二级指针方案。这允许程序员构建出具有高度自适应特性的软件组件,即使是在运行期间也可能改变内部链接模式而不影响外部接口稳定性[^2]。 #### 实际编码示例分析 ##### 二维数组指针例子: 考虑如下代码片段展示了一个典型的二维数组及其对应的指针应用情形: ```cpp #include <iostream> using namespace std; void printMatrix(int (*matrix)[3], size_t rows){ for(size_t i=0 ;i<rows;++i){ for(size_t j=0;j<3;++j){ cout<<(*matrix)[j]<<" "; } ++matrix; cout<<"\n"; } } int main(){ int matrix[][3]={ {1,2,3}, {4,5,6} }; printMatrix(matrix,sizeof(matrix)/sizeof(matrix[0])); return 0; } ``` 上述 `printMatrix` 函数接受一个指向每行含有三个整型成员的一维数组指针作为参数,并通过调整此指针对整个二维空间进行遍历输出。 ##### 二级指针的例子: 下面给出一段关于如何运用二级指针创建可变长度列表项之间相互引用关系的小例子: ```c #include<stdio.h> typedef struct Node{ char data; struct Node *next; }*NodePtr,**ListHead; // 初始化头结点为空 void init(ListHead list){ *list=NULL; } // 插入新节点到头部位置 void insertFront(ListHead list,char value){ NodePtr newNode=(NodePtr)malloc(sizeof(struct Node)); if(!newNode)return ; newNode->data=value; newNode->next=*list; *list=newNode; } int main(){ ListHead myList=&(struct Node*){NULL}; init(myList); insertFront(myList,'A'); ... } ``` 这里定义了一种名为 `ListHead` 的二级指针类型用于追踪由一系列带有自我循环特性(`next`)字段组成的线性序列起点地址。每当新增加一个元素时,只需更新前驱单元中的相应部分即可完成整体结构调整工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值