目录
一、基本概念
1.基本用法
C程序设计中使用指针可以
-使程序简洁、经凑,高效
-有效地表示复杂的数据结构
-动态分配内存
-得到多于一个的函数返回值
2.地址和变量
地址和变量
在计算机内存中,每个字节单元,都有一个编号,称为地址。
1Byte =8bit。
看到的地址是起始地址,编译或函数调用时为其分配内存单元,变量是对程序中数据存储空间的抽象。
int a = 8;
0 | 数据 |
... | 数据 |
2000 | 8 |
2001 | 数据 |
2002 | 数据 |
2003 | 数据 |
3.指针
在C语言中,内存单元的地址称为指针,专门用来存放地址的变量,称为指针变量。
在不影响理解的情况中,有的对地址、指针和指针变量不区分,通称指针。
指针变量的说明
一般形式:
<存储类型> <数据类型> *<指针变量名>;
例如,auto char *pName (放的内容是char类型)
指针的存储类型是指针变量本身的存储类型。
指针说明时指定的数据类型不是指针变量本身类型,而是指针目标的数据类型。简称为指针的数据类型
指针的初始化
指针在说明的同时,也可以被赋予初值
一般形式:
<存储类型> <数据类型> *<指针变量名> = <地址量>;
例如:
int a, *pa=&a;
在上面语句中,把变量a的地址作为初值赋予了刚说明的int类型指针pa。
int a =3; //int a; a =3;
int *pa = &a; //int *pa; pa = &a;
#include <stdio.h>
int main(int argc,char *argv[]){
int a = 10;
int *p;
p = &a;
printf("&p:%p %d\n", &p,sizeof(int *));
printf("%p %p\n", p, &a);
return 0;
}
&p:0xbfa9364c 4
0xbfa93648 0xbfa93648 //指针变量p的地址,就是变量a的地址;
指针的目标
指针指向的内存区域中的数据称为指针的目标
如果它指向的区域是程序中的一个变量的内存空间,则这个变量称为指针的目标变量。简称为指针的目标。
引入指针要注意程序中的px、*px和&px三种表示方法的不同意义。设px为一个指针,则:
px-指针变量,它的内容是地址量
*px-指针所指向的对象,它的内容是数据
&px-指针变量占用的存储区域的地址,是个常量
#include <stdio.h>
int main(int argc,char *argv[]){
int a = 10;
int *p;
p = &a;
printf("%d %d\n", a, *p);
return 0;
}
10 10
指针的赋值
是通过赋值运算符向指针变量送一个地址值
向一个指针变量赋值时,送的值必须是地址常量或指针变量,不能是普通的整数(除了赋零以外)
//1
doublx x =15, *px;
px = &x; //类型相同,都是double
//2
float a, *px,*py;
px = &a;
py = px;
//3
int a[20],*pa;
pa = a; //等价于pa
总结
什么是指针?内存以字节为单位编号,内存单元的地址称为指针,专门用来存放地址的变量,称为指针变量。
指针占几个字节?32位系统都是4个字节。
操作系统和内存有多少个地址线?32根地址线需要4G的内存,这里可以根据计算公式来得到结果,比如32根地址线的计算结果是2^32=4*1024mb=4G,所以它应该用4G内存。一般而言内存上地址线会有64个,最多支持128bit,所以无论内存多大,地址线的总数是有限制的
二、指针的运算
1.指针运算的意义
不同数据类型的两个指针实行加减整数运算是无意义的
px+n表示的实际位置的地址量是:(px)+sizeof(px的类型)*n
px-n表示的实际位置的地址量是: (px)-sizeof(px的类型)*n
运算符 | 计算形式 | 意义 |
+ | px+n | 指针向地址大的方向移动n个数据 |
- | px-n | 指针向地址小的方向移动n个数据 |
++ | px++或 ++px | 指针向地址大的方向移动1个数据 |
-- | px-- 或 --px | 指针向地址小的方向移动1个数据 |
- | px-py | 两个指针之间相隔数据元素的个数 |
q先赋值,后p++ 所以还是之前的值 ,如果是++p就不一样了
#include <stdio.h>
int main(int argc,char *argv[]){
int a[5] = {4, 8, 1, 2, 7};
int *p,*q;
p = a; //&a[0]
printf("%p %d\n", p, *q);
q = p++;
printf("%p %d\n", p, *p);
printf("%p %d\n", q, *q);
return 0;
}
0xbfa44cec -1079745597
0xbfa44cf0 8
0xbfa44cec 4 //q先赋值,后p++ 所以还是之前的值 ,如果是++p就不一样了
2.两指针相减运算的意义
px-py运算的结果是两指针指向的地址位置之间相隔数据的个数。因此,两指针相减不是两指针有的地址值相减的结果。
-两指针相减的结果值不是地址量,而是一个整数值,表示两指针之间相隔数据的个数。(一般结合数组,用的频率才高)
#include <stdio.h>
int main(int argc,char *argv[]){
int a[5] = {4, 8, 1, 2, 7};
int *p,*q;
p = a;
q = &a[3];
printf("%p %p\n", p, q);
printf("%d %d\n", *p, *q);
printf("%d\n", q-p);
return 0;
}
0xbfdd15bc 0xbfdd15c8
4 2
3
3.指针的关系运算符
-两指针之间的关系运算表示它们指向的地址位置之间的关系。指向地址大的指针大于指向地址小的指针。
-指针与一般整数变量之间的关系运算没有意义。但可以和0进行等于或不等于的关系运算,判断指针是否为空。
> < >= <= != ==
#include <stdio.h>
int main(int argc,char *argv[]){
int *p= NULL;
printf("%p\n",p);
return 0;
}
(nil)
程序举例1
第一步 --p //指向a[0]
第二步 *--p //取地址里数据
第三部 (*--p)++// 先赋值,后数据++
#include <stdio.h>
int main(int argc,char *argv[]){
int a[]={5,8,7,6,2,7,3};
int y,*p = &a[1];
y = (* --p)++;
printf("%d %d",y,a[0]);
}
5 6
程序举例2
指针可以代替数组下标
#include <stdio.h>
int main(int argc,char *argv[]){
int i, *p, a[7];
p = a;
for(i = 0; i<7; i++)
scanf("%d",p++);
printf("\n");
p = a;
for(i = 0; i<7; i++){
printf("%d",*p);
p++;
}
}
三、指针与数组
1.简述
在C语言中,数组的指针是指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的起始地址
一维数组的数组名为一维数组的指针(起始地址)
如double x[8];x为x数组的起始地址
设指针变量px的地址值等于数组指针x(既指针变量px指向数组的首元素),则:
x[i]、*(px+i)、*(x+i)和px[i]具有完全相同的功能:访问数组第i+1个数组元素。
2.数组名和指针变量
x[i] 和 *px
x++会报错,因为是地址常量,但是x+1,x+2可以
但是指针px++可以。
虽然都是一样,但是一个是数组名是常量,一个是指针是变量
下标法:x[i]
指针法: *(px+i)
注意
指针变量和数组在访问数组中元素时,一定条件下其使用方法具有相同的形式,因为指针变量和数组名都是地址量
但指针变量和数组的指针(或叫数组名)在本质上不同,指针变量是地址变量,而数组的指针是地址常量。
程序举例
编写一个函数,将整型数组中n个数按反序存放
#include <stdio.h>
int main(int argc,char *argv[]){
int buf[10]={0};
int *p,*q,temp,n,i;
n = 10;
printf("please input:\n");
for(i = 0; i < n; i++){
scanf("%d",&buf[i]); //循环接受数字
}
p = buf;
q = &buf[n-1];
//冒泡算法
while(p < q)
{
temp = *p;
*p = *q;
*q = temp;
p++;
q--;
}
}
2,1 Top
3.公式结论
a[i] <<==>>*(a+i)
四、指针与二维数组
1概念
多维数组就是具有两个或两个以上下标的数组
在c语言中,二维数组的元素连续存储,按行优先存。
2遍历
#include <stdio.h>
int main(int argc,char *argv[]){
int a[3][2] = {{1,6},{9,12},{61,12}};
int *p, i, n;
n = sizeof(a) / sizeof(int);
p = a;
printf("%p %p\n", p, p+1);
printf("%p %p\n", a, a+1);
for(i = 0; i < n; i++)
printf("%d ",*(p+i));
puts("");
return 0;
}
//结果
0xbfbd2c08 0xbfbd2c0c
0xbfbd2c08 0xbfbd2c10
1 6 9 12 61 12
用一级指针遍历二维数组,p = a 类型不匹配。p是1级, a是两级,改成&a[0][0] 就没警告了,也可以写p=a[0],a[0]也是数组名。a[0]++会报错,常量不能放等号左边
3行指针
二维数组名代表数组的起始地址,数组名+1,是移动一行元素。因此,二维数组名常被称为行地址
a[0] | a[0][0] |
a[0][1] | |
a[0][2] | |
a[1] | a[1][0] |
a[1][1] | |
a[1][2] | |
a[2] | a[2][0] |
a[2][1] | |
a[2][2] |
行指针(数组指针)
存储行地址的指针变量,叫做行指针变量。形式如下:
-<存储类型> <数据类型> (*<指针变量名>)[表达式];
例如: int a[2][3];
int (*p)[3];//行指针
方括号中的常量表示是表示指针加1,移动几个数据。
当用行指针操作二维数组时,表达式一般写成1行的元素个数,既列数。
程序举例:遍历二维数组,二维数组指针和行地址概念
#include <stdio.h>
int main(int argc,char *argv[]){
int a[3][2] = {{1,6},{9,12},{61,12}};
int i, j;
int (*p)[2];
p = a;
printf("%p %p\n", a[0], a[0]+1);//
printf("%p %p\n", a, a+1); //a和p是行地址指针变量
printf("%p %p\n", p, p+1); //a和p是行地址指针变量
printf("%d %d %d %d\n",a[1][1],p[1][1],*(*(a+1)+1),*(*(p+1)+1));
//遍历二维数组
for(i = 0; i < 3; i++)
for(j = 0; j < 2; j++)
printf("%d %d %d %d\n",a[i][j],p[i][j],*(*(a+i)+j),*(*(p+i)+j));
puts("");
return 0;
}
0xbfe98698 0xbfe9869c //+4
0xbfe98698 0xbfe986a0 //+8
0xbfe98698 0xbfe986a0 //+8
12 12 12 12
1 1 1 1
6 6 6 6
9 9 9 9
12 12 12 12
61 61 61 61
12 12 12 12
a[0]+1 =*a +1
如果移动一行一列*(*(a+1)+1)或*(*(p+1)+1) 等于a[1][1] 或p[1][1]
4.总结
主要介绍了指针与二维数组,包括一级指针如何访问二维数组以及行指针如何访问二维数组
二维数组特点:1地址常量,地址名+1是移动一行。编程,实现二维数组遍历。