【C语言】字符指针、指针数组、数组指针

本文详细介绍了C语言中的指针概念,包括字符指针、指针数组和数组指针的使用及区别。通过示例代码解释了指针如何存储和操作数组,以及在内存中的存储方式。同时,文章还探讨了二维数组传参时数组指针的作用,并提供了相关的笔试题以加深理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、字符指针

二、指针数组

三、数组指针

 四、指针笔试题

一、字符指针

1. 指向单个字符:

int main()
{
	char a = 'a';
	char* pa = &a;
	printf("%c", *pa);
	return 0;
}

2.指向字符串:此时p指向字符数组arr首元素的地址

int main()
{
	char arr[] = "abcdef";
	char* p = arr;
    printf("%c\n",*p);
	printf("%s\n", p);
    return 0;
}

注意:如果要打印p指向的字符数组的所有元素,在printf传参里传的是p而不是*p。

下面我们来观察这样一段代码:

int main()
{
	char arr[] = "abcdef";
	char* p = arr;
	printf("p:%p\n", p);
	printf("&p:%p\n", &p);
	printf("arr:%p\n", arr);
	printf("&arr:%p\n", &arr);
    printf("&arr[0]:%p\n",&arr[0]);
	return 0;
}

从结果上来看,p和&p看起来是不一样的,而p、arr、&arr[0]和&arr看起来是一样的。

针对它们的关系,画张图好理解:

①p是一级指针,本质是地址,p自己本身有一个地址(因此可以有二级指针指向它),p内部存着的arr的地址。

②从&arr[0]、&arr和arr的地址打印来看,arr和&arr都是arr[0]的地址。

虽然p、arr、&arr地址的值是一样的,它们之间有什么关系呢?

 我们用下面这段代码解释:

int main()
{
	char arr[] = "abcdef";
	char* p = arr;
	printf("p+1:%p\n", p + 1);
	printf("arr+1:%p\n", arr + 1);
	printf("&arr+1:%p\n", &arr + 1);
	return 0;
}

从打印结果来看,p和arr跳过1个字节后的地址仍然相等,而&arr似乎跳过不止一个字节,将十六进制换算为十进制,B代表12,和前面的差了7个字节,正好是这个字符数组元素的个数。

究其原因

①p和arr是一样的,都代表arr数组首元素的地址,

②&arr代表的是整个arr数组的地址,但是与其首元素地址是一样的,

③p和arr的类型都是char*类型的指针,&arr的类型是char(*)[7]的数组指针(后面会讲)。

二、指针数组

顾名思义,就是存放指针的数组。

那指针数组到底有什么用呢?我们来看下面这段代码:

int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	int* arr[3] = { arr1,arr2,arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", *(*(arr + i) + j));
		}
		printf("\n");
	}
	return 0;
}

 在字符指针的讲解中,我们知道数组名就是数组首元素的地址,我们将arr1、arr2、arr3数组的首元素地址放进arr的指针数组中,将arr模拟成了一个二维数组进行遍历打印。

 其中比较难理解的是这句代码:

printf("%d ", *(*(arr + i) + j));

我们印象中的二维数组应该是这样连续存储的:

但其实在内存中arr是这样存储的:

我们先用arr+i解引用得到arr1、arr2和arr3的首元素地址,再用它加上j变量后解印用得到每个元素的值,也就是第一次解印用得到地址,第二次解印用得到值

其实可以将*(*(arr + i) + j)直接写成arr[i][j],结果是一样的。

三、数组指针

区别数组指针和指针数组:一个是指针,一个是数组,一个是指向数组的指针,一个是存放指针的数组。

指针数组:

int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 2,3,4,5,6 };
int arr3[5] = { 3,4,5,6,7 };
int* arr[3] = { arr1,arr2,arr3 };

arr数组有3个元素,分别是三个数组名,数组名表示数组首元素的地址,类型是int*。

数组指针:

int arr[5]={1,2,3,4,5};
int (*parr)[5]=&arr;

parr是指向arr数组的指针,类型是int(*)[5]。 

 问:为什么不能写成这样呢?

int(*parr)[5]=arr;

虽然编译过得去,但是报了一个警告:

原因是arr是数组首元素的地址,类型是int *,而&arr的类型是int(*)[5]。

这就解释了上面为什么arr+1只是跳过1个数组元素,而&arr+1跳过了整个数组的元素。

数组指针的应用:二维数组传参

void print(int(*parr)[5], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf("%d ", *(*(parr+i)+j));
		}
		printf("\n");
	}
}
int main()
{
	int arr[2][5] = {{ 1,2,3,4,5 }, { 2,3,4,5,6 }};
	print(arr,2,5);
	return 0;
}

 四、指针笔试题

int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	printf("%s\n", **++cpp);
	printf("%s\n", *-- * ++cpp + 3);
	printf("%s\n", *cpp[-2] + 3);
	printf("%s\n", cpp[-1][-1] + 1);
	return 0;
}

c、cp、cpp之间的关系画图表示: 

**++cpp:

 *--*++cpp+3:

*cpp[-2]+3,即*(*(cpp-2))+3:

cpp[-1][-1]+1,即*(*(cpp-1)-1)+1:

<think>好的,我现在要仔细分析用户的问题,关于C语言字符指针字符数组的区别及使用场景。首先,用户引用了多个资料,需要结合这些资料来组织答案。用户可能已经有一定的C语言基础,但可能对指针数组的具体区别不太清楚,尤其是在字符处理方面容易混淆。 首先,我需要回顾字符指针字符数组的基本定义。根据引用[2],字符指针指向字符类型的指针,例如`const char* pc = "abcdef";`,这里的pc存储的是字符串首字符的地址。而字符数组则是存储字符数组,例如`char arr[] = "abcdef";`,这时候数组arr在内存中拥有整个字符串的副本,每个字符都是数组的一个元素。 接下来,区别方面需要从内存分配、修改权限、赋值方式、sizeof结果等方面展开。比如,字符数组在定义时分配足够的内存,可以修改内容,而字符指针指向字符串字面量可能存储在只读区域,不可修改。这一点在引用[2]中提到过,当用指针存储字符串时,存储的是首字符地址,整个字符串可能不可修改。 然后,使用场景需要分别说明。字符数组适合需要修改字符串内容的情况,比如缓冲区操作。而字符指针常用于指向字符串字面量或动态分配内存,适合作为函数参数传递,或者处理只读字符串的情况。引用[4]也提到,很多操作通过指针实现,所以指针在处理动态内存或字符串传递时更灵活。 还要注意常见错误,比如字符指针未初始化就使用,或者试图修改字符串字面量的内容导致崩溃。引用[5]中的例子说明指针的解引用操作,需要正确理解指针的层级关系,避免错误。 需要确保回答结构清晰,分点列出区别和使用场景,并给出示例代码。同时,生成的相关问题要围绕用户可能的后续疑问,比如初始化方式、内存管理、常见错误等。 最后,检查是否符合格式要求,行内公式用$...$,独立公式用$$...$$,虽然当前问题可能不涉及数学公式,但如果有需要的地方要正确应用。引用标识要自然添加在对应的知识点后面,确保引用来源准确。</think>### 字符指针字符数组的区别及使用场景 #### 一、核心区别 1. **内存分配方式** - **字符数组**:在声明时分配固定内存空间,存储字符串的完整副本。例如: $$char\ a[] = "hello";$$ 此时数组$a$占6字节(含结尾的$\text{\\0}$),内存由编译器自动分配[^2][^4]。 - **字符指针**:仅存储字符串首字符的地址,指向字符串可能位于只读区域(如字符串字面量)。例如: $$char\ *p = "hello";$$ 指针$p$本身占用4/8字节(取决于系统),但字符串内容不可修改[^5]。 2. **修改权限** - 字符数组内容可修改(如$a[0] = 'H'$),而字符指针指向字符串字面量通常不可修改(尝试修改会导致未定义行为)[^2][^4]。 3. **赋值方式** - 字符数组只能在初始化时整体赋值,后续需逐个元素修改;字符指针可重新指向其他字符串地址。例如: ```c char arr[10]; arr = "new"; // 错误!数组名是常量指针 char *p = "old"; p = "new"; // 合法,p指向新的字符串地址 ``` 4. **sizeof结果** - `sizeof(数组名)`返回数组总字节数,`sizeof(指针)`返回指针变量的大小(如4字节)[^3]。 --- #### 二、使用场景对比 | **场景** | **字符数组** | **字符指针** | |------------------------|----------------------------------------|---------------------------------------| | 需要修改字符串内容 | ✅ 直接操作数组元素 | ❌ 若指向字符串字面量则不可修改 | | 动态内存管理 | ❌ 固定大小,无法动态扩展 | ✅ 可指向动态分配的内存(如malloc) | | 函数参数传递 | 传递数组首地址(退化为指针) | 直接传递指针,灵活性更高 | | 存储只读字符串 | ❌ 浪费内存(需完整拷贝) | ✅ 节省内存,仅存储地址 | --- #### 三、代码示例 1. **字符数组的修改** ```c char arr[] = "hello"; arr[0] = 'H'; // 合法,数组内容变为"Hello" ``` 2. **字符指针的只读性** ```c char *p = "world"; p[0] = 'W'; // 运行时错误!尝试修改只读内存 ``` 3. **动态内存分配(指针优势)** ```c char *p = malloc(10 * sizeof(char)); strcpy(p, "dynamic"); free(p); // 使用后需手动释放内存 ``` --- #### 四、常见错误 1. **未初始化的指针** ```c char *p; strcpy(p, "test"); // 野指针,导致崩溃 ``` 2. **混淆指针数组的赋值** ```c char arr[6]; arr = "hello"; // 错误!数组名不可重新赋值 ``` 3. **越界访问** ```c char *p = "short"; printf("%c", p[10]); // 访问非法内存 ``` ---
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值