参数是由右往左入栈的,内存单元的指针和内存

作者: 编程  发布:2019-08-28

几个关于数组、指针的易错点,数组,指针易错

指针是某个变量的地址,是一个地址

指针变量是存放指针(某个变量地址)的变量,是一个变量。

指针变量是一个变量,也有它自己的地址;这几句话对理解指针非常有帮助。

举例: 

int c = 76;
int *pointer = &c;

普通变量 c 和 指针变量 pointer 在内存中的形式如下图:

9159.com 1

指针变量pointer 有它自己在内存中的地址,见上图黄色区域,指针变量的值存放着一个地址,见上图红色方框中的蓝色方框中的地址,这个地址指向变量c,也即这个地址和变量c的地址相等;

我们把c,c的地址,*pointer,pointer,pointer的地址分别输出,见下列代码:

cout << c << endl;         //输出:76                                         
cout << *pointer << endl;  //输出:76                                         
cout << hex << &c << endl; //输出:0x0012FF74                          
cout << pointer << endl;   //输出:0x0012FF74                          
cout << &pointer << endl;  //输出:0x0012FF90                          

 

 #---------------------------------------------------------------------------------#

指针基类型的作用:当用指针遍历时, 1等于几个byte取决与指针基类型;

举例:

9159.com 2

如上图,指针p2 指向某个int值,int值占据4个byte大小(有些环境下占2个byte),那么p2 的值是在p2的值的基础之上加上4个byte找到的。

 

 #---------------------------------------------------------------------------------#

数组与指针

数组名相当于指向数组第一个元素的指针。

对于int a[4] = {1,3,5,7};

a是指向数组第一个元素的指针,即a相当于&a[0];

&a是"指向数组"的指针,&a 1将跨越16个字节;

  &a相当于管辖范围上升了一级;

*a是数组的第一个元素a[0], 即*a等价于a[0];

  *a相当于管辖范围下降了一级;

来看代码,这个例子我问过两位师兄,都没能答对。注意标红色的两条输出的区别。

9159.com 3

#include<iostream>
#include<iomanip>
using namespace std;

int main()
{
  int a[4] = {1,3,5,7};
  cout << hex << a << endl;   //输出:0028F7C4
  cout << &a << endl;         //输出:0028F7C4
  cout << &a 1 << endl;       //输出:0028F7D4
  cout << *(&a) << endl;      //输出:0028F7C4
  cout << *(&a) 1 << endl;    //输出:0028F7C8
}

9159.com 4

9159.com 5这是数组a在内存中的存放形式,非常有助于理解。

 

如果换成二维数组,情况也类似,只是稍微复杂些;

9159.com 6

来看个例子:

9159.com 7

#include<iostream>
#include<iomanip>
using namespace std;

int main()
{
  int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
  cout << hex << a << endl;   //输出:0xbf870830
  cout << &a[0] << endl;      //输出:0xbf870830

  cout << a 1 << endl;        //输出:0xbf870840
  cout << &a[0] 1 << endl;    //输出:0xbf870840

  cout << *a << endl;         //输出:0xbf870830
  cout << a[0] << endl;       //输出:0xbf870830
  cout << &a[0][0] << endl;   //输出:0xbf870830

  cout << *a 1 << endl;       //输出:0xbf870834
  cout << a[0] 1 << endl;     //输出:0xbf870834
  cout << &a[0][0] 1 << endl; //输出:0xbf870834

  cout << &a 1 << endl;       //输出:0xbf870860
}

9159.com 8

 

第一次做这个题目几乎很难都答对,但知道了规则之后就都能理解了,要更深入了解数组跟指针的关系和区别的话还得看其他的书。

小结一下:

数组名相当于指向数组第一个元素的指针。这里第一个元素不单单只一个数,可能是一个小数组(如上例);

对于二维数组a,见下图;

a相当于指向数组的第0行那个小数组;

*a等价于a[0], 相当于让a下降了一级;

&a表示“指向二维数组”的指针,相当于上升的一级;

9159.com 9

 

 #---------------------------------------------------------------------------------#

指针用作函数返回值时,必须保证返回的地址是有意义的!

举例:

9159.com 10

#include<iostream>
using namespace std;

int *getInt1()
{
  int value1 = 20;
  return &value1;
}

int *getInt2()
{
  int value2 = 30;
  return &value2;
}

int main()
{
  int *p, *q;
  p = getInt1();
  q = getInt2();

  cout << *p << endl; //输出: 30
  return 0;
}

9159.com 11

上述代码,编译出现警告。指针变量p存放了一个局部变量的地址,而局部变量的作用域只在函数内部,函数调用结束后即被释放(这半句话的表述不够准确,水平有限,暂时只能这么描述)。

解决办法: 将局部变量变为静态局部变量即可。或者也可以设为全局变量(不过这明显不是个好主意)。

int *getInt1()
{
  int static value1 = 20;
  return &value1;
}

指针是某个变量的地址,是一个地址 。 指针变量 是存放指针(某个变量地址)的变量,是一个...

例外还有一个概念:直接访问:就是按照变量地址存取变量值的方式,间接访问:将变量i的地址存放到临沂个变量中。举个例子:你跟小三约好去开房,有两种方式见到她,第一种就是小三饥渴的不行,直接和你约见面的地点,打开门就看见她。这叫直接访问,另外一种就是,小三说,想跟你玩个游戏,当你打开见面的房门里边有个纸条,上边写着,今天来点刺激的,写下了一个地址,xx乐园魔天伦28号。于是你就是哪里找她了。这就叫间接访问。

10.3.2 通过指针引用数组元素

C语言规定:如果指针变量p已指向数组中的一个元素,则p 1指向同一数组中的下一个元素。

引入指针变量后,就可以用两种方法来访问数组元素了。

如果p的初值为&a[0],则:

1) p i和a i就是a[i]的地址,或者说它们指向a数组的第i个元素。

9159.com 12

2) *(p i)或*(a i)就是p i或a i所指向的数组元素,即a[i]。例如,*(p 5)或*(a 5)就是a[5]。

3) 指向数组的指针变量也可以带下标,如p[i]与*(p i)等价。

根据以上叙述,引用一个数组元素可以用:

1) 下标法,即用a[i]形式访问数组元素。在前面介绍数组时都是采用这种方法。

2) 指针法,即采用*(a i)或*(p i)形式,用间接访问的方法来访问数组元素,其中a是数组名,p是指向数组的指针变量,其处值p=a。

【例10.9】输出数组中的全部元素。(下标法)

main(){

  int a[10],i;

  for(i=0;i<10;i )

    a[i]=i;

  for(i=0;i<5;i )

    printf("a[%d]=%dn",i,a[i]);

}

9159.com 13

【例10.10】输出数组中的全部元素。(通过数组名计算元素的地址,找出元素的值)

main(){

  int a[10],i;

  for(i=0;i<10;i )

    *(a i)=i;

  for(i=0;i<10;i )

    printf("a[%d]=%dn",i,*(a i));

}

9159.com 14

【例10.11】输出数组中的全部元素。(用指针变量指向元素)

main(){

  int a[10],I,*p;

  p=a;

  for(i=0;i<10;i )

    *(p i)=i;

  for(i=0;i<10;i )

    printf("a[%d]=%dn",i,*(p i));

}

9159.com 15

【例10.12】

main(){

  int a[10],i,*p=a;

  for(i=0;i<10;){

    *p=i;

    printf("a[%d]=%dn",i ,*p );

  }

}

9159.com 16

几个注意的问题:

1) 指针变量可以实现本身的值的改变。如p 是合法的;而a 是错误的。因为a是数组名,它是数组的首地址,是常量。

2) 要注意指针变量的当前值。请看下面的程序。

【例10.13】找出错误。

main(){

  int *p,i,a[10];

  p=a;

for(i=0;i<10;i )

    *p =i;

  for(i=0;i<10;i )

    printf("a[%d]=%dn",i,*p );

}

9159.com 17

【例10.14】改正。

main(){

  int *p,i,a[10];

  p=a;

for(i=0;i<10;i )

*p =i;

  p=a;

  for(i=0;i<10;i )

    printf("a[%d]=%dn",i,*p );

}

9159.com 18

3) 从上例可以看出,虽然定义数组时指定它包含10个元素,但指针变量可以指到数组以后的内存单元,系统并不认为非法。

4) *p ,由于 和*同优先级,结合方向自右而左,等价于*(p )。

5) *(p )与*( p)作用不同。若p的初值为a,则*(p )等价a[0],*( p)等价a[1]。

6) (*p) 表示p所指向的元素值加1。

7) 如果p当前指向a数组中的第i个元素,则

*(p--)相当于a[i--];

*( p)相当于a[ i];

*(--p)相当于a[--i]。

当然结构体本身也要对齐,stu的有效对齐值为4。从而0x000d到0x000f也归结构体所战友,即sizeof(str)=16;

(2)*:指针运算符(或称“间接访问”运算符),

10.2.4 指针变量几个问题的进一步说明

指针变量可以进行某些运算,但其运算的种类是有限的。它只能进行赋值运算和部分算术运算及关系运算。

  1. 指针运算符

1) 取地址运算符&:取地址运算符&是单目运算符,其结合性为自右至左,其功能是取变量的地址。在scanf函数及前面介绍指针变量赋值中,我们已经了解并使用了&运算符。

2) 取内容运算符*:取内容运算符*是单目运算符,其结合性为自右至左,用来表示指针变量所指的变量。在*运算符之后跟的变量必须是指针变量。

需要注意的是指针运算符*和指针变量说明中的指针说明符*不是一回事。在指针变量说明中,“*”是类型说明符,表示其后的变量是指针类型。而表达式中出现的“*”则是一个运算符用以表示指针变量所指的变量。

【例10.6】

main(){

  int a=5,*p=&a;

  printf ("%d",*p);

}

9159.com 19

表示指针变量p取得了整型变量a的地址。printf("%d",*p)语句表示输出变量a的值。

  1. 指针变量的运算

1) 赋值运算:指针变量的赋值运算有以下几种形式。

① 指针变量初始化赋值,前面已作介绍。

② 把一个变量的地址赋予指向相同数据类型的指针变量。

例如:

int a,*pa;

pa=&a;    /*把整型变量a的地址赋予整型指针变量pa*/

③ 把一个指针变量的值赋予指向相同类型变量的另一个指针变量。

如:

    int a,*pa=&a,*pb;

    pb=pa;    /*把a的地址赋予指针变量pb*/

由于pa,pb均为指向整型变量的指针变量,因此可以相互赋值。

④ 把数组的首地址赋予指向数组的指针变量。

例如:

    int a[5],*pa;

    pa=a;

    (数组名表示数组的首地址,故可赋予指向数组的指针变量pa)

也可写为:

    pa=&a[0];  /*数组第一个元素的地址也是整个数组的首地址, 也可赋予pa*/

当然也可采取初始化赋值的方法:

    int a[5],*pa=a;

⑤ 把字符串的首地址赋予指向字符类型的指针变量。

例如:

    char *pc;

    pc="C Language";

或用初始化赋值的方法写为:

    char *pc="C Language";

这里应说明的是并不是把整个字符串装入指针变量,而是把存放该字符串的字符数组的首地址装入指针变量。在后面还将详细介绍。

⑥ 把函数的入口地址赋予指向函数的指针变量。

例如:

    int (*pf)();

    pf=f;     /*f为函数名*/

2) 加减算术运算

对于指向数组的指针变量,可以加上或减去一个整数n。设pa是指向数组a的指针变量,则pa n,pa-n,pa , pa,pa--,--pa运算都是合法的。指针变量加或减一个整数n的意义是把指针指向的当前位置(指向某数组元素)向前或向后移动n个位置。应该注意,数组指针变量向前或向后移动一个位置和地址加1或减1在概念上是不同的。因为数组可以有不同的类型,各种类型的数组元素所占的字节长度是不同的。如指针变量加1,即向后移动1 个位置表示指针变量指向下一个数据元素的首地址。而不是在原地址基础上加1。例如:

    int a[5],*pa;

    pa=a;      /*pa指向数组a,也是指向a[0]*/

pa=pa 2;   /*pa指向a[2],即pa的值为&pa[2]*/

指针变量的加减运算只能对数组指针变量进行,对指向其它类型变量的指针变量作加减运算是毫无意义的。

3) 两个指针变量之间的运算:只有指向同一数组的两个指针变量之间才能进行运算,否则运算毫无意义。

① 两指针变量相减:两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。实际上是两个指针值(地址)相减之差再除以该数组元素的长度(字节数)。例如pf1和pf2是指向同一浮点数组的两个指针变量,设pf1的值为2010H,pf2的值为2000H,而浮点数组每个元素占4个字节,所以pf1-pf2的结果为(2000H-2010H)/4=4,表示pf1和 pf2之间相差4个元素。两个指针变量不能进行加法运算。例如,pf1 pf2是什么意思呢?毫无实际意义。

② 两指针变量进行关系运算:指向同一数组的两指针变量进行关系运算可表示它们所指数组元素之间的关系。

例如:

pf1==pf2表示pf1和pf2指向同一数组元素;

pf1>pf2表示pf1处于高地址位置;

pf1<pf2表示pf2处于低地址位置。

指针变量还可以与0比较。

设p为指针变量,则p==0表明p是空指针,它不指向任何变量;

p!=0表示p不是空指针。

空指针是由对指针变量赋予0值而得到的。

例如:

#define NULL 0

int *p=NULL;

对指针变量赋0值和不赋值是不同的。指针变量未赋值时,可以是任意值,是不能使用的。否则将造成意外错误。而指针变量赋0值后,则可以使用,只是它不指向具体的变量而已。

【例10.7】

main(){

  int a=10,b=20,s,t,*pa,*pb; /*说明pa,pb为整型指针变量*/

  pa=&a;                     /*给指针变量pa赋值,pa指向变量a*/

  pb=&b;                     /*给指针变量pb赋值,pb指向变量b*/

  s=*pa *pb;                 /*求a b之和,(*pa就是a,*pb就是b)*/

  t=*pa**pb;                 /*本行是求a*b之积*/

  printf("a=%dnb=%dna b=%dna*b=%dn",a,b,a b,a*b);

  printf("s=%dnt=%dn",s,t);

}

9159.com 20

【例10.8】

main(){

  int a,b,c,*pmax,*pmin;             /*pmax,pmin为整型指针变量*/

  printf("input three numbers:n");  /*输入提示*/

  scanf("%d%d%d",&a,&b,&c);         /*输入三个数字*/

  if(a>b){                          /*如果第一个数字大于第二个数字...*/

    pmax=&a;                        /*指针变量赋值*/

    pmin=&b;}                       /*指针变量赋值*/

  else{

    pmax=&b;                        /*指针变量赋值*/

    pmin=&a;}                       /*指针变量赋值*/

  if(c>*pmax) pmax=&c;              /*判断并赋值*/

  if(c<*pmin) pmin=&c;              /*判断并赋值*/

    printf("max=%dnmin=%dn",*pmax,*pmin); /*输出结果*/

}

9159.com 21

int b = (*p)[0];(b = 1)

int i,*p;

10.4.2 使用字符串指针变量与字符数组的区别

用字符数组和字符指针变量都可实现字符串的存储和运算。但是两者是有区别的。在使用时应注意以下几个问题:

1. 字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身是存放在以该首地址为首的一块连续的内存空间中并以‘\0’作为串的结束。字符数组是由于若干个数组元素组成的,它可用来存放整个字符串。

  1. 对字符串指针方式

char *ps="C Language";

可以写为:

    char *ps;

ps="C Language";

而对数组方式:

    static char st[]={"C Language"};

不能写为:

    char st[20];

    st={"C Language"};

而只能对字符数组的各元素逐个赋值。

从以上几点可以看出字符串指针变量与字符数组在使用时的区别,同时也可看出使用指针变量更加方便。

前面说过,当一个指针变量在未取得确定地址前使用是危险的,容易引起错误。但是对指针变量直接赋值是可以的。因为C系统对指针变量赋值时要给以确定的地址。

因此,

    char *ps="C Langage";

或者

    char *ps;

    ps="C Language";

都是合法的。

 

1数组元素的指针

10.2 变量的指针和指向变量的指针变量

变量的指针就是变量的地址。存放变量地址的变量是指针变量。即在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个变量的地址或称为某变量的指针。

为了表示指针变量和它所指向的变量之间的关系,在程序中用“*”符号表示“指向”,例如,i_pointer代表指针变量,而*i_pointer是i_pointer所指向的变量。

9159.com 22

因此,下面两个语句作用相同:

i=3;

*i_pointer=3;

第二个语句的含义是将3赋给指针变量i_pointer所指向的变量。

int* const p。意思是p是指针常量,不可以被更改。但是p指向的数据可以被更改。

printf(“%d”,*p);

10 指针

指针是C语言中广泛使用的一种数据类型。运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构;能很方便地使用数组和字符串;并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了C语言的功能。学习指针是学习C语言中最重要的一环,能否正确理解和使用指针是我们是否掌握C语言的一个标志。同时,指针也是C语言中最为困难的一部分,在学习中除了要正确理解基本概念,还必须要多编程,上机调试。只要作到这些,指针也是不难掌握的。

const int* const p。显然p本身是常量,p指向的变量也是常量。

两个运算符需牢记:

10.2.2 指针变量的引用

指针变量同普通变量一样,使用之前不仅要定义说明,而且必须赋予具体的值。未经赋值的指针变量不能使用,否则将造成系统混乱,甚至死机。指针变量的赋值只能赋予地址,决不能赋予任何其它数据,否则将引起错误。在C语言中,变量的地址是由编译系统分配的,对用户完全透明,用户不知道变量的具体地址。

两个有关的运算符:

1) &:取地址运算符。

2) *:指针运算符(或称“间接访问” 运算符)。

C语言中提供了地址运算符&来表示变量的地址。

其一般形式为:

&变量名;

如&a表示变量a的地址,&b表示变量b的地址。变量本身必须预先说明。

设有指向整型变量的指针变量p,如要把整型变量a 的地址赋予p可以有以下两种方式:

(1) 指针变量初始化的方法

  int a;

    int *p=&a;

(2) 赋值语句的方法

    int a;

    int *p;

p=&a;

不允许把一个数赋予指针变量,故下面的赋值是错误的:

int *p;

p=1000;

被赋值的指针变量前不能再加“*”说明符,如写为*p=&a 也是错误的。

假设:

int i=200, x;

int *ip;

我们定义了两个整型变量i,x,还定义了一个指向整型数的指针变量ip。i,x中可存放整数,而ip中只能存放整型变量的地址。我们可以把i的地址赋给ip:

ip=&i;

此时指针变量ip指向整型变量i,假设变量i的地址为1800,这个赋值可形象理解为下图所示的联系。

9159.com 23

以后我们便可以通过指针变量ip间接访问变量i,例如:

x=*ip;

运算符*访问以ip为地址的存贮区域,而ip中存放的是变量i的地址,因此,*ip访问的是地址为1800的存贮区域(因为是整数,实际上是从1800开始的两个字节),它就是i所占用的存贮区域, 所以上面的赋值表达式等价于

x=i;

另外,指针变量和一般变量一样,存放在它们之中的值是可以改变的,也就是说可以改变它们的指向,假设

int i,j,*p1,*p2;

i='a';

j='b';

p1=&i;

p2=&j;

则建立如下图所示的联系:

9159.com 24

这时赋值表达式:

p2=p1

就使p2与p1指向同一对象i,此时*p2就等价于i,而不是j,图所示:

9159.com 25

如果执行如下表达式:

*p2=*p1;

9159.com,则表示把p1指向的内容赋给p2所指的区域, 此时就变成图所示

9159.com 26

通过指针访问它所指向的一个变量是以间接访问的形式进行的,所以比直接访问一个变量要费时间,而且不直观,因为通过指针要访问哪一个变量,取决于指针的值(即指向),例如"*p2=*p1;"实际上就是"j=i;",前者不仅速度慢而且目的不明。但由于指针是变量,我们可以通过改变它们的指向,以间接访问不同的变量,这给程序员带来灵活性,也使程序代码编写得更为简洁和有效。

指针变量可出现在表达式中, 设

int x,y,*px=&x;

指针变量px指向整数x,则*px可出现在x能出现的任何地方。例如:

y=*px 5; /*表示把x的内容加5并赋给y*/

y= *px; /*px的内容加上1之后赋给y, *px相当于 (*px)*/

y=*px ; /*相当于y=*px; px */

【例10.1】

main()

{ int a,b;

  int *pointer_1, *pointer_2;

  a=100;b=10;

  pointer_1=&a;

pointer_2=&b;

  printf("%d,%dn",a,b);

  printf("%d,%dn",*pointer_1, *pointer_2);

}

9159.com 27

对程序的说明:

1) 在开头处虽然定义了两个指针变量pointer_1和pointer_2,担它们并未指向任何一个整型变量。只是提供两个指针变量,规定它们可以指向整型变量。程序第5、6行的作用就是使pointer_1指向a,pointer_2指向b。

9159.com 28

2) 最后一行的*pointer_1和*pointer_2就是变量a和b。最后两个printf函数作用是相同的。

3) 程序中有两处出现*pointer_1和*pointer_2,请区分它们的不同含义。

4) 程序第5、6行的“pointer_1=&a”和 “pointer_2=&b”不能写成“*pointer_1=&a”和 “*pointer_2=&b”。

请对下面再的关于“&”和“*”的问题进行考虑:

1) 如果已经执行了“pointer_1=&a;”语句,则&*pointer_1是什么含义?

2) *&a含义是什么?

3) (pointer_1) 和pointer_1 的区别?

【例10.2】输入a和b两个整数,按先大后小的顺序输出a和b。

main()

{ int *p1,*p2,*p,a,b;

  scanf("%d,%d",&a,&b);

  p1=&a;p2=&b;

  if(a<b)

    {p=p1;p1=p2;p2=p;}

  printf("na=%d,b=%dn",a,b);

  printf("max=%d,min=%dn",*p1, *p2);

}

9159.com 29

strlen(...)是函数,要在运行时才能计算。参数必须是字符型指针(char*)。它的功能是:返回字符串的长度。该字符串可能是自己定义的,也可能是内存中随机的,该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,直到遇到结束符NULL。返回的长度大小不包括NULL。

第三类物理地址:也就是内存单元的实际地址,用于芯片级内存单元寻址。物理地址也由32位无符号整数表示。

10.5 函数指针变量

在C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数。然后通过指针变量就可以找到并调用这个函数。我们把这种指向函数的指针变量称为“函数指针变量”。

函数指针变量定义的一般形式为:

类型说明符  (*指针变量名)();

其中“类型说明符”表示被指函数的返回值的类型。“(* 指针变量名)”表示“*”后面的变量是定义的指针变量。最后的空括号表示指针变量所指的是一个函数。

例如:

    int (*pf)();

表示pf是一个指向函数入口的指针变量,该函数的返回值(函数值)是整型。

【例10.31】本例用来说明用指针形式实现对函数调用的方法。

int max(int a,int b){

  if(a>b)return a;

  else return b;

}

main(){

  int max(int a,int b);

  int(*pmax)();

  int x,y,z;

  pmax=max;

  printf("input two numbers:n");

  scanf("%d%d",&x,&y);

  z=(*pmax)(x,y);

  printf("maxmum=%d",z);

}

9159.com 30

从上述程序可以看出用,函数指针变量形式调用函数的步骤如下:

1) 先定义函数指针变量,如后一程序中第9行 int (*pmax)();定义 pmax为函数指针变量。

2) 把被调函数的入口地址(函数名)赋予该函数指针变量,如程序中第11行 pmax=max;

3) 用函数指针变量形式调用函数,如程序第14行 z=(*pmax)(x,y);

4) 调用函数的一般形式为:

(*指针变量名) (实参表)

使用函数指针变量还应注意以下两点:

a) 函数指针变量不能进行算术运算,这是与数组指针变量不同的。数组指针变量加减一个整数可使指针移动指向后面或前面的数组元素,而函数指针的移动是毫无意义的。

b) 函数调用中"(*指针变量名)"的两边的括号不可少,其中的*不应该理解为求值运算,在此处它只是一种表示符号。

char s1[] = "123456";

(1)下标法

10.2.1 定义一个指针变量

对指针变量的定义包括三个内容:

(1) 指针类型说明,即定义变量为一个指针变量;

(2) 指针变量名;

(3) 变量值(指针)所指向的变量的数据类型。

其一般形式为:

类型说明符  *变量名;

其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指向的变量的数据类型。

例如:    int *p1;

表示p1是一个指针变量,它的值是某个整型变量的地址。或者说p1指向一个整型变量。至于p1究竟指向哪一个整型变量,应由向p1赋予的地址来决定。

再如:

int *p2;        /*p2是指向整型变量的指针变量*/

    float *p3;      /*p3是指向浮点变量的指针变量*/

char *p4;       /*p4是指向字符变量的指针变量*/

应该注意的是,一个指针变量只能指向同类型的变量,如P3 只能指向浮点变量,不能时而指向一个浮点变量,时而又指向一个字符变量。

123456是在运行时刻赋值的,而789654是在编译时就确定的。但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。

E.m:输出数组中的全部元素。下面介绍三种方法:

10.4 字符串的指针指向字符串的针指变量

或者:

int i;

10.1 地址指针的基本概念

在计算机中,所有的数据都是存放在存储器中的。一般把存储器中的一个字节称为一个内存单元,不同的数据类型所占用的内存单元数不等,如整型量占2个单元,字符量占1个单元等,在前面已有详细的介绍。为了正确地访问这些内存单元,必须为每个内存单元编上号。根据一个内存单元的编号即可准确地找到该内存单元。内存单元的编号也叫做地址。既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。 内存单元的指针和内存单元的内容是两个不同的概念。 可以用一个通俗的例子来说明它们之间的关系。我们到银行去存取款时,银行工作人员将根据我们的帐号去找我们的存款单, 找到之后在存单上写入存款、取款的金额。在这里,帐号就是存单的指针, 存款数是存单的内容。对于一个内存单元来说,单元的地址即为指针,其中存放的数据才是该单元的内容。在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。

9159.com 31

图中,设有字符变量C,其内容为“K”(ASCII码为十进制数 75),C占用了011A号单元(地址用十六进数表示)。设有指针变量P,内容为011A,这种情况我们称为P指向变量C,或说P是指向变量C的指针。

严格地说,一个指针是一个地址,是一个常量。而一个指针变量却可以被赋予不同的指针值,是变量。但常把指针变量简称为指针。为了避免混淆,我们中约定:“指针”是指地址,是常量,“指针变量”是指取值为地址的变量。定义指针的目的是为了通过指针去访问内存单元。

既然指针变量的值是一个地址,那么这个地址不仅可以是变量的地址,也可以是其它数据结构的地址。在一个指针变量中存放一个数组或一个函数的首地址有何意义呢?因为数组或函数都是连续存放的。通过访问指针变量取得了数组或函数的首地址,也就找到了该数组或函数。这样一来,凡是出现数组,函数的地方都可以用一个指针变量来表示,只要该指针变量中赋予数组或函数的首地址即可。这样做,将会使程序的概念十分清楚,程序本身也精练,高效。在C语言中,一种数据类型或数据结构往往都占有一组连续的内存单元。用“地址”这个概念并不能很好地描述一种数据类型或数据结构,而“指针”虽然实际上也是一个地址,但它却是一个数据结构的首地址,它是“指向”一个数据结构的,因而概念更为清楚,表示更为明确。 这也是引入“指针”概念的一个重要原因。

p = a;//二维数组的首地址赋给p,也就是a[0]或者&a[0][0]

pointer_2=&b;

10.3.1 指向数组元素的指针

一个数组是由连续的一块内存单元组成的。数组名就是这块连续内存单元的首地址。一个数组也是由各个数组元素(下标变量)组成的。每个数组元素按其类型不同占有几个连续的内存单元。一个数组元素的首地址也是指它所占有的几个内存单元的首地址。

定义一个指向数组元素的指针变量的方法,与以前介绍的指针变量相同。

例如:

    int a[10];   /*定义a为包含10个整型数据的数组*/

int *p;      /*定义p为指向整型变量的指针*/

应当注意,因为数组为int型,所以指针变量也应为指向int型的指针变量。下面是对指针变量赋值:

p=&a[0];

把a[0]元素的地址赋给指针变量p。也就是说,p指向a数组的第0号元素。

9159.com 32

C语言规定,数组名代表数组的首地址,也就是第0号元素的地址。因此,下面两个语句等价:

p=&a[0];

p=a;

在定义指针变量时可以赋给初值:

int *p=&a[0];

它等效于:

int *p;

p=&a[0];

当然定义时也可以写成:

    int *p=a;

从图中我们可以看出有以下关系:

    p,a,&a[0]均指向同一单元,它们是数组a的首地址,也是0 号元素a[0]的首地址。应该说明的是p是变量,而a,&a[0]都是常量。在编程时应予以注意。

数组指针变量说明的一般形式为:

类型说明符  *指针变量名;

其中类型说明符表示所指数组的类型。从一般形式可以看出指向数组的指针变量和指向普通变量的指针变量的说明是相同的。

 

1字符串的表示形式

10.4.1 字符串的表示形式

在C语言中,可以用两种方法访问一个字符串。

1) 用字符数组存放一个字符串,然后输出该字符串。

【例10.24】

main(){

  char string[]=”I love China!”;

  printf("%sn",string);

}

9159.com 33

说明:和前面介绍的数组属性一样,string是数组名,它代表字符数组的首地址。

9159.com 34

2) 用字符串指针指向一个字符串。

【例10.25】

main(){

  char *string=”I love China!”;

  printf("%sn",string);

}

9159.com 35

字符串指针变量的定义说明与指向字符变量的指针变量说明是相同的。只能按对指针变量的赋值不同来区别。对指向字符变量的指针变量应赋予该字符变量的地址。

如:

    char c,*p=&c;

表示p是一个指向字符变量c的指针变量。

而:

    char *s="C Language";

则表示s是一个指向字符串的指针变量。把字符串的首地址赋予s。

上例中,首先定义string是一个字符指针变量,然后把字符串的首地址赋予string(应写出整个字符串,以便编译系统把该串装入连续的一块内存单元),并把首地址送入string。程序中的:

char *ps="C Language";

等效于:

char *ps;

ps="C Language";

【例10.26】输出字符串中n个字符后的所有字符。

main(){

  char *ps="this is a book";

  int n=10;

  ps=ps n;

  printf("%sn",ps);

}

9159.com 36

运行结果为:

book

在程序中对ps初始化时,即把字符串首地址赋予ps,当ps= ps 10之后,ps指向字符“b”,因此输出为"book"。

【例10.27】在输入的字符串中查找有无‘k’字符。

main(){

  char st[20],*ps;

  int i;

  printf("input a string:n");

  ps=st;

  scanf("%s",ps);

  for(i=0;ps[i]!='\0';i )

    if(ps[i]=='k'){

       printf("there is a 'k' in the stringn");

       break;

    }

  if(ps[i]=='\0') printf("There is no 'k' in the stringn");

}

9159.com 37

【例10.28】本例是将指针变量指向一个格式字符串,用在printf函数中,用于输出二维数组的各种地址表示的值。但在printf语句中用指针变量PF代替了格式串。 这也是程序中常用的方法。

main(){

  static int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};

  char *PF;

  PF="%d,%d,%d,%d,%dn";

  printf(PF,a,*a,a[0],&a[0],&a[0][0]);

  printf(PF,a 1,*(a 1),a[1],&a[1],&a[1][0]);

  printf(PF,a 2,*(a 2),a[2],&a[2],&a[2][0]);

  printf("%d,%dn",a[1] 1,*(a 1) 1);

  printf("%d,%dn",*(a[1] 1),*(*(a 1) 1));

}

9159.com 38

【例10.29】本例是把字符串指针作为函数参数的使用。要求把一个字符串的内容复制到另一个字符串中,并且不能使用strcpy函数。函数cprstr的形参为两个字符指针变量。pss指向源字符串,pds指向目标字符串。注意表达式:(*pds=*pss)!=`\0'的用法。

cpystr(char *pss,char *pds){

  while((*pds=*pss)!='\0'){

      pds ;

      pss ; }

}

main(){

  char *pa="CHINA",b[10],*pb;

  pb=b;

  cpystr(pa,pb);

  printf("string a=%snstring b=%sn",pa,pb);

}

9159.com 39

在本例中,程序完成了两项工作:一是把pss指向的源字符串复制到pds所指向的目标字符串中,二是判断所复制的字符是否为`\0',若是则表明源字符串结束,不再循环。否则,pds和pss都加1,指向下一字符。在主函数中,以指针变量pa,pb为实参,分别取得确定值后调用cprstr函数。由于采用的指针变量pa和pss,pb和pds均指向同一字符串,因此在主函数和cprstr函数中均可使用这些字符串。也可以把cprstr函数简化为以下形式:

    cprstr(char *pss,char*pds)

      {while ((*pds =*pss )!=`\0');}

即把指针的移动和赋值合并在一个语句中。进一步分析还可发现`\0'的ASCⅡ码为0,对于while语句只看表达式的值为非0就循环,为0则结束循环,因此也可省去“!=`\0'”这一判断部分,而写为以下形式:

      cprstr (char *pss,char *pds)

{while (*pdss =*pss );}

表达式的意义可解释为,源字符向目标字符赋值,移动指针,若所赋值为非0则循环,否则结束循环。这样使程序更加简洁。

【例10.30】简化后的程序如下所示。

cpystr(char *pss,char *pds){

    while(*pds =*pss );

}

main(){

  char *pa="CHINA",b[10],*pb;

  pb=b;

  cpystr(pa,pb);

  printf("string a=%snstring b=%sn",pa,pb);

}

9159.com 40

假设stu从地址0x0000开始存放,i有效对齐值为4,0x0000%4=0,所以其被存放在0x0000到0x0003这4个连续的字节空间中。c有效对齐值为1,0x0004%1=0,所以其被放在0x0004处。j的有效对齐值为4,但是0x0005%4!=0,编译器会补充3个空字节,因此j被存放在起始地址为0x0008空间。同理,cc存储在0x000c处。

printf(“%d,%dn”,pointer_1,*pointer_2);*

10.3 数组指针和指向数组的指针变量

一个变量有一个地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。所谓数组的指针是指数组的起始地址,数组元素的指针是数组元素的地址。

p ;

int pointer_1,*pointer_2;*

10.6 指针型函数

前面我们介绍过,所谓函数类型是指函数返回值的类型。在C语言中允许一个函数的返回值是一个指针(即地址),这种返回指针值的函数称为指针型函数。

定义指针型函数的一般形式为:

类型说明符 *函数名(形参表) 

    { 

……          /*函数体*/

    } 

其中函数名之前加了“*”号表明这是一个指针型函数,即返回值是一个指针。类型说明符表示了返回的指针值所指向的数据类型。

如:

    int *ap(int x,int y)

    {

      ......       /*函数体*/

}

表示ap是一个返回指针值的指针型函数,它返回的指针指向一个整型变量。

【例10.32】本程序是通过指针函数,输入一个1~7之间的整数,输出对应的星期名。

main(){

  int i;

  char *day_name(int n);  

  printf("input Day No:n");

  scanf("%d",&i);

  if(i<0) exit(1);

  printf("Day No:--->%sn",i,day_name(i));

}

char *day_name(int n){

  static char *name[]={ "Illegal day",

                        "Monday",

                        "Tuesday",

                        "Wednesday",

                        "Thursday",

                        "Friday",

                        "Saturday",

                        "Sunday"};

  return((n<1||n>7) ? name[0] : name[n]);

}

9159.com 41

C语言

来源:

4.如果a是一个数组,即int a[4],那么a和&a

E.m:比较两个整数,顺序输出整数。

10.2.3 指针变量作为函数参数

函数的参数不仅可以是整型、实型、字符型等数据,还可以是指针类型。它的作用是将一个变量的地址传送到另一个函数中。

【例10.3】题目同例10.2,即输入的两个整数按大小顺序输出。今用函数处理,而且用指针类型的数据作函数参数。

swap(int *p1,int *p2)

{int temp;

temp=*p1;

*p1=*p2;

*p2=temp;

}

main()

{

int a,b;

int *pointer_1,*pointer_2;

  scanf("%d,%d",&a,&b);

  pointer_1=&a;pointer_2=&b;

  if(a<b) swap(pointer_1,pointer_2);

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

  }

9159.com 42

对程序的说明:

swap是用户定义的函数,它的作用是交换两个变量(a和b)的值。swap函数的形参p1、p2是指针变量。程序运行时,先执行main函数,输入a和b的值。然后将a和b的地址分别赋给指针变量pointer_1和pointer_2,使pointer_1指向a,pointer_2指向b。

9159.com 43

接着执行if语句,由于a〈b,因此执行swap函数。注意实参pointer_1和pointer_2是指针变量,在函数调用时,将实参变量的值传递给形参变量。采取的依然是“值传递”方式。因此虚实结合后形参p1的值为&a,p2的值为&b。这时p1和pointer_1指向变量a,p2和pointer_2指向变量b。

9159.com 44

接着执行执行swap函数的函数体使*p1和*p2的值互换,也就是使a和b的值互换。

9159.com 45

函数调用结束后,p1和p2不复存在(已释放)如图。

9159.com 46

最后在main函数中输出的a和b的值是已经过交换的值。

请注意交换*p1和*p2的值是如何实现的。请找出下列程序段的错误:

swap(int *p1,int *p2)

{int *temp;

*temp=*p1;      /*此语句有问题*/

*p1=*p2;

*p2=temp;

}

请考虑下面的函数能否实现实现a和b互换。

swap(int x,int y)

{int temp;

temp=x;

x=y;

y=temp;

}

如果在main函数中用“swap(a,b);”调用swap函数,会有什么结果呢?请看下图所示。

9159.com 47

【例10.4】请注意,不能企图通过改变指针形参的值而使指针实参的值改变。

swap(int *p1,int *p2)

{int *p;

p=p1;

p1=p2;

p2=p;

}

main()

{

int a,b;

int *pointer_1,*pointer_2;

  scanf("%d,%d",&a,&b);

  pointer_1=&a;pointer_2=&b;

  if(a<b) swap(pointer_1,pointer_2);

  printf("n%d,%dn",*pointer_1,*pointer_2);

  }

9159.com 48

其中的问题在于不能实现如图所示的第四步(d)。

9159.com 49

【例10.5】输入a、b、c3个整数,按大小顺序输出。

swap(int *pt1,int *pt2)

{int temp;

temp=*pt1;

*pt1=*pt2;

*pt2=temp;

}

exchange(int *q1,int *q2,int *q3)

{ if(*q1<*q2)swap(q1,q2);

if(*q1<*q3)swap(q1,q3);

if(*q2<*q3)swap(q2,q3);

}

main()

{

int a,b,c,*p1,*p2,*p3;

  scanf("%d,%d,%d",&a,&b,&c);

  p1=&a;p2=&b; p3=&c;

  exchange(p1,p2,p3);

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

  }

9159.com 50

int a = 4;

特别注意:(括号的重要性)(*pointer_1)++相当于a++。注意括号的必要性,如果没有括号,就成为了*pointer_1 ,++与*是同一级别运算符,因此结合方向自右向左,它相当于*(pointer_1++)。由于++在pointer_1的右侧,是后加,因此先对pointer_1的原值进行*运算,得到的a值,然后使pointer_1的值改变,这样pointer_1不再指向a了。

10.3.3 数组名作函数参数

数组名可以作函数的实参和形参。如:

main()

{int array[10];

……

……

f(array,10);

……

……

}

f(int arr[],int n);

     {

……

……

}

array为实参数组名,arr为形参数组名。在学习指针变量之后就更容易理解这个问题了。数组名就是数组的首地址,实参向形参传送数组名实际上就是传送数组的地址,形参得到该地址后也指向同一数组。这就好象同一件物品有两个彼此不同的名称一样。

9159.com 51

同样,指针变量的值也是地址,数组指针变量的值即为数组的首地址,当然也可作为函数的参数使用。

【例10.15】

float aver(float *pa);

main(){

  float sco[5],av,*sp;

  int i;

  sp=sco;

  printf("ninput 5 scores:n");

  for(i=0;i<5;i ) scanf("%f",&sco[i]);

  av=aver(sp);

  printf("average score is %5.2f",av);

}

float aver(float *pa)

{

  int i;

  float av,s=0;

  for(i=0;i<5;i ) s=s *pa ;

  av=s/5;

  return av;

}

9159.com 52

【例10.16】将数组a中的n个整数按相反顺序存放。

算法为:将a[0]与a[n-1]对换,再a[1]与a[n-2] 对换……,直到将a[(n-1/2)]与a[n-int((n-1)/2)]对换。今用循环处理此问题,设两个“位置指示变量”i和j,i的初值为0,j的初值为n-1。将a[i]与a[j]交换,然后使i的值加1,j的值减1,再将a[i]与a[j]交换,直到i=(n-1)/2为止,如图所示。

9159.com 53

程序如下:

void inv(int x[],int n)   /*形参x是数组名*/

{

int temp,i,j,m=(n-1)/2;

for(i=0;i<=m;i )

{j=n-1-i;

   temp=x[i];x[i]=x[j];x[j]=temp;}

return;

}

main()

{int i,a[10]={3,7,9,11,0,6,7,5,4,2};

printf("The original array:n");

for(i=0;i<10;i )

   printf("%d,",a[i]);

printf("n");

inv(a,10);

printf("The array has benn inverted:n");

for(i=0;i<10;i )

  printf("%d,",a[i]);

printf("n");

}

9159.com 54

对此程序可以作一些改动。将函数inv中的形参x改成指针变量。

【例10.17】对例10.16可以作一些改动。将函数inv中的形参x改成指针变量。

程序如下:

void inv(int *x,int n)   /*形参x为指针变量*/

{

int *p,temp,*i,*j,m=(n-1)/2;

i=x;j=x n-1;p=x m;

for(;i<=p;i ,j--)

{temp=*i;*i=*j;*j=temp;}

return;

}

main()

{int i,a[10]={3,7,9,11,0,6,7,5,4,2};

printf("The original array:n");

for(i=0;i<10;i )

   printf("%d,",a[i]);

printf("n");

inv(a,10);

printf("The array has benn inverted:n");

for(i=0;i<10;i )

  printf("%d,",a[i]);

printf("n");

}

9159.com 55

运行情况与前一程序相同。

【例10.18】从0个数中找出其中最大值和最小值。

调用一个函数只能得到一个返回值,今用全局变量在函数之间“传递”数据。程序如下:

int max,min;      /*全局变量*/

void max_min_value(int array[],int n)

{int *p,*array_end;

array_end=array n;

max=min=*array;

for(p=array 1;p<array_end;p )

   if(*p>max)max=*p;

   else if (*p<min)min=*p;

return;

}

main()

{int i,number[10];

printf("enter 10 integer umbers:n");

for(i=0;i<10;i )

   scanf("%d",&number[i]);

max_min_value(number,10);

printf("nmax=%d,min=%dn",max,min);

}

9159.com 56

说明:

1) 在函数max_min_value中求出的最大值和最小值放在max和min中。由于它们是全局,因此在主函数中可以直接使用。

2) 函数max_min_value中的语句:

max=min=*array;

array是数组名,它接收从实参传来的数组numuber的首地址。

*array相当于*(&array[0])。上述语句与 max=min=array[0];等价。

3) 在执行for循环时,p的初值为array 1,也就是使p指向array[1]。以后每次执行p ,使p指向下一个元素。每次将*p和max与min比较。将大者放入max,小者放min。

9159.com 57

4) 函数max_min_value的形参array可以改为指针变量类型。实参也可以不用数组名,而用指针变量传递地址。

【例10.19】程序可改为:

int max,min;      /*全局变量*/

void max_min_value(int *array,int n)

{int *p,*array_end;

array_end=array n;

max=min=*array;

for(p=array 1;p<array_end;p )

   if(*p>max)max=*p;

   else if (*p<min)min=*p;

return;

}

main()

{int i,number[10],*p;

p=number;             /*使p指向number数组*/

printf("enter 10 integer umbers:n");

for(i=0;i<10;i ,p )

   scanf("%d",p);

p=number;

max_min_value(p,10);

printf("nmax=%d,min=%dn",max,min);

}

9159.com 58

归纳起来,如果有一个实参数组,想在函数中改变此数组的元素的值,实参与形参的对应关系有以下4种:

1) 形参和实参都是数组名。

main()

{int a[10];

……

f(a,10)

……

f(int x[],int n)

{

……

}

}

a和x指的是同一组数组。

2) 实用数组,形参用指针变量。

main()

{int a[10];

……

f(a,10)

……

f(int *x,int n)

{

……

}

}

3) 实参、型参都用指针变量。

4) 实参为指针变量,型参为数组名。

【例10.20】用实参指针变量改写将n个整数按相反顺序存放。

void inv(int *x,int n)

{int *p,m,temp,*i,*j;

m=(n-1)/2;

i=x;j=x n-1;p=x m;

for(;i<=p;i ,j--)

   {temp=*i;*i=*j;*j=temp;}

return;

}

main()

{int i,arr[10]={3,7,9,11,0,6,7,5,4,2},*p;

p=arr;

printf("The original array:n");

for(i=0;i<10;i ,p )

   printf("%d,",*p);

printf("n");

p=arr;

inv(p,10);

printf("The array has benn inverted:n");

for(p=arr;p<arr 10;p )

  printf("%d,",*p);

printf("n");

}

9159.com 59

注意:main函数中的指针变量p是有确定值的。即如果用指针变作实参,必须现使指针变量有确定值,指向一个已定义的数组。

【例10.21】用选择法对10个整数排序。

main()

{int *p,i,a[10]={3,7,9,11,0,6,7,5,4,2};

printf("The original array:n");

for(i=0;i<10;i )

   printf("%d,",a[i]);

printf("n");

p=a;

sort(p,10);

for(p=a,i=0;i<10;i )

  {printf("%d  ",*p);p ;}

printf("n");

}

sort(int x[],int n)

{int i,j,k,t;

for(i=0;i<n-1;i )

   {k=i;

    for(j=i 1;j<n;j )

      if(x[j]>x[k])k=j;

    if(k!=i)

    {t=x[i];x[i]=x[k];x[k]=t;}

    }

}

9159.com 60

说明:函数sort用数组名作为形参,也可改为用指针变量,这时函数的首部可以改为:

sort(int *x,int n) 其他可一律不改。

数组指针:a pointer to an array,本质是指针,即指向数组的指针。在内存中占有一个指针的存储空间。

E.p:&a为变量a的地址,*p为指针变量p所只想的存储单元的内容(即p所指向的变量的值)

10.3.1 指向多维数组的指针和指针变量

本小节以二维数组为例介绍多维数组的指针变量。

  1. 多维数组的地址

设有整型二维数组a[3][4]如下:

0   1   2   3

    4   5   6   7

8   9  10  11

它的定义为:

int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}}

设数组a的首地址为1000,各下标变量的首地址及其值如图所示。

9159.com 61

前面介绍过,C语言允许把一个二维数组分解为多个一维数组来处理。因此数组a可分解为三个一维数组,即a[0],a[1],a[2]。每一个一维数组又含有四个元素。

9159.com 62

例如a[0]数组,含有a[0][0],a[0][1],a[0][2],a[0][3]四个元素。

数组及数组元素的地址表示如下:

从二维数组的角度来看,a是二维数组名,a代表整个二维数组的首地址,也是二维数组0行的首地址,等于1000。a 1代表第一行的首地址,等于1008。如图:

9159.com 63

a[0]是第一个一维数组的数组名和首地址,因此也为1000。*(a 0)或*a是与a[0]等效的, 它表示一维数组a[0]0 号元素的首地址,也为1000。&a[0][0]是二维数组a的0行0列元素首地址,同样是1000。因此,a,a[0],*(a 0),*a,&a[0][0]是相等的。

同理,a 1是二维数组1行的首地址,等于1008。a[1]是第二个一维数组的数组名和首地址,因此也为1008。&a[1][0]是二维数组a的1行0列元素地址,也是1008。因此a 1,a[1],*(a 1),&a[1][0]是等同的。

由此可得出:a i,a[i],*(a i),&a[i][0]是等同的。

此外,&a[i]和a[i]也是等同的。因为在二维数组中不能把&a[i]理解为元素a[i]的地址,不存在元素a[i]。C语言规定,它是一种地址计算方法,表示数组a第i行首地址。由此,我们得出:a[i],&a[i],*(a i)和a i也都是等同的。

另外,a[0]也可以看成是a[0] 0,是一维数组a[0]的0号元素的首地址,而a[0] 1则是a[0]的1号元素首地址,由此可得出a[i] j则是一维数组a[i]的j号元素首地址,它等于&a[i][j]。

9159.com 64

由a[i]=*(a i)得a[i] j=*(a i) j。由于*(a i) j是二维数组a的i行j列元素的首地址,所以,该元素的值等于*(*(a i) j)。

【例10.22】

main(){

    int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};

    printf("%d,",a);

    printf("%d,",*a);

    printf("%d,",a[0]);

    printf("%d,",&a[0]);

    printf("%dn",&a[0][0]);

    printf("%d,",a 1);

    printf("%d,",*(a 1));

    printf("%d,",a[1]);

    printf("%d,",&a[1]);

    printf("%dn",&a[1][0]);

    printf("%d,",a 2);

    printf("%d,",*(a 2));

    printf("%d,",a[2]);

    printf("%d,",&a[2]);

    printf("%dn",&a[2][0]);

    printf("%d,",a[1] 1);

    printf("%dn",*(a 1) 1);

    printf("%d,%dn",*(a[1] 1),*(*(a 1) 1));

}

9159.com 65

  1. 指向多维数组的指针变量

把二维数组a分解为一维数组a[0],a[1],a[2]之后,设p为指向二维数组的指针变量。可定义为:

          int (*p)[4]

它表示p是一个指针变量,它指向包含4个元素的一维数组。若指向第一个一维数组a[0],其值等于a,a[0],或&a[0][0]等。而p i则指向一维数组a[i]。从前面的分析可得出*(p i) j是二维数组i行j 列的元素的地址,而*(*(p i) j)则是i行j列元素的值。

二维数组指针变量说明的一般形式为:

类型说明符  (*指针变量名)[长度]

其中“类型说明符”为所指数组的数据类型。“*”表示其后的变量是指针类型。“长度”表示二维数组分解为多个一维数组时,一维数组的长度,也就是二维数组的列数。应注意“(*指针变量名)”两边的括号不可少,如缺少括号则表示是指针数组(本章后面介绍),意义就完全不同了。

【例10.23】

main(){

    int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};

    int(*p)[4];

    int i,j;

    p=a;

    for(i=0;i<3;i )

    {for(j=0;j<4;j ) printf("-  ",*(*(p i) j));

    printf("n");}

}

9159.com 66

int (*p)[4];//指针p指向一个整形的一维数组,数组的长度是4,

if(a

&a是整个数组的地址。

{ int temp;//定义为int temp,而不是int*temp,因为交换的是值。

int* p[8];

{ int a[10];//数组中一共有十个元素,所以数组下标一定要注意。

};

入门的指针学习,将从七个方面进行介绍,这是指针最基本的用法,需要掌握。

char *s2 = "789654;

(3)用指针变量指向数据元素

 

{ void swap(int *p1,int *p2); //声明一个函数,在后边需要使用。

有效对齐值N(存放起始地址%N=0)是最终用来决定数据存放地址方式的值。

int a,b;

const int*p <==>等价于const int (*p)<==>等价于int const (*p)。意思是p指向的数据是常量,不可以被更改。但是指针p本身是可以被更改的。

第二类线性地址:线性地址是一个32位的无符号整数,可以表达高达2^32(4GB)的地址。通常用16进制表示线性地址,其取值范围为0x00000000~0xffffffff。

p;

#include

 

pointer_1=&a; pointer_2=&b;

 

函数的参数不一定是整型,浮点型,字符型等数据,也可以是指针类型,他的作用是将一个变量的地址传送到另一个函数中。

 struct str

}

 

二变量的指针和指向变量的指针变量

 

*p2=temp;

 

scanf(“%dn”,&a[i]);

sizeof(...)是运算符,在头文件中typedef为unsigned int,其值在编译时即计算好了(由于在编译时计算,因此sizeof不能用来返回动态分配的内存空间的大小),参数可以是数组、指针、类型、对象、函数等。它的功能是:获得保证能容纳实现所建立的最大对象的字节大小。

{ int a[10];

  int j;

数组在内存中占用存储单元,并且有相应的存储地址。指针可以指向int,char,等变量类型,也可以指向数组。所谓的数组元素的指针就是数组元素的地址。

  char c;

特别注意两点:第一,‘*’代表的是指针类型。第二,指针变量必须有类型,因为不同的指针变量类型,在操作时会受到影响。

char a[10] = "123456";  //sizeof(a) = 10, strlen(a) = 6
char *p = "123456";     //sizeof(p) = 4,  strlen(p) = 6, sizeof(*p) = 1
a[0] = 0;               //sizeof(a) = 10, strlen(a) = 0

void main()

指针数组:array of pointers,本质是数组,用于存储指针,也就是说数组元素都是指针。在内存中占有多个指针的存储空间。

1.地址:内存区的每一个字节有一个编号,这就是地址。来自百度百科:由于我们现在使用的是80x86这种微处理器,所以我们可以把地址分为三类:第一类:逻辑地址:机器语言指令仍用这种地址指定一个操作数的地址或一条指令的地址。这种寻址方式在Intel的分段结构中表现得尤为具体,它使得MS-DOS或Windows程序员把程序分为若干段。每个逻辑地址都由一个段和偏移量组成。

  char cc;

它也等价于:int*p;p=&a[0];

b = (*P)[0];(b = 5)

for(i=0;i<10;i )

3.指针数组和数组指针

void main()

 

取指针所指向的对象的内容。

 

}

 

void main()

p[0] = &a;

{ int a[10];//数组中一共有十个元素,所以数组下标一定要注意。

 

void main()

5.sizeof和strlen

int i;

虽然a和&a值相同,但是意义不同。a是数组名,同时a是数组首元素的地址,也就是&a[0]的值。

2016/8/20

2.const

2.指针:将地址形象化的称为“指针”。意思就是通过她能找到以他为地址的内存单元。在信息科学中,指针一般指向一个函数或一个变量。在使用一个指针时,一个程序既可以直接使用这个指针所存储的内存地址,又可以使用这个地址里存储的函数值。

{

for(i=0;i<10;i )

1.栈: 在函数调用时,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向函数的返回地址,也就是主函数中的下一条指令的地址,程序由该点继续运行。

printf(“%dn”,*(a i));//利用数组的名称计算地址。不能写成*a+i,意义完全不同。

  int i;

1定义一个指针变量

 

一、地址和指针的概念

int b = *p[0];或者*(p[0]),[]优先级比*高。

scanf(“%dn”,&a[i]);

p = &a;

前言:以上的入门篇知识均来自谭浩强c语言书籍,清华大学出版社。其中例子均属本人胡邹。与原书作者毫无关系。

 

c规定,在所有的变量使用前,必须要定义变量,指定其类型,这样方便分配内存单元。但是指针跟其他变量不一样,他是用来专门存放地址的。

 

}

 6.地址对齐
当然标准并没有硬性规定怎么对齐,不同架构不同编译器可能对齐策略也不一样。除非指明编译器怎么对齐,不然就是未定义行为。1)数据类型自身的对齐值:就是基本数据的长度,如 sizeof(char)、sizeof(int)。
2)指定对齐值:#pragma pack (value)时的指定对齐值value。
3)结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
4)数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的那个值。

for(i=0;i<10;i )

p = a;//p加1后,p的步长是4,所以p现在指向了a[1][]

c语言中,有两种方式可以访问一个字符串,第一是:用字符数组存放一个字符串,然后输出。第二是,用字符指针指向一个字符串。

 

}

int a[100] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};

scanf(“%d,%d”,&a,&b);

int a[3][4];

for(p=a;p

temp=*p1;

以上基础入门篇的一部分学习,还有许多知识点需要进一步学习。

#include

指针和指针变量是不同的两个概念:指针是一个地址,而指针变量是存放地址的变量。

2通过指针引用数组变量

printf(“%d,%dn”,a,b);

for(i=0;i<10;i )

a=100;b=10;

printf(“%d,%dn”,a,b);

pointer_1=&a;

四、字符串与指针

}

*i_pointer_1=100;(不合法);

三、数组与指针

void main()

它的赋值方式int*p=&a[0]//把数组a0的地址赋值给p;

#include

比较:(1),(2)两种方法的时间复杂度相等,(3)比其他两种方法快。原因在于:用指针变量直接指向元素,不必每次都重新计算地址。

(2)通过数组名计算数组元素地址,找出元素的值

2指针变量的引用

指针变量中只能存放地址(指针),不要将一个整数(或者任何其他非地址类型的数据)复制给一个指针变量。

for(i=0;i<10;i )

#include

int * pointer_1,* pointer_2;

scanf(“%dn”,&a[i]);

(1)&:取地址运算符。

printf(“%dn”,a[i]);

void swap(int *p1,int *p2)//前边声明的函数

}

开始写Log的原因是因为想要去华为工作,现在进行知识点的恶补,因为好久没有使用c了。看看自己在准备的这个过程中能学到多少东西,进行总结,可以进行客观的学习效率评价。

如:通过指针变量访问整形变量。

变量的指针就是变量的地址,存放变量地址的变量是指针变量,他用来指向另外一个变量。在信息工程中,“*”符号表示“指向”的对象。如果定义i_pointer所指向的变量。

p1=p2;

3指针变量作为函数参数

{ int a,b;

#include

本文由9159.com发布于编程,转载请注明出处:参数是由右往左入栈的,内存单元的指针和内存

关键词: 9159.com