指针常量/常量指针&指针数组/数组指针

本文深入探讨了C语言中指针常量、常量指针及指针数组、数组指针的概念与用法,通过实例解析了它们的区别与应用场景。

1、指针常量/常量指针

指针常量:指针本身是常量,它的值(即地址)不可修改,但是可以对它所指向的内容进行修改如:int *const p;

常量指针:指向常量的指针,它所指向的地址中的内容是不可修改的。如const int *p;

另:常量指针常量:指向常量的指针常量,它所指向的地址不可修改,并且地址的内容也不可修改。如const int *const p

2、指针数组/数组指针

指针数组:存放指针的数组,即这个数组中的数据全都是指针。如:int *(p[])

int *p[3];//这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
int a[3][4];
for(i=0;i<3;i++)
p[i]=a[i];


数组指针:指向数组的指针,如:int (*p)[]

int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
 p=a;        //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
 p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]


附:优先级:()>[]>*

&lt;think&gt; 我们正在讨论C语言中的指针、结构体、数组和结构体指针的表示形式及区别。下面将逐步展开: 1. **指针(Pointer)**: - 表示形式:`int *ptr;`(指向整型的指针) - 本质:存储内存地址的变量。可以通过解引用操作符(`*`)访问或修改所指向地址的数据。 2. **结构体(Struct)**: - 表示形式: ```c struct Student { char name[20]; int age; }; ``` - 本质:用户自定义的数据类型,可以包含多个不同类型的成员(变量)。 3. **数组(Array)**: - 表示形式:`int arr[10];`(含有10个整型元素的数组) - 本质:连续内存空间存储的相同类型元素的集合。 4. **结构体指针(Pointer to Struct)**: - 表示形式:`struct Student *pStudent;` - 本质:指向结构体类型的指针。通过该指针可以访问结构体的成员。 接下来,详细说明它们的区别和联系: ### 一、指针与普通变量的区别 - 普通变量直接存储数据值,而指针存储的是内存地址。 - 指针可以通过解引用操作符(`*`)间接访问或修改其指向地址的数据。 示例: ```c int a = 10; // 普通整型变量 int *p = &amp;a; // 指针p指向a的地址 *p = 20; // 通过指针修改a的值为20 ``` ### 二、结构体与数组的区别 | 特性 | 数组(Array) | 结构体(Struct) | |--------------|----------------------------------|----------------------------------| | **元素类型** | 必须相同 | 可以不同 | | **内存布局** | 连续内存块 | 连续内存块(但可能有填充字节) | | **访问方式** | 通过下标`[]` | 通过成员名(`.`操作符) | | **大小** | `sizeof(数组)=元素大小&times;元素个数` | `sizeof(结构体)&ge;各成员大小之和`(因为内存对齐) | ### 三、结构体指针与结构体实例 - 结构体实例:`struct Student stu;`,直接访问成员使用`.`操作符,如`stu.age`。 - 结构体指针:`struct Student *pStu = &amp;stu;`,访问成员有两种方式: 1. 解引用后使用`.`:`(*pStu).age` 2. 直接使用`-&gt;`操作符:`pStu-&gt;age` 注意:`-&gt;`操作符是解引用和成员访问的组合,即`pStu-&gt;age`等价于`(*pStu).age`。 ### 四、数组指针的关系 - 数组名在大多数表达式中会退化为指向其首元素的指针。 例如:`int arr[10];`,`arr`的类型是`int[10]`,但在表达式中(如函数传参)会退化为`int*`。 - 指针可以像数组一样通过下标访问,但本质不同: ```c int arr[10]; int *p = arr; // p指向arr[0] // 以下等价 arr[2] = 5; *(arr+2) = 5; p[2] = 5; *(p+2) = 5; ``` ### 五、结构体中的数组 在结构体中,数组作为成员: ```c struct Example { int nums[10]; }; ``` 访问方式: ```c struct Example ex; ex.nums[0] = 1; // 通过结构体实例访问 struct Example *pEx = &amp;ex; pEx-&gt;nums[0] = 1; // 通过结构体指针访问 ``` ### 六、指向数组指针 指向数组指针与指向数组首元素的指针不同: ```c int arr[10]; int *p1 = arr; // 指向首元素的指针,类型int* int (*p2)[10] = &amp;arr; // 指向整个数组指针,类型int(*)[10] ``` 使用区别: - `p1+1` 前进一个`int`大小(4字节) - `p2+1` 前进整个数组大小(40字节) ### 七、结构体数组 结构体数组数组元素为结构体的数组: ```c struct Student class[30]; // 有30个学生的数组 ``` 访问: ```c class[0].age = 18; // 第一个学生的年龄 (class+1)-&gt;age = 19; // 第二个学生,等价于class[1].age ``` ### 八、指针数组数组指针 - 指针数组数组元素是指针,如`int *arr[10]`,表示10个整型指针组成的数组。 - 数组指针:指向数组指针,如`int (*p)[10]`。 ### 九、结构体指针数组 结构体指针数组数组元素是指向结构体的指针。 ```c struct Student *pClass[30]; // 数组中有30个指向Student的指针 ``` 访问: ```c pClass[0]-&gt;age = 18; // 访问第一个指针指向的结构体的age成员 ``` ### 十、内存对齐问题(补充) 结构体成员在内存中可能因为对齐而存在间隙,例如: ```c struct Example { char c; // 1字节 // 编译器可能插入3字节填充(假设int为4字节且要求4字节对齐) int i; // 4字节 }; ``` 使用`sizeof(struct Example)`可能是8字节而不是5字节。 通过以上内容,我们可以总结出: - 指针是地址,结构体是复合数据类型,数组是相同类型元素的集合。 - 结构体指针是指向结构体的指针,通过它访问成员使用`-&gt;`。 - 数组名在多数情况下退化为指针,但二者不等价(数组名是常量指针,不可修改)。 - 结构体中可以包含数组,也可以包含指针。 现在,根据要求,我们需要生成相关问题: &sect;&sect;相关问题&sect;&sect; 1. 为什么结构体中会出现内存填充?如何手动控制结构体的内存对齐? 2. 指针数组数组指针在内存布局上有何不同? 3. 函数参数传递时,传递结构体实例和传递结构体指针有何性能差异? 4. 如何通过结构体指针访问结构体中的数组成员?请举例说明。 5. 在C语言中,数组名和指针在哪些情况下可以互换?在哪些情况下不能? &lt;/think&gt; ### 指针、结构体、数组、结构体指针的表示形式及区别 #### 一、基本概念与表示形式 | **类型** | **表示形式** | **本质说明** | **示例代码** | |------------------|----------------------------------|----------------------------------|----------------------------------| | **数组** | `数据类型 数组名[长度]` | 连续内存空间存储同类型元素 | `int arr[5] = {1,2,3,4,5};` | | **结构体** | `struct 结构体名 {成员列表};` | 不同数据类型成员的集合 | `struct Point { int x; int y; };`| | **指针** | `数据类型 *指针变量名` | 存储内存地址的变量 | `int *p = &amp;num;` | | **结构体指针** | `struct 结构体名 *指针变量名` | 指向结构体内存的指针 | `struct Point *ptr = &amp;point;` | #### 二、关键区别分析 1. **内存访问方式** - **数组**:通过偏移量直接访问元素 $$ \text{地址计算:} \text{基地址} + \text{索引} \times \text{元素大小} $$ ```c arr[2] = 10; // 直接访问第3个元素 ``` - **结构体**:通过成员名访问 ```c struct Point p1; p1.x = 5; // 直接访问成员 ``` - **指针**:通过解引用访问目标数据 $$ \text{目标数据} = *(\text{指针值}) $$ ```c int num = 42; int *p = &amp;num; *p = 100; // 修改目标数据 ``` - **结构体指针**:通过`-&gt;`运算符访问成员 ```c struct Point *ptr = &amp;p1; ptr-&gt;y = 8; // 等价于 (*ptr).y ``` 2. **内存布局差异** ```mermaid graph LR A[数组内存] --&gt; B[元素0] B --&gt; C[元素1] C --&gt; D[...] D --&gt; E[元素n] F[结构体内存] --&gt; G[成员1] G --&gt; H[成员2] H --&gt; I[...] I --&gt; J[成员n] K[指针内存] --&gt; L[存储地址] L --&gt; M[目标数据] N[结构体指针] --&gt; O[存储地址] O --&gt; P[结构体实例] P --&gt; Q[成员访问] ``` 3. **类型兼容性** | **操作** | **数组** | **结构体** | **指针** | **结构体指针** | |---------------------|----------|------------|----------|----------------| | 直接赋值 | ❌ | ✅ | ✅ | ✅ | | 作为函数参数传递 | ✅&sup1; | ✅ | ✅ | ✅ | | sizeof返回值 | 数组大小 | 结构体大小 | 指针大小 | 指针大小 | | 支持++/--操作符 | ❌ | ❌ | ✅ | ✅ | &gt; &sup1; 数组作为参数时退化为指针(如`void func(int arr[])`等价于`void func(int *arr)`) #### 三、典型场景对比 1. **结构体 vs 结构体指针** ```c // 结构体实例 struct Point pt1; pt1.x = 10; // 直接访问 // 结构体指针 struct Point *pt_ptr = &amp;pt1; pt_ptr-&gt;y = 20; // 通过指针访问 ``` - **内存差异**: - 实例:直接分配结构体内存(栈/静态区) - 指针:分配指针内存 + 指向的结构体内存(可能位于堆) 2. **数组 vs 指针** ```c int arr[3] = {1,2,3}; int *p = arr; // p指向arr[0] printf(&quot;%d&quot;, arr[1]); // 输出2 printf(&quot;%d&quot;, *(p+1)); // 等价输出2 ``` - **关键区别**: - `sizeof(arr)` = 12(假设int为4字节) - `sizeof(p)` = 4/8(指针大小) #### 四、复合类型示例 ```c // 结构体中包含数组 struct SensorData { char name[20]; float readings[10]; }; // 结构体指针数组 struct SensorData *deviceArray[5]; // 访问示例 deviceArray[0] = malloc(sizeof(struct SensorData)); strcpy(deviceArray[0]-&gt;name, &quot;TempSensor&quot;); deviceArray[0]-&gt;readings[2] = 36.5; // 三级访问 ``` #### 五、内存对齐问题 结构体/数组需考虑内存对齐: ```c struct Example { char c; // 1字节 // 3字节填充(假设4字节对齐) int i; // 4字节 }; // 总大小=8字节 int arr[3]; // 连续12字节(无填充) ``` &gt; 对齐规则:成员地址必须是其大小的整数倍[^1] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值