数组做形参退化为指针

如果数组作为函数参数,则数组形参会退化为指针,以下代码在编译器看来是等价的

  1. void fun1(int a[]);
  2. void fun2(int a[]);
  3. void fun3(int a[]);
  4. void fun4(int *a);
  1. #include<iostream>
  2. using namespace std;
  3. void fun1(int a[])
  4. {
  5. cout << sizeof(a) / sizeof(a[]) << endl;
  6. }
  7. void fun2(int a[])
  8. {
  9. cout << sizeof(a) / sizeof(a[]) << endl;
  10. }
  11. void fun3(int a[])
  12. {
  13. cout << sizeof(a) / sizeof(a[]) << endl;
  14. }
  15. void fun4(int *a)
  16. {
  17. cout << sizeof(a) / sizeof(a[])<<endl;
  18. }
  19. void main(int argc, char* argv[])
  20. {
  21. int arr[] = {,,,,,,,,,};
  22. fun1(arr);
  23. fun2(arr);
  24. fun3(arr);
  25. fun4(arr);
  26. cout << sizeof(arr) / sizeof(arr[]) << endl;
  27. system("pause");
  28. }

数组

  1. #include<stdio.h>
  2.  
  3. void main(int argc, char* argv[])
  4. {
  5. int arr[] = {};
  6. printf("arr=%d, &arr=%d\n", arr, &arr);
  7. printf("arr+1=%d, &arr+1=%d\n",arr+, &arr+);
  8. system("pause");
  9. }

//arr,&arr的数组类型不一样
//arr,数组首元素地址,一个元素4字节,+1,+4
//&arr,整个数组的首地址,一个数组4*10=40字节,+1,+40

char* argv[]和char** argv

  1. void main(int argc, char* argv[])
  2. void main(int argc, char** argv)

main函数写成上面2种形式都行,于是有人自然会认为char* argv[]与char** 等价。char* argv[]与char**本质上是不同的,char* argv[]是素组,你可以这样

  1. char* argv[] = {"hello","world","Linux","NB"}

但是char**就不行,下面代码是语法错误

  1. char** argv = {"hello","world","Linux","NB"}

指针只能指向一块内存,{"hello","world","Linux","NB"}是4块内存。数组可以指向多块内存。

那为啥在main函数中,2者就等价了呢? 因为数组作为形参,会自动退化为指针。

为啥void a不行,void* a就行

对于出void以外的内置类型,自定义类型。我们可以

  1. typename var;
  2. typename* p_var;

但是唯独void不行

  1. void a; //语法错误
  2. void* p; //OK

C/C++中类型就像模具,其本身不占用内存,根据模具产生的对象占内存。void a,单纯这一句,编译器无法计算出a到底占用多少内存。void*  p就行,这是因为在32位架构下,指针都是4Byte。

二维数组首行地址,和首行首元素地址的值是一样

  1. #include<stdio.h>
  2.  
  3. void main(int argc, char* argv[])
  4. {
  5. int i;
  6. char* str1[] = {"","",""};
  7. char str2[][] = { "","","" };
  8. printf("str1=%x, *str1=%x, &(**str1)=%x\n", str1, *str1, &(**str1));
  9. printf("str2=%x, *str2=%x\n", str2, *str2);
  10. for (i = ; i < sizeof(str2) / sizeof(str2[]); i++)
  11. {
  12. //printf("%s\n",str2+i); 与下一行输出内容一样
  13. printf("%s\n", *(str2 + i));//*(str2 + i)等价于str2[i]
    }
    } system("pause"); }

二级指针的内存模型

  1. #include <string.h>
  2. #pragma warning(disable:4996) // 等价于_CRT_SECURE_NO_WARNINGS
  3. int main(int argc, char* argv[])
  4. {
  5. int i = ;
  6. char* p0 = NULL;
  7. p0 = (char*)malloc(sizeof(char)*);
  8. strcpy(p0, "Hello World");
  9.  
  10. //3个char* ,每个的值都是空
  11. char* ch_arr[] = { };
  12. for ( i = ; i < sizeof(ch_arr)/sizeof(ch_arr[]); i++)
  13. {
  14. ch_arr[i] = (char*)malloc(sizeof(char)*);
  15. strcpy(ch_arr[i], "Hello World");
  16. }
  17.  
  18. int arr[];//静态分配
  19. int *p_arr = (int*)malloc(sizeof(int) * );//等价于arr[10],动态分配
  20.  
  21. char* str_arr[] = { };
  22. char** p_str_arr = (char**)malloc(sizeof(char*) * );
  23. strcpy(p_str_arr[], "Hello World");
  24. }

最后一句会报错

  1. strcpy(p_str_arr[], "Hello World");

出错原因在于向未分配内存的地址拷贝数据,这和7,8行道理一样。

改为如下则不会有问题

  1. char* str_arr[] = { };
  2. char** p_str_arr = (char**)malloc(sizeof(char*) * );
  3. //strcpy(p_str_arr[0], "Hello World");
  4. for ( i = ; i < ; i++)
  5. {
  6. p_str_arr[i] = (char*)malloc(sizeof(char) * );
  7. strcpy(p_str_arr[i], "Hello World");
  8. }

此时内存模型

从上图可见,heap实际上分配了2大块,于是释放内存步骤为:

先释放那3个100B,再释放3个4B

  1. for ( i = ; i < ; i++)
  2. {
  3. free(p_str_arr[i]);
  4. p_str_arr[i] = NULL;
  5. }
  6. if (NULL != p_str_arr)
  7. {
  8. free(p_str_arr);
  9. }

如果是函数返回二级指针的情况呢,看如下代码

  1. #include <string.h>
  2. #pragma warning(disable:4996) // 等价于_CRT_SECURE_NO_WARNINGS
  3.  
  4. char** getBuf(int n)
  5. {
  6. int i;
  7. char** buf = (char**)malloc(sizeof(char*) * );
  8. //strcpy(buf[0], "Hello World");错误写法
  9. for (i = ; i < ; i++)
  10. {
  11. buf[i] = (char*)malloc(sizeof(char) * );
  12. strcpy(buf[i], "Hello World");
  13. }
  14. return buf;
  15. }
  16. void Free(char** buf, int n)
  17. {
  18. int i;
  19. for (i = ; i < n; i++)
  20. {
  21. free(buf[i]);
  22. buf[i] = NULL;
  23. }
  24. if (NULL != buf)
  25. {
  26. free(buf);
  27. }
  28. }
  29. int main(int argc, char* argv[])
  30. {
  31. int n = ;
  32. int i;
  33. char** buf = getBuf(n);
  34. Free(buf, );
    buf = NULL;
  35. }

再说释放内存,为啥Free后还要让buff = NULL

  1. Free(buf, );
  2. buf = NULL;

C++——数组形参退化为指针的更多相关文章

  1. C语言 数组名不是指针

    今天上计算机系统课的时候老师讲到了C中的聚合类型的数据结构.在解释数组名的时候说"数组名是一个指针,指向该数组的第一个元素",附上ppt(第二行): 我觉得这是不正确的,是一个常见 ...

  2. CC++语法::数组名退化(array decaying)

    参考: CSDN::C/C++中数组名退化为指针的情况 stackoverflow::What is array decaying? 起因 笔者在写memset的时候总想偷一点懒(因为我们一般都是为了 ...

  3. C语言中,数组名作为参数传递给函数时,退化为指针

    C语言中,数组名作为参数传递给函数时,退化为指针   C语言中,数组名作为参数传递给函数时,退化为指针:需要数组大小时, 需要一个参数传数组名,另一个传数组大小. 数组名做函数参数时,就相当于指针了. ...

  4. C语言 数组做函数参数退化为指针的技术推演

    //数组做函数参数退化为指针的技术推演 #include<stdio.h> #include<stdlib.h> #include<string.h> //一维数组 ...

  5. c 数组做为形參时 该參数退化为指针

    当数组做为函数的形參的时候,该參数退化为指针,而且是无法直接求得数组的大小. 传数组给一个函数.数组类型自己主动转换为指针类型,因而传的实际是地址. void func(int array[10]) ...

  6. sizeof(数组名)和sizeof(指针)

    在做这道题时: 32位环境下,int *p=new int[10];请问sizeof(p)的值为()A.4              B.10              C.40           ...

  7. Linux C\C++基础——数组形参的使用

    1.数组形参 ]) void fun(int a[]) void fun(int *a) ],int n) void fun(char*p[],int n) void fun(char**p,int ...

  8. c语言 数组名是常量指针

    //数组名是常量指针 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include ...

  9. C语言 指针基础篇 数组,函数与指针的运用 2 14

    下面看看如何在函数中运用指针吧 下面是往函数传入指针的简单操作,不是传入数组的.判断一个a是否大于b是的话给,是的话对其进行操作,不是的话就直接返回. #include <stdio.h> ...

随机推荐

  1. Swift学习 (二)

    2.控制流: 主要有三种类型的语句 if,switch和新增的guard for,while break,continue 关于if 语句里的条件不再需要使用()包裹了. 1 2 3 4 let nu ...

  2. C#关于DateTime得到的当前时间的格式和用法

    DateTime.Now.ToShortTimeString() DateTime dt = DateTime.Now; dt.ToString();//2005-11-5 13:21:25 dt.T ...

  3. sqlserver 数据库操作导出数据sql工具

    软件名称sqldbx 下载URL  https://download.csdn.net/download/yanghl1998/7832861 Navicat Premium  这个工具任何类型数据库 ...

  4. Spring 分布式事务详解

    在学习分布式事务的过程中会遇到以下关键名词: 相关名词: XA :XA规范的目的是允许多个资源(如数据库,应用服务器,消息队列,等等)在同一事务中访问,这样可以使ACID属性跨越应用程序而保持有效.X ...

  5. 如何抓住ECS的命门,让我们的学习事半功倍

    导读 这是一篇老文写与2019年5月 我们说如何提高我们的学习效率,有人说一本书一般只会讲一个知识点,那我们学习ECS 如何抓住学习的重点,提高学习效率.经过本人一段时间的学习总结,总于找到了一个便捷 ...

  6. Egret入门学习日记 --- 第十二篇(书中 5.1节 内容)

    第十二篇(书中 5.1节 内容) 昨天把 第4章完成了. 今天来看第5章. 接下来是 5.1节 的内容. 总结一下 5.1节 的重点: 1.如何制作一个公用按钮皮肤. 跟着做: 重点1:如何制作一个公 ...

  7. Python爬取链家二手房源信息

    爬取链家网站二手房房源信息,第一次做,仅供参考,要用scrapy.   import scrapy,pypinyin,requests import bs4 from ..items import L ...

  8. 解决 OpenCV with CUDA 编译提示缺少 nvcuvid.h 的问题

    系统环境: 操作系统:Ubuntu 18.04.01 显卡型号:GeForce GTX 1060 6G CMake 版本:3.10.2 GCC 版本:7.4.0 GNU Make 版本:4.1 CUD ...

  9. 向量空间模型(Vector Space Model)

    搜索结果排序是搜索引擎最核心的构成部分,很大程度上决定了搜索引擎的质量好坏.虽然搜索引擎在实际结果排序时考虑了上百个相关因子,但最重要的因素还是用户查询与网页内容的相关性.(ps:百度最臭名朝著的“竞 ...

  10. Ant 构建 Jmeter脚本报错详解

    在搭建Ant构建Jmeter脚本的时候,小组成员遇到了各种问题. 再这里总结一下,遇到类似问题的可以做个参考 1.提示 does not exist 解决方案: 出现这种的问题原因有很多. 先排除权限 ...