指针和数组
1.什么是指针和数组
指针和数组是C语言中的两个重要概念。
指针(Pointer)是一个变量,用于存储内存地址。它可以指向任何数据类型(如整数、字符、数组、结构体等),并通过访问内存地址来获取或修改存储在该地址上的值。指针变量本身也有一个内存地址。
数组(Array)是一种数据结构,用于存储相同类型的一组连续的元素。数组可以包含多个元素,每个元素都有一个唯一的索引来访问它。在C语言中,数组的元素可以是任何数据类型。数组名表示数组的起始地址,而数组元素可以通过索引和偏移量来访问。
数组名是数组首元素地址。
指针和数组之间存在紧密的关系。事实上,数组名本身就是一个指针常量,指向数组的第一个元素的地址。可以使用指针来访问数组的元素,也可以通过数组名来访问数组的元素。
下面是一个简单的示例,展示了指针和数组的关系:
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // 将数组名赋值给指针
// 使用指针访问数组元素
printf("使用指针访问数组元素:\n");
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, *(ptr + i));
}
// 使用数组名访问数组元素
printf("\n使用数组名访问数组元素:\n");
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
return 0;
}
在上面的示例中,我们将数组名arr
赋值给指针ptr
,然后使用指针和数组名分别访问数组的元素。通过指针的偏移和解引用操作,我们可以访问数组中的每个元素。
需要注意的是,指针和数组虽然有关联,但它们并不完全相同。指针是一个变量,可以指向不同的内存地址,而数组是一块连续的内存空间。此外,指针可以进行算术运算和指针运算,而数组名本身不能进行类似的操作。
二级指针
二级指针(Double Pointer)是指针的指针,即指向指针的指针变量。在C语言中,可以通过声明一个指向指针的指针来实现二级指针。
二级指针的主要作用是可以通过间接引用来修改指针变量的值,进而修改被指向的变量。它在某些情况下可以用于传递指针的引用或者动态分配内存。
下面是一个简单的示例,展示了二级指针的使用:
#include <stdio.h>
int main() {
int value = 10;
int *ptr1 = &value; // 一级指针,指向变量value
int **ptr2 = &ptr1; // 二级指针,指向指针ptr1
printf("Value: %d\n", value);
printf("Value through ptr1: %d\n", *ptr1);
printf("Value through ptr2: %d\n", **ptr2);
// 修改value的值
*ptr1 = 20;
printf("\nModified value: %d\n", value);
printf("Modified value through ptr1: %d\n", *ptr1);
printf("Modified value through ptr2: %d\n", **ptr2);
return 0;
}
在上面的示例中,我们首先声明了一个整数变量value
,然后声明了一个指向该变量的一级指针ptr1
,最后声明了一个指向一级指针的二级指针ptr2
。通过一级指针和二级指针的间接引用,我们可以获取和修改变量value
的值。
需要注意的是,二级指针可以有更多级别,即指向指针的指针的指针,以此类推。每一级指针的间接引用都会提供对更底层的数据的访问和修改能力。二级指针在某些情况下可以用于动态分配多维数组或链表等数据结构,或者用于传递指针的引用以在函数中修改指针的值。
字符指针
字符指针(Character Pointer)是指向字符型数据的指针变量。在C语言中,字符串被表示为以空字符(‘\0’)结尾的字符数组,而字符指针可以用于指向字符串的起始地址。
字符指针可以用于访问字符串中的字符,也可以通过指针运算来遍历字符串。通过修改字符指针的值,可以改变指向的字符串。
下面是一个简单的示例,展示了字符指针的使用:
#include <stdio.h>
int main() {
char str[] = "Hello, World!"; // 字符数组
char *ptr = str; // 字符指针,指向字符串的起始地址
// 使用字符指针遍历并打印字符串
while (*ptr != '\0') {
printf("%c", *ptr);
ptr++;
}
printf("\n");
// 修改字符指针指向的字符串
ptr = "New String";
printf("Modified string: %s\n", ptr);
return 0;
}
在上面的示例中,我们首先声明了一个字符数组str
,其中存储了一个字符串。然后,我们声明了一个字符指针ptr
,将其初始化为字符串str
的起始地址。通过递增指针的方式,我们遍历并打印了字符串中的每个字符。接着,我们将字符指针指向了一个新的字符串,从而改变了指向的字符串。
需要注意的是,字符指针可以指向任何以空字符结尾的字符数组,包括字符串常量和动态分配的字符数组。此外,可以使用字符指针和字符串相关的库函数来进行字符串操作,如strcpy
、strlen
、strcmp
等。
总结来说,字符指针是用于指向字符串的起始地址的指针变量,可以用于访问和操作字符串数据。
拓展:
int main()
{
char str1[] = "hello world";
char str2[] = "hello world";
const char* str3 = "hello world";//常量字符串 不可修改
const char* str4 = "hello world";
if(str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
这段代码会输出以下结果:
str1 and str2 are not same
str3 and str4 are same
这是因为在C语言中,数组和指针在比较时有所不同。
对于str1
和str2
,它们都是字符数组,定义为char str1[] = "hello world";
和char str2[] = "hello world";
。当两个数组进行比较时,实际上是比较它们在内存中的地址。虽然它们的内容相同,但是它们在内存中的地址是不同的,因此str1 == str2
的比较结果为假,输出"str1 and str2 are not same"。
对于str3
和str4
,它们都是指向字符串常量的指针,定义为char* str3 = "hello world";
和char* str4 = "hello world";
。在这种情况下,字符串常量是存储在静态存储区域的,编译器会确保相同的字符串常量只有一个副本。因此,str3
和str4
实际上指向相同的内存地址,因此str3 == str4
的比较结果为真,输出"str3 and str4 are same"。
需要注意的是,尽管str3
和str4
指向相同的字符串常量,但是它们仍然是指针,而不是数组。因此,不能通过str3 == str2
的方式来比较它们,因为这样比较的是指针的值,而不是它们所指向的字符串内容。
如果想要比较两个字符串的内容是否相同,可以使用strcmp
函数来进行字符串比较,或者使用循环逐个比较字符。
拓展:const *char str1 = "hello"
和const char *str2 = "hello"
有什么区别
const *char str1 = "hello"
和 const char* str2 = "hello"
之间的区别在于指针的声明方式。
-
const *char str1 = "hello"
:这种声明方式将const
关键字放在*
前面,表示指针指向的内容是常量。这意味着str1
是一个指向常量的指针,不能通过str1
修改所指向的字符串内容。 -
const char* str2 = "hello"
:这种声明方式将const
关键字放在char
前面,表示指针指向的是一个常量字符。这意味着str2
是一个指向字符常量的指针,不能通过str2
修改所指向的字符。实际上,这两种声明方式在语义上是等价的,它们都指示了指针指向的内容是常量。无论是
str1
还是str2
,都不能通过指针来修改所指向的字符串内容。示例代码:
const *char str1 = "hello"; const char* str2 = "hello"; // 尝试修改字符串内容 str1[0] = 'H'; // 错误,str1 指向的内容是常量 str2[0] = 'H'; // 错误,str2 指向的内容是常量
需要注意的是,虽然这两种声明方式在语义上是等价的,但是根据个人的编码风格和习惯,可以选择其中一种方式来声明指针。