C++——数组形参退化为指针
数组做形参退化为指针
如果数组作为函数参数,则数组形参会退化为指针,以下代码在编译器看来是等价的
void fun1(int a[100]); void fun2(int a[1]); void fun3(int a[]); void fun4(int *a);
#include<iostream> using namespace std; void fun1(int a[100]) { cout << sizeof(a) / sizeof(a[0]) << endl; } void fun2(int a[1]) { cout << sizeof(a) / sizeof(a[0]) << endl; } void fun3(int a[]) { cout << sizeof(a) / sizeof(a[0]) << endl; } void fun4(int *a) { cout << sizeof(a) / sizeof(a[0])<<endl; } void main(int argc, char* argv[]) { int arr[] = {10,9,8,7,6,5,4,3,2,1}; fun1(arr); fun2(arr); fun3(arr); fun4(arr); cout << sizeof(arr) / sizeof(arr[0]) << endl; system("pause"); }
数组
#include<stdio.h> void main(int argc, char* argv[]) { int arr[10] = {0}; printf("arr=%d, &arr=%d\n", arr, &arr); printf("arr+1=%d, &arr+1=%d\n",arr+1, &arr+1); system("pause"); }
//arr,&arr的数组类型不一样
//arr,数组首元素地址,一个元素4字节,+1,+4
//&arr,整个数组的首地址,一个数组4*10=40字节,+1,+40
char* argv[]和char** argv
void main(int argc, char* argv[]) void main(int argc, char** argv)
main函数写成上面2种形式都行,于是有人自然会认为char* argv[]与char** 等价。char* argv[]与char**本质上是不同的,char* argv[]是素组,你可以这样
char* argv[] = {"hello","world","Linux","NB"}
但是char**就不行,下面代码是语法错误
char** argv = {"hello","world","Linux","NB"}
指针只能指向一块内存,{"hello","world","Linux","NB"}是4块内存。数组可以指向多块内存。
那为啥在main函数中,2者就等价了呢? 因为数组作为形参,会自动退化为指针。
为啥void a不行,void* a就行
对于出void以外的内置类型,自定义类型。我们可以
typename var; typename* p_var;
但是唯独void不行
void a; //语法错误 void* p; //OK
C/C++中类型就像模具,其本身不占用内存,根据模具产生的对象占内存。void a,单纯这一句,编译器无法计算出a到底占用多少内存。void* p就行,这是因为在32位架构下,指针都是4Byte。
二维数组首行地址,和首行首元素地址的值是一样
#include<stdio.h> void main(int argc, char* argv[]) { int i; char* str1[3] = {"111","222","333"}; char str2[3][3] = { "444","555","666" }; printf("str1=%x, *str1=%x, &(**str1)=%x\n", str1, *str1, &(**str1)); printf("str2=%x, *str2=%x\n", str2, *str2); for (i = 0; i < sizeof(str2) / sizeof(str2[0]); i++) { //printf("%s\n",str2+i); 与下一行输出内容一样 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 = 0; 6 char* p0 = NULL; 7 p0 = (char*)malloc(sizeof(char)*100); 8 strcpy(p0, "Hello World"); 9 10 //3个char* ,每个的值都是空 11 char* ch_arr[3] = { 0 }; 12 for ( i = 0; i < sizeof(ch_arr)/sizeof(ch_arr[0]); i++) 13 { 14 ch_arr[i] = (char*)malloc(sizeof(char)*100); 15 strcpy(ch_arr[i], "Hello World"); 16 } 17 18 int arr[10];//静态分配 19 int *p_arr = (int*)malloc(sizeof(int) * 10);//等价于arr[10],动态分配 20 21 char* str_arr[3] = { 0 }; 22 char** p_str_arr = (char**)malloc(sizeof(char*) * 3); 23 strcpy(p_str_arr[0], "Hello World"); 24 }
最后一句会报错
strcpy(p_str_arr[0], "Hello World");
出错原因在于向未分配内存的地址拷贝数据,这和7,8行道理一样。
改为如下则不会有问题
char* str_arr[3] = { 0 }; char** p_str_arr = (char**)malloc(sizeof(char*) * 3); //strcpy(p_str_arr[0], "Hello World"); for ( i = 0; i < 3; i++) { p_str_arr[i] = (char*)malloc(sizeof(char) * 100); strcpy(p_str_arr[i], "Hello World"); }
此时内存模型
从上图可见,heap实际上分配了2大块,于是释放内存步骤为:
先释放那3个100B,再释放3个4B
for ( i = 0; i < 3; i++) { free(p_str_arr[i]); p_str_arr[i] = NULL; } if (NULL != p_str_arr) { free(p_str_arr); }
如果是函数返回二级指针的情况呢,看如下代码
#include <string.h> #pragma warning(disable:4996) // 等价于_CRT_SECURE_NO_WARNINGS char** getBuf(int n) { int i; char** buf = (char**)malloc(sizeof(char*) * 3); //strcpy(buf[0], "Hello World");错误写法 for (i = 0; i < 3; i++) { buf[i] = (char*)malloc(sizeof(char) * 100); strcpy(buf[i], "Hello World"); } return buf; } void Free(char** buf, int n) { int i; for (i = 0; i < n; i++) { free(buf[i]); buf[i] = NULL; } if (NULL != buf) { free(buf); } } int main(int argc, char* argv[]) { int n = 3; int i; char** buf = getBuf(n); Free(buf, 3);
buf = NULL; }
再说释放内存,为啥Free后还要让buff = NULL
Free(buf, 3); buf = NULL;