首页 > 代码库 > c指针-专题

c指针-专题

---指针

内存和地址怎么理解呢?

机器中有一些位置,每一个位置被称为【字节】/byte,许多现代机器上,每个字节包含8个位。更大内存单位【字】,通常包含2个或4个字节组成。

一个字包含4个字节,它的地址是什么?

他仍然只有一个地址,是最左边还是最右边的那个字节的位置,取决于机器。

机器事实-关于整型的起始位置:

在要求边界对齐(boundaryalignment)的机器上,整型存储的起始位置只能是某些特定的字节,通常是24的倍数。

变量名和地址关系?

所有高级语言的特性之一,就是通过名字而不是地址来访问内存的位置。那么名字和地址是怎么关联的呢?关联关系不是硬件所提供的,它是由编译器实现的,硬件仍然通过地址访问内存位置。

存储在内存的一个32位值,到底是整型还是浮点呢?

什么类型取决于使用方式,如果使用整型算术指令,这个值就被解释为整数,如果使用浮点型指令,他就是个浮点数。

指针变量 他的地址对应的内存中存的是什么内容?

存储是一个值,这个值,通常被当做地址来使用。

编译器怎么为变量分配地址呢?

编译器为每个变量选择存储位置。

对于 int a=112; int *d = &a;  为什么编译器最终将a的地址写入d对应的内存空间,而不是把112写入d的内存空间呢?

原因应该是这样设计灵活,且效率高,更重要的是硬件的设计如:RAM 有地址这个概念

 

符号 * 什么含义?

用来执行间接访问操作。对一个变量间接访问,声明含义?就是不把这块内存值直接使用,二十把它用作地址,把这个地址对应的内容取出。

 

指针未初始化是什么含义,有什么风险?对未初始化的指针访问会发生什么?如 int *a  ;  *a=12;

     这个操作可能会有下面情景发生

     1.  运气好 a 的初始值是非法地址,出错,终止程序,在unix系统上这个错误memory fault  , windows出现 一般保护性异常 (general  protection exception)

     2.更严重的情况,这个指针偶尔可能包含一个合法地址,即是这样的情况: 引发错误的代码,与原先用于操作那个值得代码完全不相干。

 

编译器能检测出来初始化的指针p,   *p = 12,这样,并给出警告吗?

 

标准定义的NULL指针什么含义?我怎么定义一个NULL指针变量?怎么判断一个指针是不是NULL指针。

     表示不指向任何东西。定义一个变量赋值为零。与零值比较来判断指针是不是NULL指针。

 

在机器层面NULL指针内容到底是什么呢?

    NULL 指针可能不是0,在此情况,编译器将负责零值和内部值之间的翻译和转换。

对一个NULL指针间接访问会出现什么情况?

     因编译器而异,有的不报错。有的发生错误,并终止程序。

定义一个变量怎么声明才是好习惯?

     如果你已经知道指针将被初始化为什么地址,就把它初始化为该地址,否则把它初始化为NULL

在对一个变量赋值时,编译器会校验,检查什么?

     校验,使用变量类型和应该使用的变量的类型不一致时,编译器会报错。

 

分析下 *&a = 25;  非重点

指针常量是什么能不能访问操作并赋值?

*100 = 25;  *100 就是指针常量。这条语句是非法的,需要强制类型转换如:

 *(int *) 100 = 25;

指针常量什么样情况才能用到?

     如:与输入输出设备控制器通信。

指针的指针怎么用示意图画明白?

Int a =12;

Int *b = &a;

Int **c =&b;

 

画图要点:

    1.箭头=== 是地址

    2. 虚线。表示关系

    3. 实线。 表示*访问

 

粗椭圆  当做右值使用,使用表达式的值。

粗方框  当做左值使用,指地址。

 

为什么&ch 不能用作左值?

     &ch结果会存储在某个地方/某个内存,但是我无法知道他位于何处,这个表达式,看不出他在机器中那个特定位置,所以它不是一个合法的左值。

 

什么是左值,右值?

    左值可以再“=”号左侧,也可以在等号右侧,而右值只能出现在等号右侧。

 

分析清楚一个指针表达式是左值还是右值有什么好处?

分析表达式

    Char ch = ‘a’;

    Char *cp = &ch;

  示意图要求1. 标出求值的顺序,2中间专题是虚线,椭圆,方框

   表达式

     &ch

 Cp

&cp

*cp

*cp+1

*(cp+1)

++cp

Cp++

*++cp

*cp++

++*cp

(*cp)++

++*++cp

++*cp++

 

实例,strlen.c 必会默写

 

指针+/- 整数  在什么情况下可以这样?

     只能用于指向数组中某个元素的指针,也适用于使用malloc函数动态分配获得的内存。

指针指向数组最后一个元素的后面的那个内存位置,有什么问题?允许这样,一般不允许对指向这个位置的指针进行间接访问。

 

指针-指针什么情况下允许这样?

只有两个指针指向同一个数组中的元素时,相减的结果是 ptrdiff_t(有符号整数类型)

 

<  <=   >  >= 可以比较的两个指针有什么限制?

     他们都指向同一个数组中的元素,但是两个任意的指针可以执行相等或不相等测试。

 

 

---数组

 

 

数组名和指针是一回事吗?他们之间有什么区别联系呢?

   答: 数组和指针有着本质的不同,指针是一个标量值。数组名却有很多属性如:数组元素的数量。只有当数组名在表达式中使用时,编译器才会为它产生一个指针常量。

   只有两种场合下,数组名并不用指针常量表示 1. Sizeof 操作符  

   2.  Int a[10];  int *c; c = &a[0];     ???

 

一个一维数组我怎么才能访问他的元素; 都有哪几种方式?

      有两种方式如: intb[10];      1.   b[3]  ;     2.    *(b+3)

         即是 1. Array[subscript];    2.*(array+ (subscript));

 

小练习,指针和数组  写出对应的表达式

Int array[10];

Int *ap = array+2;

ap   ; *ap  ;  ap[0];  ap+6;  *ap+6;    *(ap+6);   ap[6] ; &ap ; ap[-1];   ap[9];

2[array];

 

为什么编译器通常不进行下标检查?

   下标引用可以作用于任意的指针,而不仅仅是数组名。  耗时麻烦

 

如果可以互换的使用指针表达式和下标表达式,我应该使用哪个呢?

      下标绝不会比指针更有效率,但指针有时会比下标更有效率。

举个例子为什么指针表达式比下标方式有时的效率更高?

      这个例子完成的功能是将数组中的所有元素都设置为0

      。。。。

 

怎么来分析指针的效率呢?

查看代码产生的汇编代码,不断调整指针和下标的使用方式,来比较不同的汇编代码

 

关于是写指针表达式还是下标呢好的习惯:

大多数情况下,不要仅仅为了几十微妙的执行时间,而写一些莫名其妙的代码。

 

把数组的名字传递给函数做形参,到底是传值,还是传地址?

      它是传值方式,传递的是该指针的拷贝。

      举个例子。。。。

 

在函数声明中怎么传递一维数组参数呢?

      Int  strlen(char *string);

      Int  strlen(char string[]);

 

      形参只是一个指针。

 

数组怎么初始化呢?

     大括号: int  vector[5] = {10,20,30,40,50}

     数组初始化的方式类似于标量变量的初始化方式,,也即是取决于他们的存储类型,分为静态和动态初始化。

不完整的初始化也是可以的

如:in   vector[5] ;

自动计算数组长度。

Int vector[] = {1,2,3}

 

字符数组该怎么初始化呢?

    Char message[] = {‘H’,’e’,’l’,’l’,’o’,0};

    也可以: charmessage[]  =  “Hello”;

字符数组和字符串常量在声明定义上有什么不同?

    Char  message1[] = “Hello”;

    Char  *message2 = “Hello”;

图呢? 画下呀todo;

 

定义一个数组,里边都是字符常量,该怎么理解呢?

     如: char  const keyword[] =  {

“do”,

“for”,

“if”,

 “register”,

“switch”,

 “while”

}

 

画个图吧这样好理解  todo;   这个是指针数组

 

 

 

一个二维数组存储的顺序到底怎样?

 

Storage order ,c中多维数组的元素存储顺序按照最右边下表率先变化的原则。

这个顺序是标准定义的。

举个例子让事实来印证这个结论。

     Int  matrix[6][10];

     Int  *mp;

      ...

     Mp = &matrix[3][8];

 Printf(“First value is %d\n”,*mp);

 Printf(“Second value is %d\n”,*++mp);

 Printf(“Third value is %d\n”,*++mp);

 

二维数组的名字从指针读法该怎么读呢,如int matrix[3][10];?

   

         Matrix 是一个指向一个包含10个整型元素的数组的 指针。

 

分析下 :声明如: int  matrix[3][10];  matrix[1][5] 这样的表达式 对应的指针形式怎么写?

   *matrix+1)  +5

   *(*(matrix +1) +5)  

    这里有个概念 叫: 指向数组的指针 。如 matrix叫指向数组的指针。

 

 

数组 是感觉是 是种数据类型,不同于 整型,浮点,字符。为什么不单独使用一个关键字 来清晰说这个情况呢?

 

怎么声明一个,在初始化时与数组(多维) 有关的指针,都有哪些初始化方式,哪些是不对的?

Int  vector[10] ,*vp = vector;

Int matrix[3][10];

    Int  *mp = matrix;      //xxx这种初始化方式或声明是不对的。他的本意是声明一个指向数组的指针。

Int (*p)[10] = matrix;  //这是正确的,声明一个指向数组的指针。

这样的指针是逐行在数组中移动。

我想逐个方位整型元素该怎么声明?

Int *pi = &matrix[0][0];

Int *pi = matrix[0];

 

错误的声明如:int  (*p)[] = matrix;

 

 

我该怎么给函数定义传一个多维数组的参数?

 

先来说说一个数组的函数原型可以怎么写吧。

  Int  vector[10];

   .....

   Funcl(vector);

  

    像下面两种都是可以的

   Void funcl(int *vec);

   Voidfuncl(int vec[]);

 

  再来说下多维数组作为参数的情况

Int   matrix[3][10];

......

Func2(matrix);

可以使用下面两种方式声明

Void func2(int  (*mat)[10]);

Void func2(int mat[][10]);

 

结论要点:编译器必须知道第二个以及以后各维的长度才能对各下标进行求值,

 

切记不对的方式如下:

   Void func2(int **mat);   //这个是指向整型指针的指针,他和指向整型数组的指针不是一回事。

 

 

    多维数组怎么初始化呢?

 

有两种形式 1.

        Int matrix[2][3] = {100, 101 ,102, 110, 111,112};

         2:

           Matrix[0][0] = 100;

           Matrix[0][1] = 101;

           Matrix[0][2] = 102;

           Matrix[1][0] = 110;

           Matrix[1][1] = 111;

           Matrix[1][2] = 112;

    多维数组中使用多个大括号层级分明的声明比较好

          Int  three_dim[2][3][5] = {

{

  {000,001, 002, 003 ,004},

  { 010, 011, 012, 013, 014},

  { 020, 021, 022, 023, 024}

},

{

  { 100, 101, 102, 103,  104},

  { 110, 111, 112, 113, 114},

  {120, 121, 122, 123, 124}

}

}

 

花括号有什么好处呢?

   1. 起到路标的作用

   2,不完整的初始化列表,很有用

 

多维数组为什么只有第一维可以缺省呢?如: int two_dim[][5] = { {},{},{}}

   原则上编译器可以让其他维缺省,但是需要每个列表中的子初始值至少有一个整出现。最终标准这种了,要求其他维大小显示提供,好处是所有的初始值列表都无需完整呀,这个好处更有用,哈哈。

 

指针数组怎么声明呢?

    Int  *api[10];

 

什么情况下会用到指针数组呢?

   写个例子,判断参数是否与一个关键字列表中的任何单词匹配,

   并返回匹配的索引值,如果未找到匹配,函数返回-1

Char const   keyword[] = {

“do”,

“for”,

“register”

};

也可以把关键字存储在一个矩阵中,

 Char const  keyword[][9] = {

“do”,

“for”,

“register”

 }

 

 好的习惯,通常选择指针数组,注意末尾NULL

   Char const *keyword[] = {

“do”,

“for”,

 “if”,

 NULL

    }

    For( kwp = keyword_table ; *kwp !=NULL;  kwp++)

 

   总结 p166

 

十三----高级指针

     复杂的表达式样的声明怎么理解?

    C  语言 设计为 推论声明。用于声明的表达式和普通的表达式再求值时,所使用的规则相同。

     分析几个复杂的声明?

         Int   *f();    //返回值,是一个指向整型的指针

         Int   (*f)();   //f  函数指针 ,,,指向一个函数。

         Int   *(*f)();

 

         Int  (*f[])( int, float);

  Int  *(*f[])( int ,  float);

 

 函数指针是合法的吗?怎么声明?

 Int  (*f)();

 

什么样的声明是非法的,为什么?

    Int   f()[];

     这个是非法的,他的返回值是一个整型数组。注意!函数

只能返回标量值,不能返回数组。

    Int  f[]();  //是一个数组, 他的元素是函数,这个函数他的返回值是整型。

   这个声明是非法的,因为数组元素必须具有相同的长度,但不同的函数显然可能具有不同的长度。   

 

 

函数指针怎么初始化  和调用呢?

int f(int);  

Int   (*pf)(int) = &f;   或者  int  (*pf)(int ) = f;

   注意在函数指针的初始化之前具有f的原型很重要,否则编译器无法检查f的类型是否与pf所指向类型一致。

注意,初始化的表达式中的&操作符是可选的,因为函数名被使用时总是由编译器把它转化为函数指针。!!!

Int  ans;

Ans =f(25);    //函数名f首先被转换为一个函数指针。即是隐式间接访问

Ans = (*pf)(25);

Ans =pf(25);  //画画图 todo;

 

函数指针,这样的指针什么情况下才能用到?

    1.吧函数指针作为参数传递给函数。

     与函数指针,,相关的一个概念, 回调函数。

 

   例子: 在一个单链表中查找一个指定值?   两个维度来分析这个例子(回调函数,函数指针)

查的程序怎么写的,有对比才知道好!!

        他的参数是一个指向链表第一个节点的指针,一个指向我们需要查找值的指针,和一个函数指针,它指向的函数用于比较存储于链表中的类型的值。

 

      Search.c   类型无关的链表查找

 

 #include  <stdio.h>

#include “node.h”

 

Node * search_list(Node * node,  void const  *value,

        Int( *compare)( void const *, void const *))

{

While(node  != NULL){

If(compare (&node->value,value)  == 0 )

    Break;

Node  =   node->link;

}

 

Return node;

}    

 

 

Int compare_ints( void   const *a ,void  const *b)

{

If( *(int*)a  == *(int  *)b)

      Return 0;

Else 

      Return 1;

}

 

什么情况下要使用回调函数?

    如果你所编写的函数必须能够在不同的时刻执行不同类型的工作或者  执行只能由函数调用者定义的工作。可以使用这个技巧。

 

2.  转移表

     举个计算器例子?  查的方法就是  switch case......

 

Double   add   (double,  double) ;

Double   sub   (double,  double) ;

Double   mul   (double,  double) ;

Double   div    (double,  double) ;

 

.......

Double (*oper_func[])(double ,double  )  = {

Add,sub,mul,div,...

}

 

调用:  result  = oper_func[oper](op1,op2);

 

 注意下标要处于合法范围!!

 

 

指向指针的指针都有哪些用途呢?

   命令行参数是一个应用

例子cmd_line.c  要能熟练写出。

 

 

字符串常量他的表达式是什么,如何理解?

  xyz 他的表达式是个指针常量, 指向第一个字符,也可以进行下标引用,间接访问,及指针运算。

    *”xyz”

 

xyz[2]

 

13.4   神秘函数不太理解???

 

课后练习也是很有必要做的。

 

参考:《c和指针》

 

 

 

 

 

 

 

 


c指针-专题