C语言数组与指针详解

来源:互联网 发布:淘宝seo是什么意思 编辑:程序博客网 时间:2024/06/05 06:29

1. 晕指针,唐式偏方一:“投石问路”

#include <stdio.h>

int main(void)

{

       char* str = "ABCDEFGHIJKL";

       int* pInt = (int*)str;

       printf("%c\n%c\n",*(str+1),*(char *)(pInt+1));

       return 0;

}

       上面的程序如果你要是灰常EASY的回答出来,那你的功底已经不错了!心病是估计没有了,可以进入吃药保养阶段了(做题)。

如果上面的题看起来就特别费劲,看来你晕的不轻,得治。要不问题会很严重。

 

基于上面的小测试,进入唐式第二方:“理气静心”

       我们先看把C指针的基础打好。在这之前先来复习下变量和常量,有的同学会问,这是为什么,我只能告诉你,你晕C指针,是因为你从刚开始有问题就没有重视,或者你忽略了问题的严重性,导致现在的情况,这也没有办法,中国的教育就不重视这一块,说实话,大学里讲C语言的,很多都没有太多的讲到变量和常量,这是很XXX的。我们开始治疗。

 

  •  常量:其值不发生改变的量称谓常量。常量又称为字面量,表述常数。它们可以和数据类型结合起来分类,比如:整形常量,浮点型常量,字符常量= =,常量是可以不经过定义和初始化,而直接引用的

常量又分为:直接常量和符号常量。

直接常量又叫做:字面常量。如12,0,4.6,‘a‘,“abcd”

符号常量如宏定义的:#define PI 3.14

特点:常量的值在其作用域内不会发生改变,也不能再被赋值。其在出现时就被当作一个立即数来使用。也就是说,只能被访问,被读,而不能被写,被赋值。

 

  •  变量:其值可以改变的量称这变量。一个变量应该有一个名字,在内存中占据一定的存储单元。变量在使用前必须要定义

 

  •  变量名和变量的值:

变量名是在,变量的声明的时候,该名字就和内存中一块地址绑定在一起了。可以通过变量名直接找到对应的内存区域,也可以通过地址找到其内存区域。因此有了引入指针的依据。

变量的值是变量所对应的内存区域内存放的二进制序列。当该变量被声明成整形时,内存区域的二进制序列被以整形的形式翻译出来。比如:int a = 97; 其在内存中是以97的二进制形式存放的,当使用时,他会被以10进制形式表现出来。同样的char a = ‘a’; a的ASIIC码是97,也是以97的二进制存放的,使用时,会被以字符a的形式表现出来。

如果变量是一个指针变量,那么指针变量里的二进制序列被翻译成一个地址,

比如:

       int a = 10;

       int * p;

       p = &a;

这里的指针变量p的值是a的地址(p = &a),它是什么啊?看下图:

声明了一个变量a,它是整型,被赋值为10(它的值被翻译成整数),要形成这种思维,时间长了你就知道这样做的好处了,又声明了一个指针变量p,它是Int类型的(它指向的地址里面要装Int),然后将a变量的地址(ox2c406b24)给了p,这儿注意下。现在访问a里面的值有了两种方式(其实本来也有这两种),一个是通过变量名a(绑定的),一个是通过地址ox2c406b24,地址ox2c406b24给了p了,p指针变量(指针变量是变量,这个思维很重要)里面存放的是ox2c406b24(a的地址),那么现在访问a可以通过:

       printf("%d\n", a);    //通过变量名

       printf("%d\n", *p);    //通过指针变量

如果你现在上面的都很明白了,那你有了晕指针好转的迹象,只是迹象,(迹象だけです^_^)。现在验证下是不是真的有好转:


1、

char ch = 'a';

int a = (int)ch;

printf("%d %c\n", a, ch);

ch是什么? ch 里面是什么? a是什么? a里面又是什么?打印什么?

2、

int add = 0x123456;

int * p = (int*)add;

add是什么?add里是什么? P是什么? P里面是什么? *p 又是什么?

嘿嘿,晕不?别慌,再来。。。

3、

#define PI 3.14

int a = PI;

printf("%d\n", a);

上面的程序有没有问题?

4、

#define PI 3.14

printf("%d\n", PI);

程序有没有问题?

5、

#define PI 3.14

int a = PI;

PI = 1.85;         

int b = PI;

printf("%d%d\n", a, b);

程序有没有问题?

 

差不多头疼的不行了吧,没事,这是药劲,好药都这样。

最后一个:

6、

char *str = "abcdef";

printf("%s\n", str);

*str = "fedcba";

printf("%s\n", str);

str[2] = 'C';     //修改第三个字符为大写

printf("%s\n", str);

程序有没有问题?

好,第一方到此为止,老中医要休息会。


上一方药劲比较重,年轻人吗。现在给你们点解药:

1、主要是测试类型转换,还有对变量的理解是否到位。

2、对变量的值的理解是否到位

3、对常量,宏替换的理解是否到位

4、对宏替换的理解是否到位

5、能否修改常量的值?

6、能否修改字符串常量的值?

通过上面我们可以学到以下内容,重点,记下:

       1、不管什么常量,其值是编译是固定好的,不能再被改变

       2、变量里的值,和其数据类型没有关系,它只是一个二进制序列,不要将电脑想的多聪明,它只认识0, 1,只不过,这一堆01被其类型限定了其代表的意义,类型为整形就是其值,指针地址类型,就是内存里的一个地址,字符型,就是其无符号整形代表的ASCII码。

      3、字符串,是常量(字符指针指向一字符串,不是数组,数组和指针的区别在后面),其值不能再被改变,char * str = "abcdef";这行代码的意思是告诉编译器:老编啊,我这儿有个抽屉(指针变量str),你给我找个房间(存放字符串的内存空间),找到后,把钥匙给我放到这个抽屉里。老编去找客房经理,找到后问:有没有空房间啊,有的话给我一间,一哥们要开房,客房经理去查房间入住情况(内存管理),最后说,这儿有一间,它是老总的房间,你要不先用着,你用没事,你可别乱动里面的东西,你要是动的话,估计你那哥们就被KILL掉,老板是黑社会的,不过,你用没事。于是,老编将老总房间钥匙给我放到了抽屉(指针变量str)里,这样,我打开抽屉(指针变量),拿出房间(内存空间)钥匙(指针),去开房了,老编告诉的,别乱动,用没事(读取),一定不能破坏里面的结构(修改数据内容),要是乱动(修改数据),会出乱子的(段错误),然后你会被KILL的(异常结束程序)。如果有两个人,就会Double kill,老板就GOD LIKE了。

 

明白了常量和变量的区别后,再进行后面的学习就轻松点了!下面我们来看下指针的双胞胎哥们,数组。一般晕车人里面有很多也晕飞机,晕船,所以呢,晕指针的,也一般都会晕数组。

 

  •  数组:我们先咬文嚼字一下,数指数据,组,就是小组,由数据组成的小组,就叫数组(学计算机的,不光要学好计算机,也要学好语文,偶尔还要懂点医学,你要是经常看计算机图书,你看台湾人写的书,都有很多文言文在里面,我们这点做的完全没有台胞做的好,所以人家计算机牛啊)。

数组有几个特点一定要注意,看到数组就要想到:

1、  数组里面的数据类型是相同的,小组里面的成员肯定要一样的啊,驴堆里站一马,驴马不分,鸡窝里蹲一丹顶鹤,鹤立鸡群,这都是不合群的东西,计算机里面也讲“和谐”。我们顺便看下数据的基本类型:int float double char 这些是基本类型,所以它们可以存在以下类似数组:int a[10], float f[10], doule d[10], char str[10]; 它们每一个都有10个元素,每一个元素的类型都是其前面声明的类型。我们是学嵌入式的,我们不能光看到表面的东西,我们要看到底,好东西是走了光才叫好,光有好东西,别人不知道也不行。其实数组在内存中是连续分配的,如下图:

定义了一char型数组a,它有6个元素,分别是'A', 'B', 'C', 'D', 'E', 'F',它们在内存里面是连续存放的,每个元素占用一个字节。 强烈谴责那些将'A' 当成"A"的人,前者是单个字符,后者是字符串,鄙视那些,问“字符和字符串有什么区别?”的人,不知道字符和字符串的区别,那你吃过羊肉串吗?单个羊肉块能叫串吗,多个羊肉块串起来才叫串,所以字符是单个,字符串可以是多个字符组成的数组(最后有一个结束符号\0),羊肉块串起来一烤就叫烤羊肉串,字符串一拷,叫拷贝串,学计算机哪有那么简单,要是我开个学校,入学前要体检,测视力,量身高体重,....,三围什么的,不行的PASS。

       仔细看上面的图,每一个字符都有一个地址,它们的跨度是1(字节),数组的每个元素都可以通过下标来访问,下标(index, for循环变量经常用i就是因为这个东西)其实就是他在数组中的位置,也就是他的号,拉10个人过来,报数,1,2,3.....,只不过,C语言里数组的下标是从0开始的,在计算机里面能访问的最小单位就是字节了,也就是地址只能找到以字节为单位,不能再精确了。数组名a和变量名道理上是一样的,在编译时就和数组的首地址绑定上了,a就是数组的首地址,变量名和数组名其实都是方便人们记忆而取的代号,它在代码反汇编后,其实不存在变量名的,回想下,访问数据有两种方式,既然反汇编后的代码不存在变量名,只能通过那种方式访问数据了,那就是地址。数组中的每一个元素,可以被看成一个变量(回想下变量的特点),因此其可以被读,写,修改,爱怎么得瑟怎么得瑟,你只要不把房间拆了(内存空间),怎么折腾都行。每一个元素的地址都可以通过首地址的偏移量(offset这个词记住,四级里没有)来算出来,这个偏移量说白了就是下标了。比如上面的图中:'C'所在地址0x28c5,相对首地址0x28c3的偏移量是2,那a[2]也可以访问'C'了,注意一点,a代表数组代表数组的首地址代表数组第一个元素的地址,这“三个代表”一定要记住。那a+1呢,a是三个代表,那它是第一代表还是第二个代表,还是第三个代表呢?这儿的a应该是第一个元素地址的意思表示的意思应该是&a[0],a+1是个地址的算术运算,而数组是个一维数组,数组中每个元素都是一个字符,a+1就是a当前地址0x28c3的下一个元素的地址0x28c4(结合图来看),也就是'B'的地址,如果a是一个二维数组名的话,那么二维数组被编译器理解为一个一维数组,一维数组里的每一个元素是一个一维数组,有点乱,别慌,屡一下,看下图:

a是个二维数组,它有3X4=12个字符元素,而编译器将它认为是一个一维数组,它有三个元素,分别是a[0], a[1], a[2], 每一个元素是一个含有4个字符的数组,那么a+1的话,是&a[0]的地址,a[0]是一个一给数组,取了一个数组的地址,再加1 ,肯定就是下一个数组的地址了,就是a[1]上图,那么这个时候它的地址增加可不再是1了,而是列数4,也就是说是a这个二维数组中元素(一维数组)的长度。

       回到前面那个图:a[5] - a[3] = ? 地址的运算,这里算的也是元素的相对偏移量,结果当然是2,虽然0x28c8- 0x28c6 = 2结果也是2,不过意义不一样,如果数组类型换成int a[10], 那么a[5] - a[3] 还是2,不是8,这个直接用下标相减就对了。

       因此我们可以总结一下,指针相加减时,要看类型,打狗看主人,指针加减看类型,其运算的值n*sizeof(类型),比如:字符型地址加1 ,其实地址加也是sizeof(char) = 1,整形地址加1,地址加sizeof(int) = 4,结构体数组中,地址加1,地址加sizeof(结构体)。

 

       通过上面的分析可以看出,数组有很多地方很相似,其实,编译器这哥们处理数组的时候就是将其看成指针来处理的,没有办法,编译器只认地址,变量名一直都是被编译器藐视的。

       看一个程序图:

程序图这个名词是我自创的,因为好多东西说不清,道不明,一个图全搞定。

上面的执行结果是什么?已经很清楚了。休息会。

       上面的例子结果是:B和E


转载:http://blog.csdn.net/mr_raptor/article/details/6844691

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 耐克标志开胶了怎么办 鞋开胶了怎么办不用胶 gta按home没反应怎么办 gta5线上车没了怎么办 吃了粘壳的鸡蛋怎么办 gta5短信删错了怎么办 电风扇2档3档开好关不管用怎么办 gta5把车替换了怎么办 gta5任务完成后卡了怎么办 gta5车被扣押了怎么办 侠盗猎车手5卡怎么办 英雄联盟转区后没法快捷施法怎么办 欠太多人的钱怎么办 我欠了很多钱怎么办 输了那么多钱我该怎么办 家里欠了钱我该怎么办 欠了好多钱我该怎么办 赌球输了好几千怎么办 欠信用卡的人死了怎么办 欠别人钱人死了怎么办 别人欠我钱人死了怎么办 美国生娃孩子怎么办医保 黑在美国病了怎么办 在外打工房租太贵怎么办 在外面打工房租租不起怎么办 买车型号错了怎么办 沃出行不退押金怎么办 钢铁雄心4人力0怎么办 钢铁雄心4没工厂怎么办 钢铁雄心4锁区怎么办 qq超市金币满了怎么办 旋转轮胎2车翻了怎么办 轮胎里面卡进小石子应该怎么办 手游吃鸡模拟器已经到达上限怎么办 逆战场手游模拟器黑屏怎么办 欧洲卡车2翻车了怎么办 卧式注塑机锁模久了打不开怎么办 欧洲卡车2困了怎么办 欧卡2没油了怎么办 欧卡2车卡住了怎么办 欧卡2车子卡住了怎么办