指针与数组的区别 —— 《C语言深度剖析》读书心得
原书很多已经写的很清楚很精炼了,我也无谓做无意义的搬运,仅把一些基础和一些我自己以前容易搞混的地方写一下。
1. 意义:
指针:
指针也是一种类型,长度为4字节,其存放的内容只能是一个地址(4字节)。
比如
char* ptr;
ptr指向一个地址,而这个地址理想的情况下存放着一个char型数据,特殊情况下也可能会指向NULL,甚至变成野指针。
数组:
数组代表的是一段连续的内存地址,而并非指针那样代表的只是一个地址。
一般数组会有名字,比如
char a[] ;
即把一段长度为100个sizeof(char)的地址段命名为a。数组也可能是匿名的,比如数组指针
char (*p)[];
这里的数组没有名字,但是数组的首地址存放在p指针中。
对于具名数组(比方说char a[10];),a代表的是数组首元素(即a[0])的首地址,而&a代表的是整个数组的首地址。尽管a与&a一般相等,但意义上有所区别。最简单做个实验,看看(a + 1)和(&a + 1)的结果,你会发现
char a[];
// Assuming that a == 0x0012ff6c (a + ) == 0x0012ff6c + sizeof(char)
(&a + ) == 0x0012ff6c + * sizeof(char)
P.S. a的地址不可修改,即不能出现a作为左值的情况(诸如a = ptr是错误的),但可以ptr = a获取a的首元素的首地址。
2. 声明定义:
指针:
// 声明,切记此时的ptr是必须初始化的,一般暂时不用会将其指向NULL。
char* ptr1;
// Definition
ptr1 = NULL;
char* ptr2 = NULL; int i;
char* ptr3 = &i; char* ptr4 = (char *) malloc( * sizeof(char)); // 意义和匿名数组中 char(*ptr4)[6] 一样,只不过申请方式有所区别。 char* ptr5 = new char[]; // C++适用, new 和 malloc的区别在此不赘述
这里注意,指针声明的时候其类型是(char *),也就是说实际上是 (char *) ptr1,这就是为什么下面赋值的时候只需要ptr1 = NULL即可,而不是*ptr1 = NULL。对于一个指针类型ptr,*是取ptr指向的地址所存放的内容。
数组:
char* a[];
char* b[] = {'l', 'h', 'f', 'c', 'w', 's'};
char* c[] = {'l', 'h', 'f', 'c', 'w', 's'};
3. 访问:
访问有两种形式:
1. 以下标方式访问: p[3]
2. 以指针方式访问: *(a+1)
指针和数组都具有这两种访问方式。
指针:
char* s = "Lhfcws";
s = s + ; // 将s指针以后一位 printf("%s", s); // 输出 hfcws , L没了。 printf("%c", s[]); // 输出 h printf("%c", *(s)); // 输出 h // 注:即使此时s = s + 5 编译器也不会报错(有的会warning),但是一般在做这种指针操作的时候要做检查,因为超出范围指针会指向不可预料的地方。
这里注意,指针声明的时候其类型是(char *),也就是说实际上是 (char *) ptr1,这就是为什么下面赋值的时候只需要ptr1 = NULL即可,而不是*ptr1 = NULL。对于一个指针类型ptr,*是取ptr指向的地址所存放的内容。
数组:
char a[] = "Lhfcws";
// a = a + 1; 此处会报错,a不可作为左值。
printf("%c", a[]); // 输出 L
printf("%c", *(a)); // 输出 L
// 插一个 strlen 函数,这里是我曾经忽略的地方,strlen会检查到\0结束,和字符数组原来长度没有关系。
printf("%d", strlen(a)); //
a[] = 0x00; // 即 \0
printf("%d", strlen(a)); //
4. 传参:
指针:
char* ptr;
char* ha = "hahaha"; void reset(char* str) {
str = ha;
} void myoutput(char* str) {
str[] = 'l';
printf("%s", str);
} void main() {
ptr = new char[];
strcpy(ptr, "Lhfcws"); myoutput(ptr); // 输出 lhfcws
printf("%s", ptr); // 还是输出 lhfcws
reset(ptr);
printf("%s", ptr); // 还是输出 lhfcws }
上述例子中:
myoutput 因为传的是指针,所以即使你只是在函数里局部修改,他也会影响到内存里变量的内容。
reset并未改变原内存上的内容,而只是将str的指针指向了另一块内存。(这点和许多语言机制是一样的,要区分修改对象内容和变量名绑定)
数组:
void fun(char a[])
{
char c = a[];
printf("%c\n", c); a[] = 'A';
i = sizeof(a);
} void fun1(char *p)
{
char c = p[];//或者是 char c = *(p+3);
printf("%c\n", c); p[] = 'B';
} int main()
{
char b[] = "abcdefg";
fun(b);
fun1(b);
printf("%s\n", b);
return ;
}
结果为:
d
d
ABcdefg
但注意,fun中传进去参数写着 char a[10], 但实际上,C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。
可以查看一下 i 值,应为 4.
5. 指针数组和数组指针:
同样还有函数指针和指针函数,其实很好分辨。语文里有个概念叫偏正短语。即两个名词连在一起,前面那个名词起的是修饰作用,真正做名词的还是后面那个。
指针数组:
就是存放指针的数组。如 int * a[10];
数组指针:
就是指向数组的指针。如 int (* p)[10]; (* 和 [] 的优先级问题此处不赘述)
P.S.
函数指针:
char * (*pf)(char * p1,char * p2);
pf = &fun; // 假设已经定义 fun(char* p1, char* p2)
(*pf) ("aa","bb");
指针函数:
就是返回类型为一个指针的函数:
char *strstr(char *str1, char *str2);
指针与数组的区别 —— 《C语言深度剖析》读书心得的更多相关文章
- C语言中指针和数组的区别
看<C专家编程>一书,看到数组与指针并不相同一章,遂做了一段测试: 代码: #include <stdio.h> #include <stdlib.h> int m ...
- C++指针和数组的区别(不能混用的情况)
通常情况下,C++中指针和数组是可以混用的,但是,在编写字符数组的全排列的时候,混用却出了问题,因此,今天特地mark一下,以备日后查找 这里整理的,不包括用new开辟的动态数组 1.数组一旦声明,我 ...
- 读书笔记之:C语言深度剖析
读书笔记之:C语言深度剖析 <C 语言深度解剖>这本书是一本“解开程序员面试笔试的秘密”的好书.作者陈正冲老师提出“以含金量勇敢挑战国内外同类书籍”,确实,这本书中的知识点都是一些在面试中 ...
- C语言深度解剖读书笔记
开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有一段时间了,一直没有时间来写这篇博客.正巧还刚刚看完了国嵌唐老师的C语言视频,觉得两者是异曲同工,所以就把两者一起记录下 ...
- C语言深度解剖读书笔记(6.函数的核心)
对于本节的函数内容其实就没什么难点了,但是对于函数这节又涉及到了顺序点的问题,我觉得可以还是忽略吧. 本节知识点: 1.函数中的顺序点:f(k,k++); 这样的问题大多跟编译器有关,不要去刻意追求 ...
- C语言深度剖析-----数组与指针分析
数组的本质: 指针的运算: 小标VS指针: a和&a的区别: 例: 数组参数: 所以下例返回4 指针和数组的对比小结:
- C语言深度剖析-----数组基础
数组的概念 数组的大小 实例 内存占用 长度 a[5] 不指定初始值的话,随机给数值 数组地址与数组名 a为数组首地址,&a为数组地址,值相等,意义不同 数组名不可以直接相等 例:主义区分指针 ...
- 【转】 C语言深度解剖读书笔记(1.关键字的秘密)
本文出处:http://blog.csdn.net/mbh_1991/article/details/10149805 开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有 ...
- C语言深度剖析-----多维数组和多维指针
多维数组和多维指针 指向指针的指针 指针变量同样也有传址调用和传值调用 case1:估算要5个字节的空间,实际只用前面3个字节,设计释放空的2字节 case2:扩充到10字节 二维数组与二维指针 二维 ...
随机推荐
- Bash 什么时候会给 HOME 赋初始值
今天无意发现下面这个表现: $ env -i bash -c cd bash: line 0: cd: HOME not set $ env -i bash -c 'echo $HOME' 这表明了 ...
- myeclipse 第一步
今天查myeclipse的一个不常用快捷键的时候,意外发现了一个学习myeclipse开端的博文(别问我是怎么查到这篇博文的....两个字:顺手),感觉比较详细,特意记下来, 别人的分享博客: htt ...
- iOS开发——UI进阶篇(十九)UISearchBar控件简介
最近用到搜索功能.总结一下 搜索,无疑可以使用UISearchBar控件! 那就先了解一下UISearchBar控件吧! UISearchBar控件就是要为你完成搜索功能的一个专用控件.它集成了很多你 ...
- 一个国家专利查询demo
写了一下午,借鉴apache的 httpclient 源码 调用 写的,拿出来分享一下,可以用作其他不同平台的项目post/get数据上面. package cn.shb.test; import o ...
- ++i vs i++
[分析] i++与++i哪个效率更高? (1)在内建数据类型的情况下,效率没有区别: (2)在自定义数据类型Class的情况下,++i效率更高! 自定义数据类型的情况下:++i返回对象的引用:i++总 ...
- Java JVM类加载机制
虚拟机的类加载机制是:JVM把描述类的数据从.class文件加载到内存,并对数据进行校验.解析.初始化,最终形成可以被JVM直接使用的Java类型. 加载.连接(验证.准备.解析).初始化.使用.卸载 ...
- 15.django之Django-Rest-Framework
1.首先安装Django-Rest-Framework pip3 install djangorestframework pip3 install markdown Markdown为可视化 API ...
- CentOS7 屏幕亮度的命令行管理
1.安装:yum install xgamma 设置亮度:xgamma -gamma n( 0.1 < n < 10.0 ,可以根据自己的喜好设置 2.shell 系统调节亮度调用的是/s ...
- FreeRTOS学习及移植笔记之二:在IAR和STM32F103VET上移植FreeRTOS
上一次,我们简单的测试了FreeRTOS的基于IAR EWARM v6.4和STM32F103VET6平台的Demo,对其有了一个基本认识.接下来我们开始自己移植FreeRTOS的过程. 1.创建一个 ...
- 整合SSM时报错:java.lang.AbstractMethodError: org.mybatis.spring.transaction.SpringManagedTransaction.getTimeout()Ljava/lang/Integer;
在整合Spring和MyBatis时,只进行了简单的插入操作,结果报了这个错,顿时整个人都崩溃了 -- 一点都看不懂嘛!只有网上搜索 结果是在使用MyBatis3.x和Spring4.x整合是 导入的 ...