C语言里数组指针以及[中括号]的用法

C语言[]作用小结

数组中[]是如何起作用的,要想回答这个问题,首先要明白[]里数字的作用

首先为什么数组要从0而不是开始编号1?

是因为数组的索引值是一个偏移量,以数组中第一个元素开始偏移,有点参考系和参照物的味道

例如一维数组a[0]表示目标元素到数组第一个元素有0个偏移量

#include"stdio.h";

int main() {

一维数组a[i]:

int a[10] = { 30,1,22,64,7,85,3,4,5,9 };

int* p1 = a;

//指针变量存储的是地址,C语言用数组名简化表示数组的首地址

p1 = &a[3];//*p1直接指向地址对应的值a[3]

printf("*(p1 + 2)加的不是数值而是偏移量\n");

printf("% d\n", *p1 + 2);

printf("*p1与2都是数字,是数值相加,加的是值64+2=66\n");

总结:

  1. a[i]=*(a + i)
  2. 减少一个[]对应增加一个*,反之也成立
  3. []前面好像必须紧跟着一个地址或者一个字母(简化表示的地址)

二维数组b[i][j]:

先说结论,效仿一维数组,翻过书的也都知道,对于二维数组b[i][j]*(b[i] + j)*(*(b + i) + j)==*((b[i])+j)

为了验证一维数组的总结,查询优先级()和[]都是从左往右结合顺序,利用()改变运算顺序

int b[4][3] = { {110,120,130} ,{210,220,230},{310,320,330},{410,420,430} };

//printf("%d\n", b([2][1]));

//printf("%d\n", b[2]([1]));

//printf("%d\n", (b[2])([1]));

printf("%d\n", (b[2])[1]);//320

//经过测试只有↑成立,结合一维数组,一个光字母代表的是一位数组的首地址

//有理由猜测这里二维数组名b代表的也是一个首地址

printf("%d\n", **b);//110

//再次请出()改变运算顺序

printf("%d\n", *(*(b + 0) + 0));//110

//是否眼熟,这不就是b[0][0]吗?

printf("%d\n", *(*(b + 1) + 1));//220

printf("%d\n", *((*b) + 2));//*(*(b + 0) + 2)130

printf("%d\n", *(*(b + 2)));//*(*(b + 2) + 0)310

//从结果上来看,上面两个+2,第一个+ 2加的是列,第二个+ 2加的是行

//列很好理解,举个例子

printf("%d\n", *(&b[2][0] + 1));//21 320,&表示的意思是取b[2][0]的地址,之后在进行偏移1

//减少一个[]对应增加一个*,你可能就会认为b[2]==&b[2][0],

printf("%d\n", *(b[2] + 1));//320果然成立

//那么这有如何, 先别急着认为报错

printf("%d\n", *((&(b[2]))[1] + 1));//420

//&(b[2])再取一遍地址是什么东西 ? 还能取地址的地址 ? 要解释这个问题还得分清二维数组的行地址

和列地址的区别

//因为别忘了[]里还有数字, 并且数字作用的对象也至关重要

//对于地址, 经过if判定b[2] == &b[2], & b[2] == &b[2][0], b[2] == &b[2][0]都成立

//b[2] == &b[2] == &b[2][0] ?

//一位数组说过减少一个[]对应增加一个*, 为什么b[2] == &b[2][0]能后面少一个[]或前面增一个&也能成立

//这也是折磨我的问题

//还是应用上述结论b[2] == &b[2][0]

//& b[2] == &(&b[2][0]), 再来减少一个[]对应增加一个*, & (&(*(*(b + 2) + 0))), *& 相互抵消,剩下(b + 2) = &b[2],

//虽然&b[2]等价于&b[2][0]但是单独的b[2]==&b[2][0]也就是*(b+2),&b[2]可以看成(b+2)

//所以更应该将b[0] = *(b + 0), & b[2] = (b + 2)这样理解

//这就是行的表示方法,举个例子b[2][1]中[1]对[1]前面的b[2]也就是b[2]=*(b+2)做运算

printf("%d\n", (*(b + 2))[1]);//320,[1]对*(b + 2)作用*(b + 2)是列地址(&b[2][0])[1]运算规则为*((b+2)[1])等同于*(*((b+2)+1))

//为了说明二维和一位的区别这个问题, 再看个实例

printf("%d\n", *(b + 2)[1]);//410,[1]对*(b + 2)作用(b + 2)是行地址(&b[2])[1]在这里[1]对(b +2)作用,(b+2)是行地址*(&b[2])[1]

//就藏在上面两个式子里

printf("%d\n", **(&b[2] + 1));//410

//足够仔细的话, 观察有两个*, 一个[](相当于一个*), 一个& 号, 总共两个** (*&相互抵消), 最后总是有两个*, 且所有的二维数组都满足这个

//printf("%d\n", *((*(b + 2))[1]));原样替换报错原因最后有3个*

//printf("%d\n", *(&b[2] + 1));输出地址也可以理解了原因最后有1个*

//挑战一下:

printf("%d\n", *((*b) + 1));//120可以说明*b是b[0][0]的地址 *b=&b[0][0]加列地址

printf("%d\n", *((*b + 1) + 2));//210

printf("%d\n", *((*(b + 1)) + 2));//230

printf("%d\n", *b[2] + 1);//311 b[2] == &b[2][0]很好理解 310+1 311

printf("%d\n", *((&b[0][0]) + 2) + 1);//131

printf("%d\n", *((*(b + 2) + 1) + 1));//330第一个+ 1,*(b + 2)是b[2]加的是&b[2][0]

printf("%d\n", *(&(b[2]))[1]);//410

printf("%d\n", *((*b + 2) + 1));//210

printf("%d\n", *(b[2] + 1));//21 320

//回顾

//b[2][1]是怎么表示出来的b[i][j]*(b[i] + j)*(*(b + i) + j)

//为什么C语言数组从a[0]开始,在C语言中索引量不是一一对应,非要[]里的数值对应的是偏移量

//就像挑战一下里的最后一道题*((*b + 2) + 1)*(*b + 2 + 1)*(*b + 3)==*(*(b+0) + 3)

printf("%d\n", *(*(b + 2) + 4));//420如果把*(b + 2)等价于&b[2][0]分开看

printf("%d\n", *(*b + 6));//310

//加深理解偏移量也可以为负数

printf("%d\n", *(b + 1)[-1]);//110

//为什么要这样做的原因是由于计算机在存储器中以连续字节的形式保存宇符, 也就是一维数组排列的,利用索引计算出元素在存储器中的位置

//b[M][N]元素b[i][j]的地址是(*b) + i * N + j 值是 * ((*b) + i * N + j)

//碰见加地址的就是加元素 不是行定加列是把二维数组按序号排成一个一维数组

int(*q)[4] = b;//明明只有3列,这里却是以四个元素为列,佐证二维数组在计算机也是一位排列的

printf("%d\n", *(*(q + 1)));//220

printf("%d\n", *q[1]);//220

int* p2 = b[1];

printf("%d\n", *p2);//210

int* p3 = b[1] + 1;//*(b+1)+ 1

printf("%d\n", *p3);//220

//知道原理后这些也简单了吧

  • 7
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目 录 第1章 C语言 8 1.1 什么是局部程序块(local block)? 8 1.2 可以把变量保存在局部程序块中吗? 9 1.3 什么时候用一条switch语句比用多条if语句更好? 9 1.4 switch语句必须包含default分支吗? 10 1.5 switch语句的最后一个分支可以不要break语句吗? 11 1.6 除了在for语句中之外,在哪些情况下还要使用逗号运算符? 11 1.7 怎样才能知道循环是否提前结束了? 13 1.8 goto,longjmp()和setjmp()之间有什么区别? 13 1.9 什么是左值(lvaule)? 15 1.10 数组(array)可以是左值吗? 15 1.11 什么是右值(rvaule)? 16 1.12 运算符的优先级总能保证是“自左至右”或“自右至左”的顺序吗? 17 1.13 ++var和var++有什么区别? 17 1.14 取模运算符(modulus operator)“%”的作用是什么? 17 第2章 变量和数据存储 18 2.1. 变量存储在内存(memory)中的什么地方? 18 2.2. 变量必须初始化吗? 19 2.3. 什么是页抖动(pagethrashing)? 19 2.4. 什么是const指针? 20 2.5. 什么时候应该使用register修饰符?它真的有用吗? 21 2.6. 什么时候应该使用volatile修饰符? 21 2.7. 一个变量可以同时被说明为const和volatile吗? 22 2.8. 什么时候应该使用const修饰符? 23 2.9. 浮点数比较(floating-point comparisons)的可靠性如何? 23 2.10. 怎样判断一个数字型变量可以容纳的最大值? 24 2.11. 对不同类型的变量进行算术运算会有问题吗? 25 2.12. 什么是运算符升级(operatorpromotion)? 25 2.13. 什么时候应该使用类型强制转换(typecast)? 26 2.14. 什么时候不应该使用类型强制转换(typecast)? 27 2.15. 可以在头文件中说明或定义变量吗? 27 2.16. 说明一个变量和定义一个变量有什么区别? 27 2.17. 可以在头文件中说明static变量吗? 28 2.18. 用const说明常量有什么好处? 28 第3章 排序与查找 28 排序 28 查找 29 排序或查找性能? 30 3.1. 哪一种排序方法最方便? 32 3.2. 哪一种排序方法最快? 33 3.3. 对外存(磁盘或磁带)中而不是内存中的数据进行排序称为外部排序。 39 3.4. 1哪一种查找方法最方便? 44 3.5. 1哪一种查找方法最快? 46 3.6. 1什么是哈希查找? 51 3.7. 1怎样对链表进行排序? 53 3.8. 1怎样查找链表中的数据? 53 第4章 数据文件 59 4.1. 当errno为一个非零值时,是否有错误发生? 59 4.2. 什么是流(stream)? 59 4.3. 怎样重定向一个标准流? 60 4.4. 怎样恢复一个重定向了的标准流? 60 4.5. stdout能被强制打印到非屏幕设备上吗? 61 4.6. 文本模式(textmode)和二进制模式(binarymode)有什么区别? 61 4.7. 怎样判断是使用流函数还是使用低级函数? 62 4.8. 怎样列出某个目录下的文件? 62 4.9. 怎样列出一个文件的日期和时间? 63 4.10. 怎样对某个目录下的文件名进行排序? 66 4.11. 怎样判断一个文件的属性? 67 4.12. 怎样查看PATH环境变量? 69 4.13. 怎样打开一个同时能被其它程序修改的文件? 69 4.14. 怎样确保只有你的程序能存取一个文件? 71 4.15. 怎样防止其它程序修改你正在修改的那部分文件内容? 71 4.16. 怎样一次打开20个以上的文件? 72 4.17. 怎样避开"Abort,Retry,Fail”消息? 72 4.18. 怎样读写以逗号分界的本? 74 第5章 编译预处理 76 5.1. 什么是宏(macro)?怎样使用宏? 76 5.2. 预处理程序(preprocessor)有什么作用? 77 5.3. 怎样避免多次包含同一个头文件? 79 5.4. 可以用#include指令包含类型名不是".h"的文件吗? 80 5.5. 用#define指令说明常量有什么好处? 80 5.6. 用enum关键字说明常量有什么好处? 81 5.7. 与用#define指令说明常量相比,用enum关键字说明常量有什么好处? 81 5.8. 如何使部分程序在
C语言中,数组指针指针数组是两种不同的概念。下面我将为您提供一些代码例子来说明它们之间的区别。 1. 指针数组示例: ```c #include <stdio.h> int main() { int a = 10, b = 20, c = 30; int *arr[3]; // 声明一个指针数组 arr[0] = &a; // 数组的第一个元素指向变量a arr[1] = &b; // 数组的第二个元素指向变量b arr[2] = &c; // 数组的第三个元素指向变量c for (int i = 0; i < 3; i++) { printf("arr[%d] = %d\n", i, *arr[i]); // 打印数组元素的值 } return 0; } ``` 输出结果: ``` arr[0] = 10 arr[1] = 20 arr[2] = 30 ``` 在上述代码中,我们声明了一个指针数组`arr`,其中的每个元素都是一个指向`int`类型的指针。然后,我们将变量`a`、`b`和`c`的地址分别赋给了数组的元素。最后,通过循环遍历数组,并使用解引用操作符`*`来获取数组元素所指向的值。 2. 数组指针示例: ```c #include <stdio.h> int main() { int arr[3] = {10, 20, 30}; int (*ptr)[3]; // 声明一个数组指针 ptr = &arr; // 数组指针指向数组arr的地址 for (int i = 0; i < 3; i++) { printf("(*ptr)[%d] = %d\n", i, (*ptr)[i]); // 通过数组指针访问数组元素的值 } return 0; } ``` 输出结果: ``` (*ptr)[0] = 10 (*ptr)[1] = 20 (*ptr)[2] = 30 ``` 在上述代码中,我们声明了一个数组`arr`,并初始化了它的元素。然后,我们声明了一个数组指针`ptr`,并将数组`arr`的地址赋给了它。通过在指针上括号,我们可以使用解引用操作符`*`来访问数组元素的值。 总结来说,指针数组是一个数组,其中的每个元素都是指针,而数组指针是一个指向数组指针。它们在使用方法和语义上有所区别。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值