c语言指针难点
先来一个例子
例:
#include "stdio.h" int main()
{
int a[] = {,,,,};
printf("a是一个地址%d\n",a);
printf("a[0]是一个数%d\n",a[]);
printf("&a[0]是地址%d\n",&a[]);
printf("&a[1]是地址%d\n",&a[]);
printf("上面等价于a+1,地址%d\n",a+);
printf("&a应该地址的地址,非法,但竟然和a一样,地址%d\n",&a);
printf("为什么加了20呢,跨越了整个数组,把数组改成6确实如此%d\n",&a+); int *p = (int *)(&a+);
printf("%d,%d", *(a+), *(p-));
}
/*
http://segmentfault.com/q/1010000000303353 假设有以下数组 int a[5] = {1,2,3,4,5};
int *x = &a+1;
虽然上面的程序可以编译通过,但是,编译器会出警告,因为x和a的类型不匹配,要想把警告去掉,有两种方法 1、通过强制类型转换 int *x = (int *)(&a+1);
print("%d\n",*(x-1));
2、为它寻找合适的类型 &a的类型是int (*)[5] int (*x)[5] = &a+1;
printf("%d\n",**(x-1));
这里就牵涉到了如何写出适当的数据类型,这在赋值和参数传递中很重要! 所以,首先我得总结一下a,&a和&a[0]这三个数据的数据类型 1.a是数组名,是指向数组第一个元素的指针,毫无疑问,在这里,数组第一个元素的数据类型是int所以a的数据类型是就是int*。 2.&a是对一个一维数组取地址,得出来的是指向数组的指针(在这里是pointer to array of int), 也就是int(*)[5]。 3.&a[0]就很简单,首先a[0]得到的是一个整形数int,然后对它取地址,所以它的数据类型就是int*。 知道了数据类型,那么对指针运算看起来就清晰多了! */
a得出来的是指向数组的指针,所以&a+1其实是以数组的长度为单位来移动的。如果你只是想要得到数组的第二个元素的话,那么就用*(&a[0]+1),因为&a[0]的数据类型是int*。画个图先:
假设有以下数组
int a[5] = {1,2,3,4,5};
int *x = &a+1;
虽然上面的程序可以编译通过,但是,编译器会出警告,因为x和a的类型不匹配,要想把警告去掉,有两种方法
1、通过强制类型转换
int *x = (int *)(&a+1);
print("%d\n",*(x-1));
2、为它寻找合适的类型 &a的类型是int (*)[5]
int (*x)[5] = &a+1;
printf("%d\n",**(x-1));
这里就牵涉到了如何写出适当的数据类型,这在赋值和参数传递中很重要!
所以,首先我得总结一下a,&a和&a[0]这三个数据的数据类型
a是数组名,是指向数组第一个元素的指针,毫无疑问,在这里,数组第一个元素的数据类型是int所以a的数据类型是就是int*。
&a是对一个一维数组取地址,得出来的是指向数组的指针(在这里是pointer to array of int), 也就是int(*)[5]。
&a[0]就很简单,首先a[0]得到的是一个整形数int,然后对它取地址,所以它的数据类型就是int*。
知道了数据类型,那么对指针运算看起来就清晰多了!
先看看通过强制类型转换的那部分代码,它会输出什么数字呢?
答案是5!通过刚才对数据类型的总结可以知道,&a的数据类型是int (*)[5],所以&a+1其实是已经移动了5*sizeof(int)个字节了现在指针是指到了数组最后一个元素的后一个元素(图1),也就是说,已经越界了!但是因为x的数据类型其实是int * 所以对于x-1,其实是向左移动了1*sizeof(int)个字节,也就是指向了最后一个元素,所以*(x-1)得出来的值就是数组的最后一个元素:5
好了,现在再看第二部分,它又会输出什么数字呢?先不说答案,刚才也说了,在这里 x的数据类型是int (*)[5],是一个指向含有5个int元素的一维数组的指针,对它进行加减运算的话就会以 sizeof(int)*5个字节为单位进行移动,所以x-1其实是向左移动了sizeof(int)*5个字节,在我的机器上是移动了 20个字节,也就是回到了数组的第一个元素,所以得出来的答案就是:1
以上是一维数组的,下面我想说说二维数组的情况,有以下一段代码:
int a[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
/*
?? = a; // int (*b)[3] = a;
?? = a[0]; // int *c = a[0];
?? = a[0][0] // int d = a[0][0];
?? = &a; // int (*e)[3][3] = &a;
?? &a[0]; // int (*f)[3] = &a[0]
?? &a[0][0]; // int *g = &a[0][0]
*/
还是先看看a,a[0],&a,&a[0],&a[0][0]这几者的数据类型:
注意上面加粗了的文字,数组名是指向数组第一个元素的指针!
a的数据类型是??int *?不是,这句加粗了的文字的核心就是第一个元素这五个字, a的第一个元素不就是a[0][0]吗?严格来说,不是,a的第一个元素其实是a[0],那么a[0]的数据类型是什么呢?a[0] 是一个包含三个int元素的数组,所以a[0]的类型就和int t[3]中t的类型一样,是int*,既然第一个元素 的数据类型是 int*,那把这个二维数组看成一维数组的话,实际上它就是一个含有三个int*元素的数组,也就是指针数组,所以a的数据类型就是int (*)[3]
再来看看&a,在一维数组的时候说了,对一个数组名取地址得出来的是指向数组的指针,所以&a的数据类型 就是int (*)[3][3]
&a[0]这个看上去有点蛋疼,但是在上上段文字中也说了,a[0]是一个包含三个int元素的数组,和int t[3] 中的t的数据类型一样,是int*,自然,&a[0]的数据类型就和&t的数据类型一样,也就是int (*)[3]
到这里,二维数组中关于数据类型就写得差不多了,既然知道了数据类型,那么运算起来就可以准确知道指针会移动到哪里!
看看下面这段代码:
#include "stdio.h" int main()
{
int a[][] = {{,,},{,,},{,,}};
int (*x)[] = a;
//x的数据类型是int (*)[3],所以,x+1实际上是移动了3*sizeof(int)个字节
int *k = a[];
int (*y)[][] = &a;
int (*q)[] = &a[];
int *z = &a[][]; printf("%d\n",(*(x+))[]);
printf("%d\n",*(k+));
printf("%d\n",(*(*(y+)))[]);
printf("%d\n",(*(q+))[]);
printf("%d\n",*(z+)); int *p = (int *)(y+);
printf("%d\n",*(p-)); }
x的数据类型是int (*)[3],所以,x+1实际上是移动了3*sizeof(int)个字节,如图所示:
k的数据类型是int *,所以,k+1实际上是移动了sizeof(int)个字节。
y的数据类型是int (*)[3][3],所以y+1实际上是移动了3*3*sizeof(int)个字节,也就是到了数组最后一个元素的后面的一个元素。如图所示:
q的数据类型是int (*)[3],所以,q+1实际上也是移动了3*sizeof(int)个字节。
z的数据类型是int *,所以z+1实际上是移动了sizeof(int)个字节。
所以对数组指针进行加减运算,最重要的是知道它的步长,而步长又是由数据类型决定的!
参考文件汇总:
1、http://www.cnblogs.com/bluewelkin/p/4059840.html
2、http://www.cnblogs.com/bluewelkin/p/3738383.html
3、http://www.cnblogs.com/bluewelkin/p/3565011.html
4、http://www.cnblogs.com/bluewelkin/p/4056847.html
c语言指针难点的更多相关文章
- [转]C语言指针学习经验总结浅谈
指针是C语言的难点和重点,但指针也是C语言的灵魂 . 这篇C语言指针学习经验总结主要是我入职以来学习C指针过程中的点滴记录.文档里面就不重复书上说得很清楚的概念性东西,只把一些说得不清楚或理解起来比较 ...
- (转载)c语言指针学习
前言 近期俄罗斯的陨石.四月的血月.五月北京的飞雪以及天朝各种血腥和混乱,给人一种不详的预感.佛祖说的末法时期,五浊恶世 ,十恶之世,人再无心法约束,道德沦丧,和现在正好吻合.尤其是在天朝,空气,水, ...
- 2-Linux C语言指针与内存-学习笔记
Linux C语言指针与内存 前面我们对于: c语言的基本用法 makeFile文件的使用 main函数的详解 标准输入输出流以及错误流管道 工具与原理 指针与内存都是c语言中的要点与难点 指针 数组 ...
- c语言指针学习【转】
前言 近期俄罗斯的陨石.四月的血月.五月北京的飞雪以及天朝各种血腥和混乱,给人一种不详的预感.佛祖说的末法时期,五浊恶世 ,十恶之世,人再无心法约束,道德沦丧,和现在正好吻合.尤其是在天朝,空气,水, ...
- C语言指针专题——指针难学的4点原因
前一篇跟大家聊了聊指针的概念,可是就算了解了指针是什么,为什么依然感觉难学?我试着从几个点切入,聊聊指针难学之处. 文末会给大家推荐几本书,有需要的朋友可以看看! 难点1. 讨厌的星号 定义指针变量p ...
- C语言入门---第九章 C语言指针
没学指针就是没学C语言! 指针是C语言的精华,也是C语言的难点. 所谓指针,也就是内存的地址,所谓指针变量,也就是保存了内存地址的变量.不过人们往往不会区分两者的概念,而是混淆在一起使用. ===== ...
- C语言指针转换为intptr_t类型
1.前言 今天在看代码时,发现将之一个指针赋值给一个intptr_t类型的变量.由于之前没有见过intptr_t这样数据类型,凭感觉认为intptr_t是int类型的指针.感觉很奇怪,为何要将一个指针 ...
- 不可或缺 Windows Native (7) - C 语言: 指针
[源码下载] 不可或缺 Windows Native (7) - C 语言: 指针 作者:webabcd 介绍不可或缺 Windows Native 之 C 语言 指针 示例cPointer.h #i ...
- C语言指针学习
C语言学过好久了,对于其中的指针却没有非常明确的认识,趁着有机会来好好学习一下,总结一下学过的知识,知识来自C语言指针详解一文 一:指针的概念 指针是一个特殊的变量,里面存储的数值是内存里的一个地址. ...
随机推荐
- Hard-Margin SVM(支持向量机)
什么是Hard-Margin SVM?指的是这个向量机只适用于“数据完全可分(seperately)”的情况. (一)什么是支持向量机? 上述三条直线,选择哪一条比较好?直觉上来说,最右面的那条直线最 ...
- mybatis系列-04-mybatis开发dao的方法
4.1 SqlSession使用范围 4.1.1 SqlSessionFactoryBuilder 通过SqlSessionFactoryBuilder创建会话工厂SqlSession ...
- 关于 终端 ls 命令 不能区分文件和目录的问题
默认的,使用ls命令来显示目录内容的时候,“终端”对于目录.可执行文件等特殊类型的文件并没有使用颜色来显示,只有使用“ls -G”时,才能显示颜色,这可真是不方便.有没有方法可以默认显示颜色呢?方法当 ...
- Spark SQL概念学习系列之SQL on Spark的简介(三)
AMPLab 将大数据分析负载分为三大类型:批量数据处理.交互式查询.实时流处理.而其中很重要的一环便是交互式查询. 大数据分析栈中需要满足用户 ad-hoc.reporting. iterative ...
- Struts2的国际化
1.概述 把在无需改写源代码即可让开发出来的应用程序能够支持多种语言和数据格式的技术称为国际化. 与国际化对应的是本地化, 指让一个具备国际化支持的应用程序支持某个特定的地区 Struts2国际化是建 ...
- linux 查看当前所在目录的全路径
有时候,使用linux的shell的时候需要查看当前位置的全路径,可以使用 pwd命令 当然,知道了该命令就可以通过man pwd来查看该命令的全部帮助手册.
- CSS构造列表
列表图片 背景列表 翻转列表 水平导航 内边距与外边距 Ul { List-style-type:none; Margin: 0; Padding: 0; } 使用图片作为列表图标 Ul { Marg ...
- C# Dictionary用法总结
转自:http://www.cnblogs.com/linlf03/archive/2011/12/09/2282574.html http://www.cnblogs.com/linzheng/ar ...
- MS-SQL Server字符串处理函数大全
MS-SQL Server字符串处理函数大全 select语句中只能使用sql函数对字段进行操作(链接sql server), select 字段1 from 表1 where 字段1.Index ...
- SQLite本地事务处理
private void toolStripButton1_Click(object sender, EventArgs e) { //判断新增的年度是否已经存在 if (HasYear()) { M ...