c语言基础2指针
---------
指针的详解
---------
指针的定是
int * a;
b = 1;
a=&b
可以看出来,a这个指针变量存的是b的地址。
可以变形为int *a=&b
*d 表示访问位置100,并取得里面的值。
一个常见的错误,int *a ;*a= 12;但是a究竟指向哪里呢,由于我们没有对a进行初始化,所以我们没有办法预测12这个值究竟存在哪里。如果变量是静态的
它会被初始化为0,如果是自动的它根本不会被初始化,无论哪种情况,声明一个 指向整型的指针都不会创建用于存储整型的内存空间。
所以程序执行这个赋值操作,将会发生什么呢?如果你运气好,a的赋值是个非法地址,这样赋值语句将会出错
标准定义了空指针,NULL指针,它作为一种变量特殊的指针变量,表示不指向任何东西,要使一个指针变量变为NULL,你可以给他附一个零值。为了测试一个指针是否为空
可以将他与零值进行比较,之所以选择零值是因为一种源代码约定,就机器语言而言,NULL指针的实际值可能与此不同,在这种情况下,编译器负责将零值和
内部值之间进行转换。
NULL指针的概念非常有用,因为他给了你一种方法,表示某个特定的指针目前并未指向任何东西。例如一个用于在某个数组中查找某个特定值的函数可能返回一个
指向查找到得数组元素的指针。如果该数组不包含指定条件的值,函数就返回一个NULL指针,这个技巧允许返回值传达两个不同片段的信息。首先,由没有
找到元素,其次,如果找到他是哪个元素?
尽管这个技巧在c程序中极为常用,但是他不符合软件工程的原则。用一个单一的值表示两种不同的意思是件非常危险的事情,因为将来可能造成无法弄清哪个才是
真正的意图。在大型的程序中,这个问题更为严重,因为你不可能对整个设计一览无余。一种更为安全的策略是让函数返回两个独立的值。首先是个状态值,
是用于查找是否成功。其次,是个指针,当状态值提示查找成功时,它指向的就是查找到得值。
对指针进行解引用操作可以获得它所指向的值,但从定义上看,NULL指针并未指向任何东西。因此对一个空指针进行解引用操作是非法的。在对指针进行解引用操作
之前首先要确保它并非NULL指针。
警告:如果对一个NULL指针进行间接访问会发生什么情况呢?它的结果因编译器而异,在有的编译器中会访问内存的位置零编译器能够确保位置零没有存储任何
变量,但机器并未妨碍你访问或修改这个位置。这种行为时非常不幸的。因为程序包含了一个错误,但机器却隐匿了它的症状,这样就使这个错误更加难找。
在其他机器上,对NULL指针进行间接访问或引发一个错误,并终止程序。宣布这个错误比隐藏这个错误要好的多,因为程序员更容易修改这个错误。
如果所有的指针变量(而不仅仅是位于静态内存中的指针变量)能够自动初始化为NULL,那是在是件幸事,但事实并非如此。不论你的机器对解引用NULL
指针这种行为作何反应,对所有的指针变量进行显示的初始化是种好做法。如果你已经知道指针将被初始化为什么地址,将他初始化为该地址,否则初始化为NULL
风格良好的程序会在指针解引用之前对它进行检查,这种初始化策略可以节省大量的调试时间
--------指针变量可以作为左值并不是因为他们是指针而是因为他们是变量。对指针变量进行间接访问表示我们应该访问指针所指向的位置。间接访问指定了
一个特定的内存位置,这样我们可以把间接访问表达式的结果作为左值使用。
*&a=25:答案是把25赋给a的值。首先&操作符取得a的地址,它是一个指针变量(注意,使用这个指针变量并不需要知道他的实际值。)。接着,*操作符访问其操作数所
指向的地址。在这个表达式中,操作数是a的地址,所以值25就存储于a中。
这条语句和简单的使用a=25有区别吗?从功能说是相同的,但是涉及更多的操作,除非编译器知道你在干什么并丢弃额外的操作,否则他所产生的目标代码
将会更大、更慢。更糟糕的是,这些额外的操作符会使源代码的可读性变差
------------
指针常量
------------
假定变量a存储于位置100下面这条语句的作用是什么?
*100=25
它看上去是把25赋给a因为a是位置100所存储的变量。但是这是错误的。这条语句实际上非法的,因为字面值100的类型是整型,而间接访问操作只能
作用于指针类型表达式。如果确实想把25存于位置100,你必须使用强制类型转换。
*(int *)100=25
强制类型转换把100从整型值强制转换为指向整型的指针,这样对它进行间接的访问就变成合法的了。
----------
指针的指针
-----------
例子
int a=12;
int *b=&a;
int **c=&b;
*操作符是从右向左的结核性所以这个表达式相当于*(*c)我们必须从里向外逐层求值。*c访问c所指向的位置,我们知道他是变量b。第二个间接访问操作符
访问这个位置所指向的地址,也就是变量a
char ch=‘q’;
char *cp=&ch;
*cp+1 由于*的优先级高于+所以先执行间接访问操作,再执行+
*(cp+1)指针加法运算的结果是个右值,因为他的存储位置未清晰定义。如果没有间接访问操作,这个表达式将不是一个合法的左值。然而间接访问跟随
指针访问一个特定的位置。这样*(cp+1)可以作为左值使用,尽管cp+1本身并不是左值。间接访问操作符是为少数几个其结果为左值的操作符之一
------------
算术运算
------------
c指针的运算只限于两种形式。第一种形式是:指针+-整数
标准定义这种形式只能用于指向数组中某个元素的指针
第二种类型
指针-指针
只有当两个指针都指向同一个数组中的元素时,才允许指针-指针
----------
关系运算
----------
对指针执行关系运算也是有限制的。
总结
计算机内存中的每个位置都由一个地址标识。通常,邻近的内存组合成一个数组,这样就允许存储更大范围的值。指针就是它的值表示内存地址的变量
指针变量的值并非它所指向的内存位置所存储的值。我们必须使用间接访问来获取它所指向位置的值。对于一个“指向整型的指针”施加间接访问操作的
结果将是一个整型值。
声明一个指针变量并不会自动分配任何内存。在对指针进行间接访问前,指针必须进行初始化或者使他指向现有的内存或者给他分配动态内存。对为初始化的
指针变量执行间接访问操作是非法的,而且这种错误常常难以检测。其结果往往是一个相关的值被修改。这种错误是难被调试发现的。
除了NULL指针之外,再也没有任何内建的记法来表示指针常量。在极少见的情况下我们偶尔需要使用指针常量,这是我们可以通过把一个整型值强制
转换为指针类型来创建它。
在指针上可以执行一些有限的算术运算
然而指针只有作用于数组中其结果才是可预测的。对任何并非指向数组元素的指针执行算数运算是非法的。如果一个指针减去一个整数后,运算结果
产生的指针所指向的位置在数组第一个元素之前那么他是非法的。
如果两个指针在一个数组之内是可以进行相减的。
任何指针之间都可以进行比较测试他们相等还是不等。如果两个指针都指向同一个数组中的元素那么他们之间可以执行</<=/>=等关系运算,用于判断他们在
数组中的相对位置,对两个不相关的指针执行关系运算,其结果是定义的。
-------------
警告的总结
1.错误的对一个未初始化的指针变量进行解引用
2.错误的对一个NULL指针进行解引用
3.向函数错误的传递NULL指针
4.未检测到指针表达式的错误,从而导致不可预料的结果。
5.对一个指针进行减法运算,使他非法的指向了数组第一个元素的前面得位置
--------------
==============
提示的总结
==============
一个指针应该只有一种意思
如果指针并不指向任何有意义的东西,就把它设置为NULL.
为什么char *p=“aaaa”;
printf(“%s\n”,p);正确
printf("%s\n",*p);出现段错误
首先对于一个数组而言,变量名a,表示一个这个数组的引用,
也就是首地址。对已指针p表示的是指针当前的地址。*p是这个地址下的值。
对于a[0],这个元素而言,他是数组的第一个元素,它现在表示的是一个值,
用数组下标表示,如果它想赋给一个指针的话,那么就必须使用地址符&,即
p=&a[0],这里的p是一个指针
int *i;
int a=10;
i=&a;
printf("%d\n",*i);正确
printf("%d\n",i);打印地址
这里的i是一个指针 i表示首地址,%d打印的是值所以需要使用i的间接访问