面试题答案
一键面试指针和数组的关系
- 本质区别:数组是一块连续的内存空间,用于存储相同类型的数据。而指针是一个变量,它存储的是另一个变量的内存地址。
- 联系:在很多情况下,数组名可以被当作指针来使用。比如在表达式中,数组名会自动转换为指向数组首元素的指针。例如,对于数组
int arr[5];
,arr
就相当于&arr[0]
,即指向数组首元素的指针。
关键差异举例(以字符数组和指向字符的指针为例)
- 初始化
- 字符数组: 可以通过多种方式初始化。例如:
char str1[] = "hello";
char str2[6] = {'h', 'e', 'l', 'l', 'o', '\0'};
这里 str1
是一个字符数组,编译器会根据初始化内容自动确定数组大小为 6(包括字符串结束符 '\0'
)。str2
明确指定了数组大小为 6,并逐个字符初始化。
- 指向字符的指针:
char *ptr = "world";
这里 ptr
是一个指向字符串常量 "world"
的指针。注意,字符串常量存储在只读内存区域。也可以先定义指针,再赋值:
char *ptr;
ptr = "world";
- 访问元素
- 字符数组: 通过下标方式访问数组元素。例如:
char str[] = "hello";
printf("%c\n", str[2]); // 输出 'l'
- **指向字符的指针**:
既可以通过下标方式(因为指针可以像数组名一样使用),也可以通过解引用指针并移动指针位置的方式访问。例如:
char *ptr = "hello";
printf("%c\n", ptr[2]); // 输出 'l'
printf("%c\n", *(ptr + 2)); // 同样输出 'l'
- 传递给函数
- 字符数组: 当将字符数组传递给函数时,实际上传递的是数组首元素的地址,即一个指针。函数可以通过这个指针访问数组中的所有元素。例如:
void printArray(char arr[]) {
int i = 0;
while (arr[i] != '\0') {
printf("%c", arr[i]);
i++;
}
}
int main() {
char str[] = "hello";
printArray(str);
return 0;
}
这里 printArray
函数的参数 arr
本质上是一个指针,尽管声明看起来像数组。
- 指向字符的指针:
传递指向字符的指针给函数与传递数组名类似,因为数组名在传递时会转换为指针。例如:
void printPtr(char *ptr) {
while (*ptr != '\0') {
printf("%c", *ptr);
ptr++;
}
}
int main() {
char *str = "hello";
printPtr(str);
return 0;
}
这里 printPtr
函数接收一个指向字符的指针,并通过解引用和移动指针来访问字符串中的字符。
- 内存分配和可修改性
- 字符数组:
数组的内存是在栈上分配(如果是局部数组),并且数组中的内容可以修改(除非数组被声明为
const
)。例如:
- 字符数组:
数组的内存是在栈上分配(如果是局部数组),并且数组中的内容可以修改(除非数组被声明为
char str[] = "hello";
str[0] = 'H'; // 合法,将 'h' 修改为 'H'
- **指向字符的指针**:
如果指针指向的是字符串常量,如 char *ptr = "world";
,则不能通过指针修改字符串内容,因为字符串常量存储在只读内存区域。如果指针指向的是动态分配的内存(如 char *ptr = (char*)malloc(10);
),则可以修改。例如:
char *ptr = "world";
// ptr[0] = 'W'; // 不合法,试图修改只读内存
char *ptr2 = (char*)malloc(10);
strcpy(ptr2, "hello");
ptr2[0] = 'H'; // 合法,修改动态分配内存中的内容
free(ptr2);