目录
(重点、难点)第七章 指针
一、特点
二、基本概念
内存&地址&指针变量
三、指针相关的运算符
&(取地址运算符)
*(解引用运算符)
四、指针的定义
五、指针的初始化
!!(使用空指针时)
!注意注意!
六、指针的算术运算
加 :+
减 :-
加加 :++
减减 :–
不同类型指针执行解引用操作
其他操作
七、!指针与数组(重要)
1、指针和数组的sizeof不同
2、指针和数组的取地址不同
3、使用指针访问,数组的元素
八、函数使用指针作为参数
1、值传递
2、地址传递
九、字符串数组的传参(地址传参)
1、字符串用指针表示刷出
2、字符串的有效长度 *strlen
3、字符串的复制 *strcpy
4、字符串的拼接 *strcat
5、字符串的比较 *strcmp
(重点、难点)第七章 指针
一、特点
- 编写代码更加简洁高效
- 指针可以直接访问内存,要访问合法的内存(不要越界访问)
- 指针可以直接操作硬件,一般要通过软件操作硬件(51单片机、ARM开发板)
变量、数组,为了存储数据
指针,为了高效的访问数据,间接访问
二、基本概念
内存&地址&指针变量
- 内存,memory:
是程序运行起来后的一个 数据暂存 的场所。在32bit下这段空间是0-4G。C语言中,操作内存的时候,最小单位是1byte(1个字节)。code(代码段),data(数据段),heap(堆区),map/share(映射/共享区,c库函数在这里),stack(栈,局部变量、函数参数、返回地址在这里)
- 地址:
由于地址空间比较大,c语言中,最小操作单位是1byte,为了方便访问内存,给每个内存都分配了一个编号,被分配的编号叫内存地址。

- 指针变量:
指针变量是专门用来存储地址的变量。指针变量只装地址。
#include <stdio.h>
int main()
{
int a=20;
//指针的定义,你是整型变量,指针也要整型;指针与类型是相对应的
int *p = &a; // &a: 获得a变量在内存中的地址
printf("a address:%p\\n",&a);
printf("p:%p\\n",p);
printf("p size :%lu\\n",sizeof(p));
return 0;
}
a address: 0x7ffde8d46a1c p: 0x7ffde8d46a1c p size : 8
- a占4个字节,存放在0x7ffde8d46a1c这个地址,可以直接访问a,也可以间接通过指针访问,在64bit系统下,所有指针都是8个字节,指针里面装的是a的地址。
三、指针相关的运算符
-
&(取地址运算符)
&:获得变量在内存空间中的地址
int num = 0;
# //地址,从这个地址开始往后四个字节的空间都是num的(因为int是4个字节)
double num1=0;
&num1; //从这个地址开始往后8个字节都是num1的
-
*(解引用运算符)
只能是指针类型才需要解引用。
*:先找到对应的内存地址值,获得内存地址空间中的内容
第一个*是类型说明符,第二个*(指针访问被指向的对象)才是解引用
*p; 值是对应内存空间中的数据,类型是int,*p在实际操作中既可以做左值也可以做右值。
*p = 10; // 给a写入数据,*p做的是左值
int a=20;
int *p= &a; //左边定义指针,指针里面存地址空间,把a的地址装进去
a; //20,直接访问
*p; //20,间接访问,值是对应内存空间中的数据,类型是int
int b=*p; //把*p对应的值装进去,这里*p做的是右值,读取*p也就是a的内容
*&a; 20,会产生临时的指针变量, & 和 * 是相反操作的运算符。
int p;
int *p; //p int –>int *
*p; //int *–>int
四、指针的定义
基类型 * 指针名;
int *p1; //int * char *p2; //char * double *p3; //double *
float *p4; //float *
#include <stdio.h>
int main()
{
int a=10;
printf("a %p\\n",&a);
//定义指针变量,注意这里的*不是解引用,是类型说明符
int *p1;
char *p2;
double *p3;
float *p4;
//64bit操作系统,所有的指针都是8个字节
printf("int * sizeof %lu\\n",sizeof(p1));
printf("char * sizeof %lu\\n",sizeof(p2));
printf("double * sizeof %lu\\n",sizeof(p3));
printf("float * sizeof %lu\\n",sizeof(p4));
return 0;
}
五、指针的初始化
指针需要初始化后才可以使用,指针必须和某个内存区域关联后才可以使用
- 野指针:未经初始化的指针或者,指针指向的内存空间是一个已经被释放的空间的指针 int *p; //野指针,p中存储的地址是一个随机值,从逻辑上来说,这个值不能读也不能写。
- 空指针:NULL(宏定义),在stdio.h中。 int*p1=NULL; //空指针,NULL==((void*)0),指针已经初始化,稍后才会关联一个变量的地址。在没有关联变量地址之前,也不能读不能写。
#include <stdio.h>
int main()
{
int a=10;
printf("a %p\\n",&a);
int *p1; //int *
char *p2; //char *
double *p3; //double *
float *p4; //float *
#if 0
//这个地方的代码,有崩溃的风险
printf("p1 %d\\n",*p1); //p1:野指针
*p1 = 20;
#endif
//定义一个变量,现在p5是空指针
int *p5=NULL; // NULL是0地址,也就是 ((void*) 0)
int *p6=&a;
printf("p6 addr %p\\n",p6);
printf("a addr %p\\n",&a);
printf("a value:%d\\n",*p6); //通过指针,间接读取a变量的值
*p6 =50; //左值,修改变量
printf("修改后a value:%d\\n",*p6);
printf("a value:%d\\n",a);// 直接访问a变量的值
return 0;
}
!!(使用空指针时)
int d=10;
int* p=NULL;
p=&d; //取地址d给p
*p=d; //关联上,就可以取值了
printf("p %p\\n",p);
printf("%d\\n",*p);
!注意注意!
如果先写*p=d,两个还没有关联,不能这样操作!
六、指针的算术运算
+ – * / % ++ —
* / %,相对于指针而言,无意义
int a =20;
int* p1=&a;
+,p1+1; //依然是指针,往后走一个基类型(int类型有4个字节),如果一个指针需要+1、-1,一般要和数组操作的时候才会使用。
加 :+
两指针单独相加无意义
+,p+1(数组),p(存储某个变量的地址)+q(存储另外一个变量的地址)==无意义
- 不同指针类型在做+1时候的区别:
| 指针类型+1 | 偏移量 |
| char* | 1个字节 |
| int* | 4个字节 |
| float* | 4个字节 |
| double* | 8个字节 |
减 :-
p-1 p-2; p应该指向一个数组,操作完成后,指针应该落在数组范围以内
int a[10]={1,2,3,4,5,6,7,8,9,0};
int* p=a+1;
int* q=a+5;
printf("q-p:%ld\\n",q-p);
- 两个地址相减,前提是这两个指针指向同一个数组。
- 意义:相差的元素个数。
- 另外,如果这两个指针指向其他不同的地方,相减无意义
加加 :++
p++;先获得当前地址,加上sizeof(基类型),计算的结果再写回指针变量本身。
减减 :–
p–;先获得当前地址,减去sizeof(基类型),计算的结果再写回指针变量本身。
不同类型指针执行解引用操作
| 不同类型指针解引用 | 操作 |
| char* | 执行解引用操作,从指针存储的地址开始,往后(地址变大的方向)取1个字节的数据 |
| int* | 执行解引用操作,从指针存储的地址开始,往后(地址变大的方向)取4个字节的数据 |
| float* | 执行解引用操作,从指针存储的地址开始,往后(地址变大的方向)取4个字节的数据 |
| double* | 执行解引用操作,从指针存储的地址开始,往后(地址变大的方向)取8个字节的数据 |
char str[100]="hello";
char* p=str;
*p :从存储地址的开始,往后走一个字节的数据
其他操作
指针在使用的时候,有两个概念需要注意:(1)指针变量本身(2)指针指向的内容

对指针变量的修改

*p =123; 修改指针指向的内容
p=b; 修改指针的指向
七、!指针与数组(重要)
数组&指针 :数组名与指针名息息相关
printf("a[0] %d\\n",a[0]); printf("point *(p+0) %d\\n",*(p+0)); printf("*(a+0) %d\\n",*(a+0)); printf("point p[0] %d\\n",p[0]);
- 数组的数组名,是一个指向数组中第一个元素的指针常量,数组一旦定义好,a中存储的地址不能发生变化。数组名的本质就是一个指针常量,需要注意的是:这个指针变量已经加了一些限制,让指针中存储的地址不能改变。(const)
- int* ~= int[ ]
1、指针和数组的sizeof不同
- sizeof 数组: int a[50]; sizeof(a)==sizeof(int)*50;
- sizeof 指针: int* p; sizeof(*p)永远是8byte;
2、指针和数组的取地址不同
- & int* 执行取地址操作 变成int**(二级指针),存储地址的地址
- int[ ]执行取地址操作,变成int(*p)[ ],数组指针,指向二维数组中的一行
3、使用指针访问,数组的元素
访问元素的方式:a[n] –> *(p+n) –> *(a+n) –> p[n]
#include <stdio.h>
int main()
{
int a[5]={1,3,5,7,9};
printf("a addr %p\\n",a);
printf("a[0] addr %p\\n",&a[0]);
int* p =a;
printf("a[0] %d\\n",a[0]);
printf("point *(p+0) %d\\n",*(p+0));
printf("*(a+0) %d\\n",*(a+0));
printf("point p[0] %d\\n",p[0]);
printf("a[0] addr %p\\n",&a[0]);
printf("point *(p+0) addr %p\\n",&(*(p+0)));
printf("*(a+0) addr %p\\n",&(*(a+0)));
printf("point p[0] addr %p\\n",&p[0]);
/*
printf("a[1] %d\\n",a[1]);
printf("point a[1] %d\\n",*(p+1));
printf("a[4] %d\\n",a[4]);
printf("point a[4] %d\\n",*(p+4));
*/
return 0;
}
八、函数使用指针作为参数
1、值传递
形参是实参的复制品,只能读取实参的数据,在被调函数内部,无法修改形参而改变实参。
涉及的变量有:基本数据类型的变量、构造类型的变量
2、地址传递
形参会接收到实参传递过来的地址,就可以在被调函数的内部,通过间接访问的方式,可以通过修改形参来修改实参。传递数组、指针。

eg:两数交换用函数,值传递无法进行交换,地址传递可以通过间接访问,修改形参来改变实参,显而易见,swap()是一个值传递,在函数运行完成后,swap()释放,无法进行main函数中的两数交换;swap2()是一个地址传递,可以成功交换两个数。
#include <stdio.h>
void swap(int a,int b)
{
int t = a;
a = b;
b =t;
}
void swap2(int *pa,int *pb)
{
int t = *pa;
*pa = *pb;
*pb =t;
}
int main()
{
int a = 10;
int b =20;
//swap(a,b);
swap2(&a,&b);
printf("a :%d b:%d\\n",a,b);
return 0;
}
九、字符串数组的传参(地址传参)
1、字符串用指针表示刷出
void myshow(char* pstr)
{
/*
int i = 0;
for(i=0;'\\0'!=pstr[i];i++)
{
printf("%c",pstr[i]);
}
*/
while(*pstr)
{
printf("%c",*pstr);
pstr++;
}
return ;
}
2、字符串的有效长度 *strlen
int mystrlen(char* pstr)
{
int len= 0;
while(*pstr)
{
len++;
pstr++;
}
return len;
}
3、字符串的复制 *strcpy
方法1
int mystrcpy(char* dst,char* src)
{
while(*dst=*src)
{
src++;
dst++;
}
方法2
int mystrcpy(char* dst,char* src)
while(1)
{
*dst=*src;
src++;
if('\\0'==*src)
{
break;
}
dst++;
}
//*(dst+1)='\\0';
*(++dst) ='\\0';
return 0;
}
4、字符串的拼接 *strcat
int my_strcat(char* dst,char* src)
{
while(*dst)
{
dst++;
}
while(*dst=*src)
{
src++;
dst++;
}
return *dst;
}
5、字符串的比较 *strcmp
int my_strcmp(char* str1,char* str2)
{
while(*str1==*str2 && *str1) //解引用,指针内容
{
str1++; //指针往后走
str2++;
}
return *str1-*str2;
}
//main函数里
int ret=my_strcmp(str,dst);
if(0==ret)
{printf("same\\n");
}
else
{
printf("not same\\n");
}
十、数组名与普通指针访问数据的注意事项
int main() { char str[100]="hello"; char* p=str; /* while(*p) { printf("%c",*p); p++; //可以++、–操作,p本身被修改 }
*/
/*
while(*str) { printf("%c",*str); str++; //数组名,一个指针常量,本身不可以++、–。当数组被定义好后,str是一个地址标号,和开辟的内存空间的编号,终身绑定,不能修改。 }
*/
show(str); }


