C语言指针与数组

数组的下标应该从0还是1开始? 我提议的妥协方案是0.5,可惜他们未予认真考虑便一口回绝    -- Stan Kelly-Bootle
 
1. 数组并非指针
为什么很多人会认为指针和数组始终应该可以互换的呢? 因为对数组的引用总是可以写成对指针的引用,而且确实存在一种指针和数组的定义完全相同的上下文环境,
不幸的是,这只是数组的一种极为普通的用法,并非所用情况下都是如此。
 
2. 什么是声明,什么是定义
C语言中对象必须有且只有一个定义,但它可以有多个extern声明.
定义:只能出现在一个地方,确定对象的类型并分配内存,用于创建新的对象,例如 int a[100]
声明:可以多次出现,描述对象的类型,用于指代其他地方定义的对象(例如在其他文件里) 例如 extern int a[100]
extern对象声明告诉编译器对象的类型和名字,对象的内存分配则在别处进行
 
3. 数组与指针的区别
出现在赋值左边的符号被称为 左值, 出现在赋值右边的符号被称为 右值。
编译器为每个变量分配一个地址(左值),这个地址在编译时可知,并且该变量在运行时一直保存于这个地址中。
存储于变量中的值(右值)只有在运行时才可知,如果需要用到变量中存储的值,编译器就发出指令从指定地址读入变量并将它存于寄存器中。
例如: char a[9] = "abcdefgh";   c = a[i] 
假设编译器符号表具有一个地址9980
运行时步骤1:取 i 的值,将它与 9980 相加 (基址加偏移量)
运行时步骤2:取地址 [9980+i] 的内容
 
例如:char* p;  c = *p;
假设编译器符号表有一个符号p,它的地址为4624
运行时步骤1:取地址 4624 的内容,假设是 1024
运行时步骤2:取地址1024的内容
 
例如:char* p = "abcdefgh"   c = p[i]
假设编译器符号表有一个p,地址为 4624
运行时步骤1:取地址4624的内容,假设是 1024
运行时步骤2:取得 i 的值,并将它与 1024 相加  (基址加偏移量)
运行时步骤3:取地址 [1024+i] 的内容
 

指针 数组
保存数组的地址    保存数据

间接访问数据,首先取指针的内容,把它作为地址,然后从这个地址提取数据。

如果指针有一个下标[i],就把指针的内容 加上i作为地址,从中提取数据

直接访问数据 a[i]只是简单的以a+i作为地址取数据
通常用于动态数据结构                通常用于存储数目固定且数据类型相同的元素
相关的函数为 malloc free 隐式分配和删除
通常指向匿名数据      自身即为数据名
定义指针时,编译器没有为指针所指向的对象分配空间,只是分配指针本身的空间
ANSI C中,初始化指针时所创建的字符串常量所定义为 只读。在有些编译器中,字符串常量被存放在只允许读取的文本段中,以防止它被修改
char* ptr = "hello world"    // 这种写法是非常不推荐的,因为 ptr 所指对象是只读的,这将隐式的将 const 转 non-const,任何对 ptr 的修改都会 coredump
const char* ptr = "hello world"   // 这种写法明确表示 ptr 的const属性,这时对 ptr 所指对象的修改都会 在编译时报错
char a[] = "hello world"              //这种写法表明 数组中元素都是可修改的,但是数组名是不可修改的左值,即数组首地址不可以改变,是常量
 
4. 什么时候数组与指针相同
所有作为函数参数的数组名可以通过编译器转换为指针
数组的声明就是数组,指针的声明就是指针,两者不能混淆
注意:如果定义一个数组,在其他文件中对它extern声明时也必须把它声明为数组,指针也是如此
 
在使用数组(在语句或表达式中引用)时,数组总是可以写成指针的形式
 
数组下标表达式总是可以改写成带偏移量的指针表达式
 
什么时候数组和指针是相同的:
规则1:表达式中的数组名(与声明不同)被编译器当作一个指向该数组第一个元素的指针 (具体见 ANSI C标准 第6.2.2.1 节)
规则2:下标总是与指针的偏移量相同(具体见ANSI C标准 第6.3.2.1 节)
规则3:在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针(具体见ANSI C标准 第6.7.1 节)
 
其实规则1和规则1合起来理解如下:对数组下标的引用总是写成"一个指向数组的起始地址的指针加上偏移量"
对数组的引用如a[i]在编译时总是被编译器改写成*(a+i)指针访问的形式
 
"作为函数参数的数组名"等同于指针
void func(int* arg);
void func(int arg[10]);
void func(int arg[]);
上述三种形式是完全等同的
 
5. 为什么C语言把数组形参当作指针
把作为形参的数组和指针等同起来是出于效率原因的考虑
在C语言中,所有非数组形式的数据实参均以传值形式(对实参做一份拷贝并传递给调用的函数,函数不能修改作为实参的实际变量的值,而只能修改传递给它的那份拷贝)调用
C语言允许程序员把形参声明为数组(程序员打算传递给函数的东西)或者指针(函数实际所接收到的东西)
不管程序员实际所写的是哪种形式,函数并不自动知道指针所指数组共有多少个元素,所以必须有个约定,如数组以NULL结尾或者另一个附加的参数表示数组的范围
可以通过向函数传递一个指向数组第一个元素的指针来访问整个数组,但也可以让指针指向任何一个元素,这样传递给函数的就是从该元素之后的数组片段

C语言指针与数组的更多相关文章

  1. C语言指针和数组知识总结(上)

    C语言指针和数组知识总结(上) 一.指针的基础 1.C语言中,变量的值能够通过指针来改变,打印指针的语句符号可以是:  %08x 2.指针的本质 指针的本质就是变量,那么既然是变量,那么一定会分配地址 ...

  2. C语言指针与数组的定义与声明易错分析

    部分摘自<C语言深度解剖> 1.定义为数组,声明为指针 在文件1中定义: char a[100]; 在文件2中声明: extern char *a; //这样是错误的 这里的extern告 ...

  3. 11-C语言指针&一维数组&字符串

    一.用指针遍历数组元素 1.最普通的遍历方式是用数组下标来遍历元素 1 // 定义一个int类型的数组 2 int a[4] = {1, 2, 3, 4}; 3 4 int i; 5 for (i = ...

  4. c语言指针,数组

    指针:说简单点就是一个地址.例如int *p,p是个变量,里面放的是地址0x0000,同理,每一个指针,不管什么类型,都是地址,也就是空间都是4个字节(32位机). 以此类推,指针也有指向它的指针in ...

  5. C语言指针和数组

    #include <stdio.h> int main() { /********************************************* * * 指针和数组: * 定义 ...

  6. c语言 指针与数组

    关键概念: 1.多个不同类型的指针可以对应同一个地址: 2.(&p)则是这样一种运算,返回一个指针,该指针的值是当时声明p 时开辟的地址,指针的类型是p的类型对应的指针类型: 3.(*p)操作 ...

  7. C语言指针和数组知识总结(下)

    一.数组指针: 数组指针就是一个指针,只不过它指向的是一个数组.可以通过如下方式来定义 typedef int Array[5]; //数组类型 Array* m;      //数组定义 还有一种更 ...

  8. C语言核心之数组和指针详解

    指针 相信大家对下面的代码不陌生: int i=2; int *p; p=&i;这是最简单的指针应用,也是最基本的用法.再来熟悉一下什么是指针:首先指针是一个变量,它保存的并不是平常的数据,而 ...

  9. 指针与数组的区别 —— 《C语言深度剖析》读书心得

    原书很多已经写的很清楚很精炼了,我也无谓做无意义的搬运,仅把一些基础和一些我自己以前容易搞混的地方写一下. 1. 意义: 指针: 指针也是一种类型,长度为4字节,其存放的内容只能是一个地址(4字节). ...

随机推荐

  1. debian常用指令

    查看软件xxx安装内容 dpkg -L xxx 查找软件 apt-cache search 正则表达式 查找文件属于哪个包 dpkg -S filename apt-file search filen ...

  2. c++ json字符串转换成map管理

    在cocos2dx for lua中,我们经常通过lua的table传入c++使用,然后早c++层操作数据. 实现步骤大致如下: table->string->c++层->通过rap ...

  3. C#:CodeSmith根据数据库中的表创建C#数据模型Model + 因为没有钱买正版,所以附加自己写的小代码

    对于C#面向对象的思想,我们习惯于将数据库中的表创建对应的数据模型: 但假如数据表很多时,我们手动增加模型类会显得很浪费时间: 这个时候有些人会用微软提供的EntityFrameWork,这个框架很强 ...

  4. python3 兔子繁殖问题

    题目 有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 代码: month = int(input("繁殖 ...

  5. Docker工具

    虚拟化 什么是虚拟化 在计算机中,虚拟化(英语:Virtualization)是一种资源管理技术,是将计算机的各种实体资源, 如服务器.网络.内存及存储等,予以抽象.转换后呈现出来, 打破实体结构间的 ...

  6. 安装VS2010 无法打开数据文件deffactory.dat

    VS2010旗舰版可用Key: YCFHQ9DWCYDKV88T2TMHG7BHP 解压VS2010安装ISO文件,找到setup\deffactory.dat文件,用记事本打开,将里面内容清空,将以 ...

  7. freertos知识点笔记——队列、二值信号量、计数信号量

    队列1.队列queue通常用于任务之间的通信,一个任务写缓存,另一个任务读缓存.队列还会有等待时间,2.阻塞超时时间.如果在发送时队列已满,这个时间即是任务处于阻塞态等待队列空间有效的最长等待时间.如 ...

  8. Mr. Panda and Crystal HDU - 6007 最短路+完全背包

    题目:题目链接 思路:不难看出,合成每个宝石需要消耗一定的魔力值,每个宝石有一定的收益,所以只要我们知道每个宝石合成的最小花费,该题就可以转化为一个背包容量为初始魔力值的完全背包问题,每个宝石的最小花 ...

  9. JDK1.8 HashMap$TreeNode.balanceInsertion 红黑树平衡插入

    红黑树介绍 1.节点是红色或黑色. 2.根节点是黑色. 3.每个叶子节点都是黑色的空节点(NIL节点). 4 每个红色节点的两个子节点都是黑色.(从每个叶子到根的所有路径上不能有两个连续的红色节点) ...

  10. The 2018 ACM-ICPC Chinese Collegiate Programming Contest Fight Against Monsters

    #include <iostream> #include <cstdio> #include <cstring> #include <string> # ...