关于数组的几个问题:
一. 数组什么时候分配内存?什么时候确定大小?
(这个问题理解的不是特别清楚,因为可以分情况讨论,回答参差不齐)
解析:
1.凡是声明都不占用内存。
例如:
声明一个类:
class AA{
int a,b,c;
}
不会占内存。
typedef 之类的声明也不会占用内存。
只在定义时占内存。比如我们定义对象:
AA a,b;
这时候就会分配内存。
(1)对全局变量,定义时就会分配内存。
(2)对局部变量,变量的内存分配在栈上进行,只有运行到变量被定义的地方时才分配内存。
要记住的是,main()内定义的变量也是局部变量,在栈上分配内存。
举例:FuncA()
{
int bb[100];}
FuncB()
{
int cc[100];
}
这两个函数中的数组都一样,仅在调用到 FUNCA 或 FUNCB 时占用内存,当从函数中返回时
,所占用的内存将会被释放掉。如果没有调用到函数,那函数中定义的变量都不会占用内存。
二. &arr, arr 区别
解析:
arr是数组的开始地址,但是&arr不是地址的地址,&arr也是数组的开始地址。
它们值相同,类型不同
复杂回答,牵扯到c语言的隐式转换
了解C的类型系统和隐式转换的规律首先,你要知道什么是隐式转换。
(1)C语言是存在数组类型的,对没错是数组类型,而这个类型在大多数情况下会隐式转换为它的元素的指针类型,所以你定义了一个数组int arr[10],arr的类型实际上是int[10],但是因为大部分情况下的隐式转换,arr的类型会退化为int*.大部分情况下都会发生隐式转换。
(2)不会发生隐式转换的情况有三个:
A. sizeof运算的时候;
B. 取地址(&)运算的时候;
C. 字符串常量初始化的时候.
这也就是为什么sizeof数组是整个数组的长度,不等于sizeof指针,
在此我不想讨论C为什么要做这么多隐式转换。
上面说&运算的时候不会发生隐式转换,所以&arr,就是取arr数组的地址,取数组的地址,注意我的用词,既不是取数组名的地址,也不是取数组首元素的地址,
arr就是数组,&arr就是取数组的地址,也就是整个数组在内存中的第一个位置的地址,这个运算结果的类型是数组的指针,
也就是int(*)[10]。
arr的值在很多情况下会隐式转换为首元素地址,所以你在问题描述里就认为它就是首元素地址(但其实不是)&arr是数组的地址,碰巧和首元素地址相同但是他们类型不同.
一个是int[10]隐式转换成的int*,另一个是int(*)[10]
以上具体知识来自:
作者:孙明琦
链接:https://www.zhihu.com/question/37722442/answer/73238767
来源:知乎
三.类似问题
int int_array[3] = {2,5,8};
printf("%p , %p , %p\n" , int_array , &int_array[0] , &int_array);
打印的结果是0x7fff0ed1a1b0 , 0x7fff0ed1a1b0 ,0x7fff0ed1a1b0。 为什么?
解析:
(1)int_array 是个数组变量,所以它其实不是一个指针,而是栈一段长度为三个 int 的连续地址空间。
(2)但是 C 语言规定了int_array 作为值引用的时候,表示的是这段空间的首地址。
(3)&int_array C 也定义了它是合法的,取值也只能是这段连续空间的首地址(还能是什么呢?)。
(4)int_array[0],是数组 int_array 的第一个元素,它的地址当然还是这段空间的首地址。
(5)其他举例:
如果 p 是一个指针,则 p[x] 是 *(p + x),这使得 2 和 4 没有冲突。
(6)需要注意的是,如果 p 是一个指针,比如 int*,&p和 p 在类型上和数值上都是不等的。
p 是一个占用 8 或 16 个字节(可以还有别的,取决于编译器的类型)空间,这个空间中存了另一端内存的地址。
&p 是一个指针的指针(int**),指向 p 这个变量自身的地址。
部分回答详情来自:
作者:邓毅
链接:https://www.zhihu.com/question/30177303/answer/47279605
来源:知乎
四.扩展关于sizeof ()
//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a)); 16//单个数组名做括号内参数代表整个数组大小
printf("%d\n",sizeof(a+0)); 4//a+0表示数组的第一个元素大小
printf("%d\n",sizeof(*a)); 4//*a表示数组中元素的大小
printf("%d\n",sizeof(a+1)); 4//a+1表示数组的第二个元素大小
printf("%d\n",sizeof(a[1])); 4//a[1]和a+0一样表示数组的元素大小
printf("%d\n",sizeof(&a)); 4//&a表示a的地址
printf("%d\n",sizeof(&a+1)); 4//&a表示数组后一个元素的地址
printf("%d\n",sizeof(&a[0])); 4//&a[0]表示数组的的一个元素的地址
printf("%d\n",sizeof(&a[0]+1));4//&a[0]+1表示数组的第二个元素地址
//字符数组
char arr[] ={'a','b','c','d','e','f'}; <span style="background-color:rgb(255,102,102);">//注意和{"abcdef"}初始化的比较</span>
printf("%d\n", sizeof(arr)); 6//arr表示整个数组
printf("%d\n",sizeof(arr+0)); 1//arr+1表示数组的第一个元素
printf("%d\n",sizeof(*arr)); 1//*arr表示数组中的元素
printf("%d\n",sizeof(arr[1])); 1//arr[1]表示数组的第二个元素
printf("%d\n",sizeof(&arr)); 4//&arr表示arr的地址
printf("%d\n",sizeof(&arr+1)); 4//&arr+1表示数组arr后的一个地址
printf("%d\n",sizeof(&arr[0]+1));4//&arr[1]+1表示数组的第二个元素的地址
char *p = "abcdef";
printf("%d\n", sizeof(p)); 4//p表示数组地址。。。。注意“abcdef”初始化会有一个\0
printf("%d\n", sizeof(p+1)); 1//p+1表示数组的第二个元素
printf("%d\n", sizeof(*p)); 1//*p表示数组的首元素
printf("%d\n",sizeof(p[0])); 1//p[0]表示数组的元素大小
printf("%d\n",sizeof(&p)); 4//&p表示数组的地址
printf("%d\n",sizeof(&p+1)); 4//&p+1表示数组的下一个地址
printf("%d\n",sizeof(&p[0]+1)); 4//&p[0]+1表示数组的第二个元素的地址
关于常量:
一. define 和 const的一些区别
1.编译器处理阶段不同:
define宏在编译预处理阶段展开, const常量修饰的只读辩论实在在编译阶段的时候确定其值。
2.类型安全检查不同
defined宏没有类型,不做类型检查,只做简单的展开
const常量有类型,在编译阶段会执行类型检查
3.存储方式不同
define定义的常量在替换后运行过程中会不断地占用内存,在内存中有若干份copy,而const定义的常量存储在数据段,只有一份copy,(在静态区)效率更高。
4.能否调试
define定义的常量不能被调试,const常量可以。
5.效率不同
编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
6.功能实质不同
const为常量值 define为字符串的替换
=================举例说明====================
const int *p; //p可变,p指向的对象不可变
int const*p; //p可变,p指向的对象不可变
int *const p; //p不可变,p指向的对象可变
const int *const p; //指针p和p指向的对象都不可变
===============区别方法=================
这里有一个记忆和理解的方法:
先忽略类型名(编译器解析的时候也是忽略类型名),我们看const离哪个近。"近水楼台先得月",离谁近就修饰谁。
判断时忽略括号中的类型
====================================
const (int) *p; //const修饰*p,*p是指针指向的对象,不可变
(int) const *p; //const修饰*p,*p是指针指向的对象,不可变
(int)*const p; //const修饰p,p不可变,p指向的对象可变
const (int) *const p; //前一个const修饰*p,后一个const修饰p,指针p和p指向的对象都不可变
部分选自:https://www.nowcoder.com/questionTerminal/a60c01a7c4ab473e81218ed0b333b4e6
网站:牛客网
本文详细解析了数组的内存分配时机、数组与指针的区别、数组的地址操作及sizeof运算符的应用,帮助读者深入理解C语言中数组的相关概念。
763

被折叠的 条评论
为什么被折叠?



