🌿前言:在我看来,书应该越读越薄的,所以我的笔记尽量记录了每一个点,可供复习与查阅,但没有详细的解释。我的C语言的笔记是在谭浩强老师的C程序设计(第五版)的基础上总结归纳的,结合了一些我自己的见解。如果是有其他见解 ,也欢迎大家提出。
一、指针
· 编译系统根据程序中定义的变量类型,分配一定长度,每一个字节有一个编号,这就是地址。地址指向该变量单元。地址形象化地称为指针。
二、指针变量
1.指针变量:专门用来存放另一变量的地址的变量
2.指针变量的定义:
类型名 *指针变量名
类型名又称基类型(指定此指针变量可以指向的变量的类型)
3.一个变量的指针的含义
(1)以存储单元编号表示的纯地址(如编号为2000的字节)
(2)是它指向的存储单元的数据类型(如int,char等)
4.怎样引用指针变量
(1)给指针变量赋值
p=&a;
(2)引用指针变量指向的变量
①
p=&a;
printf(“%d”,*p);
②
*p=1;
(表示将1赋给p当前指向的变量)
(3)引用指针变量的值
printf(“%d”,p);
Ps:
① & 取地址运算符 &a是变量a的地址
②*指针运算符 *p代表指针变量p指向的对象
5.指针变量作为函数参数
三、通过指针引用数组
1.引用数组元素的方法
(1)下标法
(2)指针法
2.在引用数组元素时指针的运算
在指针已指向一个数组元素时,可以
(1)加一个整数(+或+=),如p+1;
(p+1指向同一数组中的下一个元素)
(p–1指向同一数组中的上一个元素)
(*(a+i)是a+i所指向的数组元素即a[i])
(2)自加(p++,++p)
自减(p––,––p)
(3)两个指针相减p-q
(如果p和q都是指向同一数组中的元素,如执行p-q,结果是p-q的值除以数组元素的长度)(可以知道它们所指元素的相对距离)
3.通过指针引用数组元素
(1)通过数组名计算数组元素地址,找出元素的值。
#include<stdio.h>
int main()
{
int a[10];
int i;
printf("num:");
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(i=0;i<10;i++)
printf("%d",a[i]);
printf("%\n");
return 0;
}
可以用scanf(“%d”,a+i);代替scanf(“%d”,&a[i]);
可以用printf(“%d”,*(a+i));代替printf(“%d”,a[i]);
(2)用指针变量指向数组
#include<stdio.h>
int main()
{
int a[10];
int *p,i;
printf("num:");
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(p=a;p<(a+10);p++)
printf("%d",*p);
printf("%\n");
return 0;
}
ps:指针变量也是可以带下标的
当p指向a[3],p[2]代表a[5]
(3)一些奇奇怪怪的运算
①
p++;
*p;
p++指向下一元素a[1];再执行*p得到下一元素a[1]
②
*p++;
③
*(p++);
或
*(++p);
若p初值为a(即a[0]),则前者得到a[0],后者得到a[1]
若p在前面则先取*p,再使p加1
若p再后面则先加再取
④
++(*p);
表示p所指向的元素值加一;
++(*p)相当于++a[0]
若a[0]为3,则输出4;
4.用数组名作函数参数
(1)以变量名和数组名作为函数参数的比较
实参类型 | 变量名 | 数组名 |
---|---|---|
要求形参的类型 | 变量名 | 数组名或指针变量 |
传递的信息 | 变量的值 | 实参数组首元素的地址 |
通过函数调用能否改变实参的值 | 不能 | 能 |
(2)实参数组名代表一个固定的地址,或者说是指针常量,但形参数组名不是固定地址,而是按指针变量处理
5.通过指针引用多维数组
(1)多维数组的地址
表示形式 | 含义 | 值 |
---|---|---|
a | 二维数组名,指向一维数组a[0],即0行起始地址 | 2000 |
a[0] , *(a+0) , *a | 0行0列元素地址 | 2000 |
a+1 , &a[1] | 1行起始地址 | 2016 |
a[1] , *(a+1) | 1行0列元素a [1] [0]的地址 | 2016 |
a[1]+2 , *(a+1)+2 , &a [1] [2] | 1行2列元素a [1] [2]的地址 | 2024 |
* (a[1]+2) , * ( *(a+1)+2),a [1] [2] | 1行2列元素a [1] [2]的值 | 是元素为13 |
ps:*(a+i)与a[i]等价
(2)指向多维数组元素的指针变量
A.指向数组元素的指针变量
B.指向由m个元素组成的一维数组的指针变量
①int(*p)[4] 表示定义p为一个指针变量,它指向包含4个整型元素的一维数组。p的值是该一维数组的起始地址,这个地址的纯地址与该一维数组首元素的地址相同,但他们的基类型不同。(*p[4]是指针数组,所以括号不能少)
②
int(*p)[4] ;
p=&a;
不能写成p=a;,这样指向首元素a[0]。
p=&a;表示p指向一维数组
(*p)[3]表示p指向行中序号为3的元素
(3)用指向数组的指针作函数变量
用指针变量作形参,以接受实参的数组名传递来的地址方法有
①用指向变量的指针变量
②用指向一维数组的指针变量
四、通过指针引用字符串
1.字符串的引用方式
(1)字符数组
(2)字符指针变量
2.字符指针作为函数参数
(1)用字符数组名作为函数参数
(2)用字符型指针变量作实参
(3)用字符指针变量作形参和实参
实参 | 形参 | 实参 | 形参 |
---|---|---|---|
字符数组名 | 字符数组名 | 字符指针变量 | 字符指针变量 |
字符数组名 | 字符指针变量 | 字符指针变量 | 字符数组名 |
3.使用字符指针变量和字符数组的比较
(1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址
(2)可以对字符指针变量赋值,但不能对数组名赋值
(3)数组可以在定义时对各元素赋值,但不能用赋值语句对字符数组中全部元素整体赋值
(4)编译时为字符数组分配若干存储单元
(5)指针变量是可以改变的,但字符数组名代表一个固定值(数组首元素的地址)不能改变
(6)可以对字符数组再赋值,但不能对字符指针变量再赋值
(7)用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串.
只要改变指针变量所指的字符串,就可以改变输入输出格式。(也可用字符数组实现)
如:
char *format;
format=”a=%d,b=%f\n”;
printf(format,a,b);
相当于
printf(“a=%d,b=%f\n”,a,b);
五、指向函数的指针
1.函数的指针
(函数名代表函数的起始地址,函数名就是函数的指针)
int(*p)(int,int);
2.用函数指针变量调用函数
(1)通过函数名调用函数
(2)通过指针变量调用它所以指向的函数
3.怎样定义和使用指向函数的指针变量
类型名(*指针变量名)(函数参数表列);
(1)如果要用指针调用函数,必须先使指针变量指向该函数
(2)在给函数指针变量赋值时,只需给出函数名而不必给出参数
p=max;
4.用指向函数的指针作函数参数
指向函数的指针变量的一个重要途径是把函数的入口地址作为参数传递到其他函数。
六、返回指针值的函数
定义返回指针值的函数的原型的一般形式:
类型名 *函数名(参数表列);
七、指针数组和多重指针
1.指针数组
类型名 *数组名[数组长度];
如:
char *name[]={“sbjzxm”,”scxaj”,”cdbzjkm”};
2.指向指针数据的指针变量
定义
char **p;
3.指针数组作为main函数的形参( int main ( ) )
(1)括号中空或为void,表示main函数没有参数,调用时不必给出实参
(2)main函数可以有参数
int main(int argc , char *argv[ ])
(agrc和agrv为main函数的形参,是程序的命令行参数。agrc是参数个数(必须为int型),agrv是一个字符指针数组,数组中的每一个元素指向命令行中的一个字符串的首字符)
(3)命令行的一般形式
命令名 参数1 参数2 参数3……参数n
命令名是可执行文件名
文件名也是一个参数 argc里要加上
八、动态内存分配与指向它的指针变量
1.内存的动态分配
栈:动态存储区
堆:用户自己建立的内存动态分配区域,以存放一些临时用的数据。由于未在声明部分定义它们为变量或者数组,所以只能用指针来引用
2.建立内存的动态分配区域
(1)malloc——在内存的动态存储区中分配1个为size的连续空间
函数原型:void *malloc(unsigned int size);
(指针的基类型为void,不指向任何类型的数据,只提供一个纯地址,如果函数未能成功执行,则返回空指针)
(2)calloc——在内存的动态存储区中分配n个为size的连续空间
函数原型:void *calloc(unsigned n,unsigned size);
(空间较大,能存一个数组)
(3)recalloc——重新分配动态存储区
函数原型:void *recalloc(void *p,unsigned int size);
(4)free——释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用
函数原型:void free(void *p);
Ps:以上四个函数包含在stdlib.h头文件中,用的时候要把头文件包含进去
3.void指针类型
void类型 指向空类型或者不指向确定的类型
Ps:①指针变量可以有空值
p=NULL
(系统保证使该单元不作它用,空值与不赋值是两个概念)
补充
①int *P中 , *P和P的差别。[简单说: *P是数值,P是地址。]
*P可以当作变量来用;
*的作用是取后面的地址P里面的数据。
P是当做地址来使用,可以用在scanf函数中:scanf(“%d”,P);
②*P++和( *P)++的区别。
*P++是地址发生变化。[指向当前位置,然后再移动地址]
(*P)++是数值发生变化。[取当前值,然后再使数字值加1]
③三名区别。[数组名、函数名、字符串常量名]
数组名:表示第一个元素的地址,数组名不可以自加,它是地址常量名,亦不可被赋值。
函数名:表示该函数的入口地址。
字符串常量名:表示第一个字符的地址。
④指针的移动.
char *S="meikanshu"
while(*S)
{
printf(“%c”,*S);
S++;
}
首先指向第一个字母m,然后循环输出一个字符,S++是地址移动,输出m后,移动到下一个字母.
⑤指针变量的初始化。
A.
int a=8,*P=&a; //定义的同时初始化
B.
int a=2,*P;
P=&a; //定义之后初始化(其前不加*)
C.数组:
int a[5],*b=a;
int a[5],*b=&a[0]; //地址对地址
D.
int a,*b,*c;
b=&a;
c=b;//指针变量之间,可以互相赋值,但是一定要是同类型的指针变量。
E.
int a=5,*b,*c;
b=&a;
c=b+4;
指针的加减运算,是对变量地址的加减,与变量的值无关,且不同类型的变量,加减地址与所占字符空间大小一致,
即 int/float,每次地址加/减4字节;
char,每次加减1个字节.
⑥指针与数组。
· 引用一:b间接引用a[0],(b+2)间接引用a[2]
· 引用二:b[0]等价于a[0],b[2]等价于a[2].
即指针加下标表示访问当前指针指向的位置。
· 引用三:a等价于a[0],(a+2)等价于a[2],
数组名为首地址。
⑦指针与字符串
A.指向方法
char *p="hello";
表示在内存中,给“hello”创建空间,然后用P指向其首元素。
B.若用函数输出,则从指针指向的字符串中的元素, 开始输出,遇\0结束。
⑧指针与函数,[传值和传地址]
· 指针、数组、swap( )
这三种对内存空间操作,会对主调函数的实参产生影响。「即地址变了,其他正常」
这时无需return返回值。