声明和访问数组:
int arr[2] = {0, 1};
int arr[] = {0, 1};
int i = arr[0];
arr[1] = 23;
C中没有字符串类型,取而代之的是以'\0'
结尾的字符数组:
char * str = "abc"; //字符内容存储在堆上
char str1[] = {'a', 'b', 'c', '\0'}; //字符内容存储在栈上
第一种情况:字符内容会存储在堆上,并在栈里存储str指针变量,变量值为字符在堆上的首地址。
第二种情况:字符内容会存储在栈上,但是不会有额外变量存储字符在堆上的地址,这个地址编译器知道,只会出现在指令中,但并不会作为数据存储。
解释下第二点,总结起来一句话:只有声明指针变量才会将地址作为数据存储在内存。
如我们声明一个整型变量:int i = 15;
。这会在堆栈开辟一块空间(4个字节),存储变量的值(15),但是这个15位于堆栈的内存地址却不会作为数据存储,那么我们是怎么访问到这个变量的呢:因为变量在堆栈的地址会出现在指令中,并不是从内存里取的,而仅仅是在指令中的,具体看代码:
int i = 15;
i = 25;
对应汇编为:
movl $15, -4(%rbp)
movl $25, -4(%rbp)
#寄存器rbp存储着刚进入子程序时的堆栈地址,对堆栈变量的访问都是用rbp的值加上偏移量(常数)来进行的。
所以采用指针变量操作数据比用非指针变量或数组名要慢,前者需要至少两条汇编指令,后者只需要一条。
数组名和指针变量:
int arr[2] = {0, 1};
int *p = arr; //等效于 int *p = &arr[0];
//以下是等效操作:
int t;
t = *arr
t = *(arr+0)
t = arr[0]
t = *p
t = *(p+0)
t = p[0]
数组名和指针变量的不同点
p++ //指针变量会变化,等同于:p = &p[1]
arr++ //编译器会报错
arr++
之所以会报错,是因为arr
的值只会出现在指令中(表现为寄存器rbp的值加偏移量常数),并没存储在内存里,所以无法改变。