https://stackoverflow.com/questions/859634/c-pointer-to-array-array-of-pointers-disambiguation
https://en.wikipedia.org/wiki/Operators_in_C_and_C++
http://unixwiz.net/techtips/reading-cdecl.html
https://cdecl.org/
https://stackoverflow.com/questions/2672085/static-array-vs-dynamic-array-in-c
https://blog.csdn.net/jin13277480598/article/details/51891816
https://blog.csdn.net/oNever_say_love/article/details/49422517
http://blog.sina.com.cn/s/blog_5fd837410100my5x.html
https://blog.csdn.net/bendanban/article/details/51244358
https://blog.csdn.net/xingerr/article/details/72417057
https://blog.csdn.net/xingerr/article/details/72372563
1、
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
int *(arr3[8]); // An array of int pointers.
2、
遍历数组,使用sizeof计算数组长度,之后遍历
#include <iostream>
int main()
{
int arr[] = {1, 2, 3, 4, 5};
int length = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < length; ++i)
{
arr[i] += 3;
std::cout << arr[i] << std::endl;
}
return 0;
}
3、
std::vector 使用 array 初始化
#include <vector>
#include <string>
struct KeyPair
{
int nNum;
std::string str;
};
void fetchKeys(const std::vector<KeyPair>& keys)
{
//Go through elements of the vector
std::vector<KeyPair>::const_iterator iter = keys.begin();
for(; iter != keys.end(); ++iter)
{
const KeyPair& pair = *iter;
//...
}
}
int main()
{
KeyPair keys[] = { {0, "tester"}, {2, "yadah"}, {0, "tester"} };
//Create a vector out of the array you are having
std::vector<KeyPair> coll(keys, keys + sizeof(keys) / sizeof(keys[0]));
//Pass this vector to the function. This is safe as vector knows
//how many element it contains
fetchKeys(coll);
return 0;
}
4、
1、数组作为函数参数
C语言中,数组做为函数的参数,退化为指针。数组作为参数传给函数时,传的是指针而不是数组,传递的是数组的首元素的地址。这里我们以将以整形变量排序来讲解。
void sortArray ( int a[ ] , int num ) 以及void sortArray (int a[100] ,int num ) 都可以用void sortArray (int *a ,int num ) 表示。一般来说函数参数如果为数组,可以有两个参数,一个是数组名,一个是数组长度。对于排序而已,一般是要知道给定数组的首元素的地址,即需要排序的数组在内存中的起始地址,同时还需给定待排序的数据个数。
这里我们采用简单的冒泡排序,模块代码如下:
[cpp] view plain copy
/*2016-7-12 Jason Gel */
void sortArray(int *a ,int num )
{
int i ,j ,temp;
for(i =0 ;i <num;i++) //外层:每次选定出需要排序的一个元素,依次向后
{
for( j=i+1; j<num; j++ ) //内层:外层选定的一个元素与其后所有元素依次比较,找出最小的元素
{
if(a[i]>a[j]) //交换类代码
{
temp = a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
}
2、参数传递
如果a是一个数组,很显然大家都知道如何传递参数,代码如下:
[cpp] view plain copy
int num =0 ; //在32机器中告诉C编译器分配4个字节的内存
int a [] = {1,3,5,12,6,7,54,32}; //告诉C编译器分配32个字节的内存
// num = sizeof(a)/sizeof(int);
num = sizeof(a)/sizeof(a[0]);
printf("排序之前:");
printArray(a,num);
sortArray(a,num);
printf("排序之后:");
printArray(a,num);
如果a是一个单一整形变量的地址,还能类似于用 num = sizeof(a)/sizeof(a[0]) 求得num的值吗?如以下代码:
[cpp] view plain copy
int test =5;
int *p = &test;
int num1 = 0;
num1 = sizeof(p)/sizeof(p[0]);
sortArray(p,num1);
printf("排序之后:");
printArray(p,num1);
这样可以吗?很多人犯迷糊了,p[0]不是指数组首元素的值吗,这里p不是数组啊,p[0]都好像不存在,怎么能这样用?我们一定还记得,在C语言中可以用指针表示数组的每一个元素,本质上,对同一个对象有两种不同的符号表示,如定义:a[n]时等价于*(a+n),即寻址到内存的a,然后移动n个单元,再取出数组。故p[0]等价于*(p+0),该值的类型为int型。
num1=sizeof(p)/sizeof(p[0]) 等价于 num1=sizeof(p)/sizeof(int );
该语句在语法上面没有任何错误,但是在32机器和64位机器运行结果不同,在32机器出现正常结果,64位机器出现错误结果,原因见本文最后。
3、声明数组参量
前提:实际参数是一个数组名。C对于int a [ ] 和 int * a作了同样解释,即a是指向int的指针。
由于原型允许省略名称,因此下列4种原型都是等价的。
[cpp] view plain copy
/** 函数原型声明4中等价形式
int sum (int *a , int n)
int sum (int * , int )
int sum (int a[] , int n)
int sum (int [] , int ) //可能略微不熟悉的一种
*/
定义函数时,名称是不可以省略的,以下两种形式是等价的。
[cpp] view plain copy
/** 函数原型声明4中等价形式
int sum (int *a , int n)
{}
int sum (int a[] , int n)
{}
*/
4、a与&a的区别
[cpp] view plain copy
int num =0 ; //在32机器中告诉C编译器分配4个字节的内存
int a [] = {1,3,5,12,6,7,54,32}; //告诉C编译器分配32个字节的内存
printf("a:%d ,a+1:%d,&a:%d,&a+1:%d
",a,a+1,&a,&a+1) ;
//a+1 和 &a+1 结果不一样
//虽然输出结果上面,a和&a一样 。 但是a 和 &a所代表的数据类型不一样
/*重要*/
//a 代表的数据首元素的地址 (首元素),同时与整个数组地址重合,但其不能代表整个数组,只能代表起始个体的地址
//&a代表的是整个数组的地址 (特别特别的注意) 它的加1是以整块数组所占字节数总数为单位1
输出结果:a:1638176 ,a+1:1638180,&a:1638176,&a+1:1638208
5、指针所占字节数
指针所占用的字节数和操作系统和编译器有关。
[cpp] view plain copy
/**
2016-7-12 Jason Gel
**/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//void printArray(int * a ,int num ) 和 void printArray(int a[] ,int num )是等价的
//函数定义时候,名称是不可以省略的。函数原型容许省略名称。
/** 函数原型声明4中等价形式
int sum (int *a , int n)
int sum (int * , int )
int sum (int a[] , int n)
int sum (int [] , int ) //可能略微不熟悉的一种
*/
void printArray(int * a ,int num )
{
int i ;
for(i = 0; i< num; i++)
{
printf("%3d",a[i]);
}
printf("
");
}
//这里用的是冒泡排序(起泡排序)
void sortArray(int *a ,int num )
{
int i ,j ,temp;
for(i =0 ;i <num;i++) //外层:每次选定出需要排序的一个元素,依次向后
{
for( j=i+1; j<num; j++ ) //内层:外层选定的一个元素与其后所有元素依次比较,找出最小的元素
{
if(a[i]>a[j]) //交换类代码
{
temp = a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
}
int main ()
{
int num =0 ; //在32机器中告诉C编译器分配4个字节的内存
int a [] = {1,3,5,12,6,7,54,32}; //告诉C编译器分配32个字节的内存
int test =5;
int *p = &test;
int num1 = 0;
num1 = sizeof(p)/sizeof(p[0]);
printf("num1:%d, sizeof(p):%d,sizeof(p[0]):%d
",num1,sizeof(p),sizeof(p[0]));
sortArray(p,num1);
printf("单一元素排序之后:");
printArray(p,num1);
printf("a:%d ,a+1:%d,&a:%d,&a+1:%d
",a,a+1,&a,&a+1) ;
printf("sizeof(num1):%d
",sizeof(num1));
printf("sizeof(a):%d
",sizeof(a));
printf("sizeof(int):%d sizeof(double):%d sizeof(char):%d
",sizeof(int),sizeof(double),sizeof(char)) ;
printf("sizeof(int *):%d sizeof(double*):%d sizeof(char*):%d
",sizeof(int *),sizeof(double*),sizeof(char*)) ;
//a+1 和 &a+1 结果不一样
//虽然输出结果上面,a和&a一样 。 但是a 和 &a所代表的数据类型不一样
/*重要*/
//a 代表的数据首元素的地址 (首元素),同时与整个数组地址重合,但其不能代表整个数组,只能代表起始个体的地址
//&a代表的是整个数组的地址 (特别特别的注意) 它的加1是以整块数组所占字节数总数为单位1
// num =sizeof(a);//这个是获取的整个数组的字节数,为32个字节
printf("实参a的数据类型为整个数组,所占字节为:%d
",sizeof(a)) ;
// num = sizeof(a)/sizeof(int);
num = sizeof(a)/sizeof(a[0]); //注意规范
printf("排序之前:");
printArray(a,num);
sortArray(a,num);
printf("排序之后:");
printArray(a,num);
return 0;
}
VC 32位编译器 运行截图:
64位编译器运行截图:
核心:
可以看出在64位机器中,int*的指针为8个字节,在32位中int*为 4个字节,由于:
[cpp] view plain copy
sizeof(p)/sizeof(p[0]) //等价于sizeof(int * ) /sizeof( int )
所以在64位机器上面原本应该为1的现在变成了2,导致程序出现错误。
从程序输出结果我们可以看出:实参a的数据类型为整个数组,所占字节为32。虽然实参a表示的是数组名,但是它实际的数据类型不是int *,而是表示的整个数组所占字节数。这里不要与前文的a与&a表示地址时候弄混淆。
数组的两个特殊性质
(1)不允许拷贝和赋值
不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值。
int a[] = {0,1,2}; // 含有三个整数的数组
int s2 = a; // 错误:不允许使用一个数组初始化另一个数组
a2 = a; // 错误:不能把一个数组直接赋值给另一个数组
(2)使用数组是通常将其转化成指针
在C++语言中,指针和数组有非常紧密的联系。使用数组的时候编译器一般会把它转换成指针。
通常情况下,使用取地址符来获取指向某个对象的指针,取地址符也可以用于任何对象。数组的元素也是对象,对数组使用下标运算符得到该数组指定位置的元素。因此像其他对象一样,对数组的元素使用取地址符就能的搭配指向该元素的指针:
string nums[] = {"one", "two", "three"}; // 数组元素是string对象
string *p = &nums[0]; // p指向nums的第一个元素
然而,数组还有一个特性:在很多用到数组名字的地方,编译器都会自动地将其替换为一个指向数组首元素的指针:
string *p2 = nums; // 等价于p2 = &nums[0]
在大多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针。
三、数组形参
数组的两个特殊性质对我们定义和使用作用在数组上的函数有影响。因为不能拷贝数组,所以我们无法以值传递的方式使用数组参数。因为数组会被转换成指针,所以当我们为函数传递一个数组时,实际上传递的是指向数组首元素的指针。
尽管不能以值传递的方式传毒数组,但是我们可以把形参写成类似数组的形式:
//尽管形式不同,但这三个print函数是等价的
//每个函数都有一个const int*类型的形参
void print(const int*);
void print(const int[]);
//这里的维度表示我们期望数组含有多少元素,实际不一定
void print(const int[10]);
尽管表现形式不同,但上面的三个函数是等价的:每个函数的唯一形式都是const int*类型的。当编译器处理对print函数的调用时,只检查传入的参数是否是const int*类型:
int i=0,j[2] = {0,1};
print(&i); //正确:&i的类型是int*
print(j); //正确:j转船成int* 并指向j[0]
如果我们传给print函数的是一个数组,则实参自动地转换成指向数组首元素的指针,数组的大小对函数的调用没有影响。
和其他使用数组的代码一样,以数组作为形参的函数也必须确保使用数组时不会越界。
5、二维数组作为函数的参数
指针数组和数组指针
1.前言
数组
数组有自己的特定类型,由元素类型和数组大小共同决定
数组名的值是指针常量,也是数组第一个元素的地址
当数组名作为sizeof操作符或单目操作符&的操作数时,不用指针常量表示
arrary[2]等价于*(2 + (arrary))等价于*(arrary + 2)等价于2[arrary]
数组在个函数传参时会被弱化为指针
指针
指针本质上是一个变量
指针需要占用一定的内存空间(4个字节)
指针用于保存内存地址的值
只有当两个指针指向同一个数组中的元素,指针相减(只能加减)才有意义,为指针所指元素下表差,否则结果未定义
指针进行关系运算(> < >= <=)前提是指向同一个数组中的元素
数组与指针的异同
数组声明时编译器自动分配一片连续的内存空间
指针声明时只分配了用于容纳指针的4个字节的空间
在作为函数参数时,数组参数和指针参数等价
数组名在多数情况下可以看做常量指针,值不可改变
指针本质是变量,保存的值被看做内存中的地址
2.指针数组
说明:
指针数组是一个普通的数组
指针数组中的每一个元素为一个指针
指针数组的定义:type* pArray[n];
type*为数组中每个元素的类型
pArray为数组名
n 为数组大小
示例:
int lookup_keyword(const char* key, const char* table[], const int size)
{
/*定义常指针接收传过来的字符串,定义常指针数组接收main中的指针数组(在函数参数中数组会被弱化为指针), 定义常变量接收数组大小*/
/*第二个参数就相当于定义一个数组,数组中存放的是char*类型的数据*/
int ret = -1; //定义返回值
int i = 0;
for(i = 0; i < size; ++i)
{
if(strcmp(key, table[i]) == 0)
{
//循环比较数组中的字符串是否匹配
ret = i;
break;
}
}
return ret;
}
//宏定义计算数组的大小
#define DIM(a) (sizeof(a)/sizeof(*a))
int main()
{
//定义一个指针数组,存放char*类型的数据(keyword还是一个数组)
const char* keyword[] = {
"do",
"for",
"if",
"register",
"return",
"switch",
"while",
"case",
"static"
};
//调用函数测试
printf("%d
", lookup_keyword("return", keyword, DIM(keyword)));
printf("%d
", lookup_keyword("main", keyword, DIM(keyword)));
return 0;
}
注:在理解中主要还是把指针数组的本质(本质是一个数组,不过里面存放的是指针类型的数据)搞清楚
3.数组指针
说明:
数组指针用于指向一个数组
数组名是数组首元素的起始地址,但不是数组的起始地址
可以通过取地址符&作用于数组名得到数组的起始地址
数组指针的定义:ArrayType* pointer
也可以直接定义:type (*pointer)[n](不推荐)
pointer为数组指针变量名
type为指向数组的类型
n 为指向数组的大小
示例:
typedef int(INT)[5]; //定义int类型大小为5的数组类型(INT为类型名)
typedef float(FLOAT)[10];
typedef char(CHAR)[9];
int main()
{
INT a1; //定义数组a1
float fArrary[10];
FLOAT *pf = &fArrary; //定义数组指针pf,指向fArrary的地址
CHAR cArrary;
char(*pc)[9] = &cArrary; //直接定义数组指针pc,大小为9,指向cArrary的地址
char(*pcv)[4] = cArrary; /*直接定义数组指针pcv,大小为4,指向cArrary的地址(系统会出现warning)
(原因数组指针pcv指向的应该是大小为4的数组)*/
int i = 0;
printf("%d, %d ", sizeof(INT), sizeof(a1)); //查看定义的类型有多大
for(i = 0; i < 10; ++i)
{
/*循环给pf指向的数组赋值*/
(*pf)[i] = i;
}
for(i = 0; i < 10; ++i)
{
/*循环打印fArrary数组中的值,应该和上面赋值的一样*/
printf("%f
", fArrary[i]);
}
//打印cArrary的地址,pc+1应该是cArrary首地址向后9个字节的地址,pcv+1应该是cArrary首地址向后4个字节的地址
printf("%0x, %0x, %0x
", &cArrary, pc+1, pcv+1);
return 0;
}
4.参考
《C和指针》
二维数组和二级指针
1.前言
本文所用到的部分基础知识可以查看此文:
http://blog.csdn.net/xingerr/article/details/72372563
说明:
二维数组在内存中按照一维的方式进行存储
二维数组中的第一维是一维数组
二维数组中的第二维是具体的值
二维数组的数组名可以看做是常量指针
array[2][4]根据选择不同可以为2行4列,也可以为4行2列
2.二维数组
说明:
数组名代表数组首元素的地址
数组名可以看做指向数组的常量指针
可以看作为一维数组(在内存中存储都是线性一维的)
数组中的每个元素都是同类型的一维数组
数组定义时可以省略第一个的大小(系统自动计算),如:int a[][3];
示例1:
int main()
{
int a[5][5]; //定义二维数组
//int (*p)[4]; //定义数组指针(指向4*4字节大小的数组)(也就是每次p+1地址向后走四个字节大小)
int (*p)[5]; //定义数组指针(指向5*4字节大小的数组)
p = a; //将二维数组的数组名给数组指针p
printf("%d
", &p[4][2] - &a[4][2]); //打印两个地址相减的结果,应该为0,(如果是4*4字节大小的结果为-4)
return 0;
}
示例2:
void print(int a[], int n)
{
int i = 0;
for(i = 0; i < n; ++i)
{
/*用一维方式遍历二维数组并打印*/
printf("%d ", a[i]);
}
putchar(10);
}
int main()
{
int a[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int *p = &a[0][0]; //定义一级指针存放数组首元素的地址
print(p, 9);
/*------------------分割线----------------------*/
int i = 0;
int j = 0;
for(i = 0; i < 3; ++i)
{
for(j = 0; j < 3; ++j)
{
/*正常双重循环打印数组*/
/* 因为一维数组a[i] = *(a+i),同理二维a[i][j] = *(a[i] + j) = *(*(a + i) + j) */
printf("%d ", *(*(a + i) + j));
}
}
putchar(10);
return 0;
}
示例3:
int** Malloc(int row, int col)
{
/*给二维数组申请动态空间,row为行数,col为列数*/
int** ret = (int **)malloc(sizeof(int*) * row);
/*定义二级指针ret,并申请row大小的空间,用来存放一级指针*/
int* p = (int *)malloc(sizeof(int) * row * col);
/*定义一级指针p,并申请大小为row*col的空间,存放每行的首地址*/
int i = 0;
if(ret && p)
{
/*对内存申请是否成功进行判断*/
for(i = 0; i < row; ++i)
{
/*对每一行进行分配内存(p指向一维数组的首地址,每次取出相应的列数大小的字节给每一行)*/
ret[i] = (p + col * i);
}
}
else
{
free(ret);
free(p);
return NULL;
}
return ret;
}
void Free(int** a)
{
/*释放内存*/
free(a[0]);
free(a);
}
3.二级指针
说明:
因为指针变量会在内存中占用一定空间,所以可以定义指针保存指针变量的地址值
二级指针是解决指针的传值调用和传址调用的问题
示例:
int reset(char** p, int size, int new_size)
{
/*如果申请的内存没有使用完,则进行清空释放*/
int ret = 1;
int i = 0;
int len = 0;
char* pt = NULL;
char* temp = NULL;
char* pp = *p; //定义一级指针pp接收二级指针p里面存放的指针的地址
if((p != NULL) && (new_size > 0))
{
pt = (char *)malloc(new_size); //申请一个新的空间(大小为前面申请内存使用过的大小)
temp = pt; //临时变量进行指针操作
len = (size < new_size) ? size : new_size;
/*三目运算符选取size和new_size两者较小的给len*/
for(i = 0; i < len; ++i)
{
/*将以前申请的内存里的值进行复制到新的内存中*/
*temp++ = *pp++;
}
/*释放以前的内存*/
free(*p);
*p = pt; //返回新的内存地址
}
else
{
ret = 0;
}
return ret;
}
int main()
{
char* p = (char*)malloc(5); //动态申请内存大小为5的空间,地址给p
printf("%0x ", p); //打印申请的内存空间的地址
if(reset(&p, 5, 3))
{
/*将内存缩小为3个字节大小*/
printf("%0x
", p); //打印新的内存空间的地址
}
return 0;
}
4.参考
《C和指针》
6、静态数组与动态数组
(1)
对静态数组名进行sizeof运算时,结果是整个数组占用空间的大小;
因此可以用sizeof(数组名)/sizeof(*数组名)来获取数组的长度。
int a[5]; 则sizeof(a)=20,sizeof(*a)=4.因为整个数组共占20字节,首个元素(int型)占4字节。
int *a=new int[4];则sizeof(a)=sizeof(*a)=4,因为地址位数为4字节,int型也占4字节。
(2)
但是静态数组作为函数参数时,在函数内对数组名进行sizeof运算,结果为4,因为此时数组名代表的指针即一个地址,占用4个字节的内存(因为在传递数组名的参数时,编译器对数组的长度不做检查,具体参见博文数组的引用)。对动态数组的函数名,无论何时进行sizeof运算,得到的结果都是4.
(3)
new还需要你delete,是在堆分配空间,效率较低;而[]直接在栈上分配,会自动释放,效率高,但是栈空间有限。
(4) 通过函数返回一个数组的问题
函数声明的静态数组不可能通过函数返回,因为生存期的问题,函数调用完其内部变量占用的内存就被释放了。如果想通过函数返回一个数组,可以在函数中用new动态创建该数组,然后返回其首地址。
其原因可以这样理解,因为[]静态数组是在栈中申请的,而函数中的局部变量也是在栈中的,而new动态数组是在堆中的分配的,所以函数返回后,栈中的东西被自动释放,而堆中的东西如果没有delete不会自动释放。
例子:
int *test(int *b) //b可以是静态数组的数组名,也可以是动态数组的首地址
{
for(int i=0;i<5;i++) //输出传入的数组各元素
cout<<*(b+i)<<" ";
cout<<endl;
int *c=new int[5]; //动态创建一个数组
//如果将绿色部分换为int c[5];则主函数中调用test无法得到c数组
for(i=0;i<5;i++) //新数组的各项值等于传入的数组各项值加5
*(c+i)=*(b+i)+5;
return c; //返回新创建的动态数组的首地址
}
int main()
{
int *b=new int[5]; //创建动态数组b
for(int i=0;i<5;i++)//赋值
*(b+i)=i;
//绿色部分也可以换为int b[5]={0,1,2,3,4};即也可以是静态数组
int *c=test(b); //将b作为参数,调用test函数,返回值赋给c
for(i=0;i<5;i++) //输出test返回的数组的各项
cout<<*(c+i)<<" ";
cout<<endl;
return 0;
}
简介
以下三行代码有什么区别?
int a[10];
int *a = (int*)malloc(sizeof(int)*10);
int *a = new int[10];
1
2
3
第一行代码定义a为包含10个int类型元素的整形数组。
第二行和第三行分别使用的是C和C++来定义动态数组,他们的结果是相同的。a都可以表示为一个动态数组。
我们可以使用a[1]来取数组a的第1个元素。那他们有什么区别呢?
解释
我们从指针开始说起。所谓指针,就是用来存放内存地址的一个变量,首先,指针是个变量;其次,指针存放的是内存地址。
指针的定义中包含了一个重要的说明:指针中存放的内存地址处的内容应该如何解析。例如:int *a; 说明a是一个指针,他存放的地址处的数据被解析为一系列连续的int型数据。
那int a[10],可以说明a是一个指针么?其实,这样说是不准确的。a其实本身就是一个内存地址。只是我们在实际运行之前并不知道他真正代表哪个地址。就像宏在编译的时候被替换成宏定义的内容,静态数组a实际上在执行的时候是被替换成真实的内存地址的。也就是说a已经是内存地址了,不是变量。
那么对于int a[10] 和 int *b = new int[10],a[2]和b[2]有什么区别呢?
a[2],b[2]在赋值符号=的右端的时候。此时表示取a[2]和b[2]的值。对于a[2],首先a代表的是个内存地址,在这个内存地址处偏移sizeof(int)*2个字节,取连续sizeof(int)个字节,并将其解析为int类型的数。对于b[2],首先b是一个指针,其值是一个内存地址,首先b这个变量在内存中的地址被找到,然后取连续的sizeof(int*)个字节,解析为一个内存地址,然后在这个地址处偏移sizeof(int)*2个字节,取连续sizeof(int)个字节,并将其解析为int类型的数。
a[2],b[2]在赋值符号=的左端的时候。表示向相应的内存位置赋值。赋值符号右端的表达式的结果不管是什么类型的值,都会被默认强制类型转化为int型数据。对于a[2],a代表的是个内存地址,在这个内存地址处偏移sizeof(int)*2个字节,向连续的sizeof(int)个字节内存中写入赋值号右端的结果。对于b[2],首先b这个变量在内存中的地址被找到,然后取连续的sizeof(int*)个字节,解析为一个内存地址,然后在这个地址处偏移sizeof(int)*2个字节之后,向连续的sizeof(int)个字节内存中写入赋值号右端的结果。
实验
试验下前面讨论的内容:
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
int main(int argc, char** argv){
int a[10];
printf("%.16X
", (uint64_t)(&a));
printf("%.16X
", (uint64_t)(a));
int *b = new int[10];
printf("%.16X
", (uint64_t)(&b));
printf("%.16X
", (uint64_t)(b));
delete[] b; b = NULL;
return EXIT_SUCCESS;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
某次执行结果:
000000003093F838
000000003093F838
000000003093F878
00000000309A8D90
1
2
3
4
根据输出结果,可以推断,a和&a的值是相同的,说明a已经是地址了,取地址后还是原先的地址,所以两次地址是一样的。
后两次输出结果不同,是应为b是一个指针,是变量,变量的地址与他存储的内存地址是不同的。
7、static array vs. dynamic array
Local arrays are created on the stack, and have automatic storage duration -- you don't need to manually manage memory, but they get destroyed when the function they're in ends. They necessarily have a fixed size:
int foo[10];
Arrays created with operator new[] have dynamic storage duration and are stored on the heap (technically the "free store"). They can have any size, but you need to allocate and free them yourself since they're not part of the stack frame:
int* foo = new int[10];
delete[] foo;
static is a keyword in C and C++, so rather than a general descriptive term, static has very specific meaning when applied to a variable or array. To compound the confusion, it has three distinct meanings within separate contexts. Because of this, a static array may be either fixed or dynamic.
Let me explain:
The first is C++ specific:
A static class member is a value that is not instantiated with the constructor or deleted with the destructor. This means the member has to be initialized and maintained some other way. static member may be pointers initialized to null and then allocated the first time a constructor is called. (Yes, that would be static and dynamic)
Two are inherited from C:
within a function, a static variable is one whose memory location is preserved between function calls. It is static in that it is initialized only once and retains its value between function calls (use of statics makes a function non-reentrant, i.e. not threadsafe)
static variables declared outside of functions are global variables that can only be accessed from within the same module (source code file with any other #include's)
The question (I think) you meant to ask is what the difference between dynamic arrays and fixed or compile-time arrays. That is an easier question, compile-time arrays are determined in advance (when the program is compiled) and are part of a functions stack frame. They are allocated before the main function runs. dynamic arrays are allocated at runtime with the "new" keyword (or the malloc family from C) and their size is not known in advance. dynamic allocations are not automatically cleaned up until the program stops running.