关于指针和数组对申请内存malloc和作为参数传递的一些问题

本文通过四个试题探讨了C语言中指针、内存管理和变量生存期的问题。试题1展示了形参指针无法改变实参值,试题2揭示了返回局部变量地址的错误,试题3和4提醒我们检查内存分配是否成功并及时释放。最后,文章指出swap函数中的指针错误可能导致访问违规。

试题1.

 

试题2:

 

试题3:

 

 

试题4:

 

 

解答:

  试题1传入中GetMemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完

后的str仍然为NULL;

    试题2中

的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。

  试题3的GetMemory避免了试题1的问题,传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句

后未判断内存是否申请成功,应加上:

     试题4存在与试题3同样的问题,在执行

后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:



试题6的Test函数中也未对malloc的内存进行释放。

  剖析:

  试题1~4考查面试者对内存操作的理解程度,基本功扎实的面试者一般都能正确的回答其中50~60的错误。但是要完全解答正确,却也绝非易事。

  对内存操作的考查主要集中在:

  (1)指针的理解;

  (2)变量的生存期及作用范围;

  (3)良好的动态内存申请和释放习惯。

  再看看下面的一段程序有什么错误:

在swap函数中,p是一个“野”指针,有可能指向系统区,导致程序运行的崩溃。在VC++中DEBUG运行时提示错误“Access Violation”。该程序应该改为:




<think>我们首先需要理解二维数指针指针的定义区别,然后讨论它们在函数参数传递中的不同用法。 根据之前的讨论: - 二维数指针:指向整个二维数指针,类型为 `int (*p)[m][n]`,其中mn是数的维度。 - 行指针:指向二维数中某一行(即一个一维数)的指针,类型为 `int (*p)[n]`,其中n是列数。 在函数参数传递中,两者的主要区别在于传递的维度信息访问方式。 1. **二维数指针作为函数参数**: 当使用二维数指针时,函数内部知道整个数的维度(行数列数)。例如: ```c void func(int (*p)[2][3]) { // 必须指定行数列数 // 通过(*p)[i][j]访问元素 } int main() { int a[2][3] = {...}; func(&a); } ``` 优点:可以准确地知道数的维度,但要求数的行数列数在编译时已知,并且必须与函数参数中指定的维度一致。 2. **行指针作为函数参数**: 行指针传递的是二维数的第一行的地址(即一个指向一维数指针),函数内部只需要知道列数(即一维数的长度)。例如: ```c void func(int p[][3], int rows) { // 或者 void func(int (*p)[3], int rows) // 通过p[i][j]访问元素 } int main() { int a[2][3] = {...}; func(a, 2); // 传递数名a,它自动转换为指向第一行的指针(即行指针) } ``` 注意:数名`a`在传递给函数时会退化为指向第一行的指针(类型为`int(*)[3]`)。函数中需要额外传递行数(因为行指针类型中只包含列数信息)。 关键区别总结: - **类型不同**:二维数指针类型包含整个二维数的维度(行列),而行指针只包含列数。 - **传递方式不同**:二维数指针需要取整个数的地址(`&a`),而行指针直接传递数名(`a`)。 - **函数内部访问**:二维数指针需要解引用一次(`(*p)[i][j]`),而行指针可以直接使用`p[i][j]`(因为`p`是指向一维数指针,可以像数名一样使用)。 - **灵活性**:行指针更常用,因为它可以处理行数可变的情况(通过额外传递行数),而二维数指针要求行数列数固定。 另外,根据引用[4]中的说明:在函数中传递二维数,必须指定列数(因为需要知道每一行的长度,以便计算偏移)。行指针方式(即`int p[][3]`或`int (*p)[3]`)是更常见的做法。 示例对比: 使用二维数指针: ```c #include <stdio.h> void print_with_array_pointer(int (*p)[2][3]) { for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { printf("%d ", (*p)[i][j]); } printf("\n"); } } ``` 使用行指针: ```c void print_with_row_pointer(int p[][3], int rows) { for (int i = 0; i < rows; i++) { for (int j = 0; j < 3; j++) { printf("%d ", p[i][j]); } printf("\n"); } } ``` 在main函数中调用: ```c int main() { int a[2][3] = {{1,2,3},{4,5,6}}; print_with_array_pointer(&a); print_with_row_pointer(a, 2); return 0; } ``` 注意:行指针方式中,我们可以改变行数(通过参数rows),而二维数指针方式中行数固定为2。 因此,在函数参数传递中,行指针更灵活,适用于列数固定但行数可变的场景;而二维数指针则适用于整个数维度固定的情况,但相对较少使用。 引用说明: 引用[4]中提到:形参的COL是必需的,对二维数来说,每一行是一个一维数。要找到某个特定行中的元素,编译器必须准确地知道每一行有多少个元素。这解释了为什么行指针方式中必须指定列数(即一维数的长度)。 引用[3]中关于二维数元素地址的表示(如a[i]+j, *(a+i)+j)也支持行指针的访问方式。 引用[1]中则强调:只有把二维数改成一个指向向量的指针的前提下,才能用二级指针(char**)传递。注意,我们这里讨论的是二维数(连续存储)指针(每个元素是指针)是不同的。在传递二维数时,我们通常使用行指针,而不是二级指针(除非是指针)。 所以,在函数参数传递中,二维数指针与行指针的主要区别在于传递的维度信息访问方式,行指针更常用且灵活。 接下来,按照要求生成相关问题: </think> ### 二维数指针与行指针在函数参数传递中的区别 在C语言中,二维数指针(如 `$int (*p)[m][n]$`)指针(如 `$int (*p)[n]$`)在函数参数传递时存在显著差异,主要体现在**类型定义**、**内存模型****访问方式**上: #### 1. **类型定义与参数声明** | **类型** | **定义示例** | **参数传递示例** | |-------------------|--------------------------|----------------------------------| | **二维数指针** | `$int (*ptr)[2][3]$` | `$void func(int (*p)[2][3])$` | | **行指针** | `$int (*ptr)[3]$` | `$void func(int (*p)[3], int rows)$` | - **二维数指针**: 需完整指定行数($m$)列数($n$),如 `$[2][3]$`。传递时**必须取整个数地址**(`$&a$`)[^4]: ```c int a[2][3] = {{1,2,3}, {4,5,6}}; func(&a); // 正确:传递整个数地址 ``` - **行指针**: 只需指定列数($n$),如 `$[3]$`。传递时**直接使用数名**(`$a$`),因其自动退化为指向首行的指针[^4]: ```c func(a, 2); // 正确:a退化为行指针 ``` #### 2. **内存访问方式** | **类型** | **元素访问方式** | **偏移计算** | |-------------------|--------------------------|----------------------------------| | **二维数指针** | `$(*p)[i][j]$` | 需先解引用整个数 | | **行指针** | `$p[i][j]$` 或 `$*(*(p+i)+j)$` | 直接按行偏移 | - **二维数指针**: 需先解引用获取数本身,再按行列访问: ```c void func(int (*p)[2][3]) { printf("%d", (*p)[1][2]); // 输出6 } ``` - **行指针**: 可直接用二维语法访问,编译器自动计算偏移: ```c void func(int (*p)[3], int rows) { printf("%d", p[1][2]); // 输出6 } ``` #### 3. **灵活性对比** | **特性** | **二维数指针** | **行指针** | |-------------------|--------------------------|----------------------------------| | **行数动态性** | 固定(需编译时确定$m$) | 动态(通过参数传递$rows$) | | **列数要求** | 固定(需指定$n$) | 固定(需指定$n$) | | **适用场景** | 维度固定的静态数 | 行数可变或动态分配的二维数 | - **行指针更灵活**: 可处理动态行数(如 `$malloc$` 分配的数),只需额外传递行数参数: ```c void print_matrix(int (*p)[3], int rows) { for (int i = 0; i < rows; i++) { for (int j = 0; j < 3; j++) { printf("%d ", p[i][j]); } } } ``` #### 4. **与二级指针的混淆** - **错误用法**: 二维数**不能**直接转换为二级指针(`$int**$`),因其内存布局不同[^1]: ```c // 错误示例:类型不匹配 void test(char **ptr); char a[2][3] = {"ab", "cd"}; test(a); // 编译错误! ``` - **正确替代**: 若需二级指针语义,应使用**指针**(非连续存储): ```c char *arr[] = {"ab", "cd"}; // 指针 test(arr); // 正确:arr为char*[]类型,退化为char** ``` ### 关键区别总结 | **维度** | **二维数指针** | **行指针** | |----------------|---------------------------------|---------------------------| | **类型语义** | 指向整个二维数 | 指向单一行(一维数) | | **参数传递** | 必须用 `$&a$` | 直接用数名 `$a$` | | **元素访问** | `$(*p)[i][j]$` | `$p[i][j]$` | | **灵活性** | 低(维度完全固定) | 高(支持动态行数) | > 实际开发中,**行指针**(`$int (*p)[n]$`)因其灵活性简洁性更常用,尤其在处理动态行数或函数参数传递时。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值