数组地址和指针变量地址

在项目测试中,误用了数组地址(当然也没错),后面依葫芦画瓢,使用了指针,最后造成了程序崩溃,所以记录下来;

首先是测试结果没问题的代码:

#include <iostream>
#include <fstream>
using namespace std;
float arrSig[16000];
int main()
{
    ifstream input("data.dat",ios::in|ios::binary);
    input.read((char*)&arrSig,16000*sizeof(float));
    std::cout<<arrSig[0]<<std::endl;
    input.close();
    return 0;
}

因为数组大小不固定,所以使用指针动态申请内存

​
#include <iostream>
#include <fstream>
using namespace std;
float* arrSig;
int main()
{   
    arrSig=new float[16000];
    ifstream input("data.dat",ios::in|ios::binary);
    input.read((char*)&arrSig,16000*sizeof(float));
    std::cout<<arrSig[0]<<std::endl;
    input.close();
    return 0;
}

​

程序直接崩溃;在印象中数组名也是代表地址的,既然第一个没问题,为什么第二个崩溃了?

 原来&数组名代表数组的地址,而数组名代表的是数组首元素的地址,虽然概念变了,但是值是一样的,因为申请的地址是连续的,所以赋值时空间是足够的。而&指针名没有这个意思,&指针名代表的是指针变量的地址(“指针”也是一种变量,之所以叫他指针,完全是因为它的值是内存地址。既然指针是变量,那么变量也是需要存储空间的),与指针的地址是不一样的,当赋值大小超过存放指针变量地址的大小时,程序就会崩溃(一个网友认为指针的本质:指针的原理:当我们申请堆空间时,系统就会有一个标志来告诉我们,这些堆内存是没有人用的,给你用了。当你使用delete时,就是告诉系统那些堆内存我不用了,你拿去给别人用吧,这是时候,你之前写的东西还是在那里的,而且这个指针还是指向堆内存,而且那个堆内存里的东西,如果系统一直没用使用的话,它的值会一直存在(https://blog.youkuaiyun.com/weixin_43277501/article/details/101568141))

正确的用法:

​
#include <iostream>
#include <fstream>
using namespace std;
float* arrSig;
int main()
{   
    arrSig=new float[16000];
    ifstream input("data.dat",ios::in|ios::binary);
    input.read((char*)arrSig,16000*sizeof(float));
    std::cout<<arrSig[0]<<std::endl;
    input.close();
    return 0;
}

​

参考链接:https://blog.youkuaiyun.com/Raven_csdn/article/details/87874679 

<think>好的,我现在需要回答用户关于“数组指针变量是相互等价的”这个问题。用户之前问过关于a[i] = getchar()的问题,现在可能在学习C语言的数组指针的关系。我要确保回答准确,同时结构清晰,符合之前设定的系统级指令。 首先,我得回忆C语言中数组指针的关系。记得数组名在大多数情况下会被隐式转换为指向其首元素的指针,但数组名本身并不是指针变量。例如,声明int arr[10]; 后,arr的类型是int[10],但在表达式中,arr会退化为int*,指向arr[0]。 然后需要明确什么时候数组名不等价于指针变量。比如,sizeof(arr)会得到整个数组的大小,而如果是指针变量,sizeof(ptr)得到的是指针的大小。此外,&arr的类型是指向数组指针,而&ptr则是指向指针指针。这些区别需要说明。 接下来,要处理用户可能的误解。用户可能听说数组指针等价,但实际应用中存在差异。我需要举例说明它们的使用场景限制。比如,数组名不能作为左值被重新赋值,而指针变量可以。例如,ptr = new_address; 是合法的,但arr = new_address; 会报错。 另外,函数参数中的数组会被视为指针。例如,void func(int arr[])实际上等同于void func(int *arr)。这点需要提到,因为这可能是用户认为它们等价的原因之一。 还需要注意内存分配的不同。数组名对应的是连续内存块,而指针变量可能指向动态分配的内存或另一个数组。例如,指针可以通过malloc分配内存,而数组名在声明时已经固定了内存地址。 用户可能遇到的问题包括:误用数组名为指针进行自增操作,或者不理解为什么不能对数组名赋值。我需要用例子说明这些情况,并指出正确的做法。 总结时,要强调数组指针变量在大多数使用场景中可以互换,但存在本质区别,包括类型、赋值能力、sizeof结果、以及取地址操作的不同。需要提醒用户注意这些差异,避免程序中的潜在错误。</think>让我们深入分析“数组指针变量是否相互等价”这一问题,逐步揭示它们的异同: ### 一、**表象上的等价性** #### 1. **访问元素的语法相同** ```c int arr[5] = {1,2,3}; int *ptr = arr; printf("%d\n", arr[2]); // 输出 3 printf("%d\n", ptr[2]); // 输出 3 ``` #### 2. **函数传参时的等价** 函数参数中的数组声明会被自动转换为指针: ```c void func(int a[]); // 实际等价于 void func(int *a) ``` #### 3. **算术运算的相似性** ```c printf("%p\n", arr + 1); // 输出第二个元素地址 printf("%p\n", ptr + 1); // 同上 ``` --- ### 二、**本质区别** #### 1. **类型系统的根本差异** | 对象 | 类型 | 内存分配方式 | |-------------|--------------------|---------------------| | `int arr[5]` | `int[5]` (数组类型) | 静态/栈内存连续分配 | | `int *ptr` | `int*` (指针类型) | 动态分配或指向现有内存 | #### 2. **sizeof 运算的差异** ```c printf("%zu\n", sizeof(arr)); // 输出 20 (假设 int 占4字节) printf("%zu\n", sizeof(ptr)); // 输出 8 (64位系统指针大小) ``` #### 3. **赋值能力的区别** ```c ptr = arr; // 合法:指针可重新赋值 arr = ptr; // 非法:数组名不可作为左值 ptr++; // 合法:指针可自增 arr++; // 非法:数组名不可自增 ``` #### 4. **取地址操作的差异** ```c int (*parr)[5] = &arr; // 类型是 int(*)[5] int **pptr = &ptr; // 类型是 int** ``` --- ### 三、**关键差异对照表** | 特性 | 数组名 | 指针变量 | |---------------------|--------------------------|--------------------------| | 类型 | `T[N]` | `T*` | | 存储空间 | 自动分配连续内存 | 需单独分配内存地址 | | sizeof 结果 | 返回整个数组字节大小 | 返回指针变量的大小 | | 可修改性 | 不可被重新赋值 | 可以指向不同地址 | | & 操作符的结果类型 | 返回 `T(*)[N]` 类型 | 返回 `T**` 类型 | | 生命周期管理 | 自动销毁(栈数组) | 需手动释放(动态分配时) | --- ### 四、**典型错误场景** #### 错误1:误用数组名为指针 ```c int arr[5]; arr = (int[]){2,3,4}; // 错误!数组名不可作为左值 ``` #### 错误2:错误计算元素个数 ```c int *ptr = malloc(sizeof(int)*5); size_t count = sizeof(ptr)/sizeof(int); // 错误!结果为2(64位系统) ``` #### 错误3:错误传递二维数组 ```c void func(int **arr); // 错误声明方式 int arr[3][4]; func(arr); // 类型不匹配:实际类型是 int(*)[4] ``` --- ### 五、**正确使用建议** #### 1. 动态数组的正确处理 ```c int (*dyn_arr)[10] = malloc(sizeof(int[10])); // 正确分配二维数组 free(dyn_arr); ``` #### 2. 数组衰减规则的应用 ```c void print_array(int *arr, size_t len) { for (int i = 0; i < len; i++) { printf("%d ", arr[i]); } } int main() { int arr[] = {1,2,3,4,5}; print_array(arr, sizeof(arr)/sizeof(int)); // 正确利用数组衰减 } ``` #### 3. 类型安全的参数传递 ```c // 处理二维数组的正确方式 void matrix_op(int (*mat)[4], int rows) { // 可安全访问 mat[i][j] } int main() { int mat[3][4]; matrix_op(mat, 3); } ``` --- ### 六、**底层原理** 1. **符号表差异** 编译器在处理数组名时,会将其记录为**固定地址符号**;而指针变量是**存储地址值的变量**。 2. **机器码生成** - 数组访问:直接计算偏移地址 `基地址 + 偏移量` - 指针访问:需要先读取指针值,再加偏移量 3. **类型检查机制** ```c int arr[5][4]; int (*p1)[4] = arr; // 合法 int **p2 = arr; // 类型不兼容警告 ``` --- ### 总结 数组指针变量的核心区别: ✅ **数组名是地址常量**,包含完整的类型信息(元素类型+维度) ✅ **指针变量是存储地址变量**,仅包含目标类型信息 虽然它们在使用上有相似性,但理解这些差异对于避免内存错误、编写高效代码至关重要。在实际编程中,应始终注意: 1. 数组不可重新赋值 2. 注意sizeof的语义差异 3. 正确处理多维数组类型
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值