1、指针
1.1 概念
指针就是地址
指针是一种数据类型,是一种保存地址的数据类型
int是一种数据类型,是一种保存整数的数据类型 1 2 3 4
float是一种数据类型,是一种保存浮点数的数据类型 3.14
1.2 什么是地址
内存分配的最小单位是字节,每一个字节都有一个编号,我们把整个编号就叫做地址
地址的本质:内存单元的编号
指针:指针就是地址
指针的本质:内存单元的编号
1.3 什么是指针变量
int a;
float b;
指针变量:专门原来保存地址(内存单元的编号)的变量
1.4 定义
存储类型 数据类型 *指针变量名;
int * p;
数据类型表示本指针变量所指向的变量的数据类型
存储类型表示本指针变量所指向的变量的存储类型
指针变量名是*后面的内容,*只是说明定义的是一个指针变量
只能指向相同数据类型的变量
int * p;
存储类型:auto、static、extern、register
数据类型:指针所执行的数据类型 //int
指针数据类型(去掉指针变量名):数据类型 * //int*
*在c语言中有3种用法:
1、作为双目运算符,表示乘法 3*4
2、在定义变量的时候使用,表示指针这种数据类型
3、作为单目运算符,表示取值 *p(取指定变量p所存储的地址) 通过指针间接访问指针所指向的对象
在C语言中,变量的地址是由编译系统分配的,用户不知道变量的具体位置。C语言中提供了地址运算符“&”来表示变量的地址,其一遍形式为 &变量名;
int i,*p;
p = &a;
注意:
1、指针的指向是可以改变的
2、给指针赋值时,要注意数据类型的匹配
&是取地址运算符 *(地址)是取值运算符
3、&和*互为逆运算,可以相互抵消,正和负的关系
4、&为引用,*为解引用
在32OS系统中,所有的指针都占4个字节
在64OS系统中,所有的指针都占8个字节
思考
1、什么是指针
指针是一种数据类型,是一种保存地址的数据类型(内存单元的编号)
2、什么是地址
内存单元的编号
3、什么是指针变量
专门用来保存指针的变量
4、如何定义一个指针变量
存储类型 数据类型 * 指针变量名
5、如果给指针变量赋值
p = &a
6、赋值之后可以做什么呢
修改、操作变量a
2、空指针
空指针:int * p = NULL;
没有指向的指针(值为0的指针,就认为该指针没有指向)
注意:0号地址禁止操作(一旦操作会出现段错误//Segmentation fault (core dumped))非法访问内存空间。
要操作必须改变空指针的指向
3、野指针
int * p;
不知道指向哪里的指针
局部变量没有初始化的时候,其值为随机值
局部指针变量没有初始化,就成了野指针
空指针操作不会出问题,野指针操作可能会出问题(随机值)//不清楚具体指向,修改取值会报错
如何避免野指针的出现?初始化为NULL
4、gdb的调试
Linux中调试使用调试器gdb
编辑器:vim
编译器:gcc
调试器:gdb
调试步骤
(1)编译程序的时候添加 "-g"参数
gcc -g error.c -o error
(2)启动gdb调试器
gdb 可执行文件的名字 (gdb error)
(3)设置断点
b main
b 行号
(4)运行
r
(5)其它参数
n(next):下一步,不进入子函数
s(step):下一步,进入子函数
p(printf) a(要打印的值)
c(continue):可以直接跳出循环,执行下一步
q(quit):退出
5、值传递
编写一子函数,实现两个数的交换
值传递:以下类型变量作为函数参数传递,包括基本数据类型变量(例如int、char、double等)、结构体类型变量。被调函数中对形参值的修改,不影响主调函数中的实参值。
值传递:类似物体的克隆,被调函数操作克隆的物体,主调函数操作源物体。本质是形参的地址空间与实参的地址空间不同。
6、地址传递
include <stdio.h>
void swp(int * m, int * n);
int main(void)
{
int a = 1;
int b = 2;
swp(&a,&b);
printf("%d\n",a);
printf("%d\n",b);
return 0;
}
void swp(int * m, int * n)
{
int tmp =0;
tmp = *m;
*m = *n;
*n = tmp;
}
地址传递:以下类型变量作为函数参数传递,包括数组名、指针或地址。被调函数中对形参值的修改,会影响主调函数中的实参值。
地址传递:类似物体的移动,两个函数先后操作同一个物体。本质是形参的地址空间与实参的地址空间相同。
7、const修饰的指针
const:只读
用来修饰变量,使用const修饰的变量只能读,不能被修改(未修改存储位置,仍在栈区)
int a = 10;//存放在栈区
const int a = 10;//
判断const修饰的变量是否在常量区
const修饰的指针:
指针变量:
(1)指针常量: 指针的指向不能发生改变
int * const p = NULL;//值可改,指向不可改
(2)常量指针: 指针所指向的内容不能被修改
int const *p = NULL;//指向可改,值不可改
(3)值和指向都不能修改
const int * const p = NULL;//值和指向都不能被修改
8、二级指针
8.1 概念
二级指针:指针的指针
二级指针的内存空间存放的是一级指针变量的地址
8.2 定义
一级指针: 存储类型 数据类型 *指针变量名;
数据类型:指针所指向的数据类型
一级指针:列指针,*一级指针:取值
二级指针:行指针,*二级指针:变为一级指针(列指针、一维数组名)
二级指针的定义:
存储类型 数据类型 **指针变量名;
二级指针的数据类型:数据类型 **
p = &a;
pp = &p;
pp = &&a;//不能对a的地址常量取地址
案例
(1).若有语句int *point, a=4;point=&a;下面均代表a的地址的一组选项是(D)
A. a, point,*&a B. &*a, &a,*point
C. &point, *&point, &a D. &a, &*point, point
(2).已有定义int k=2; int*ptr1,*ptr2;且ptr1和ptr2均已指向变量k,下面不能正确执行赋值语句的
是(B)
A. k=*ptr1+*ptr2; B.ptr2=k;
C.ptr1=ptr2; D. k=*ptr1*(*ptr2);
总结
1、指针指向的数据类型就是将变量名和*(一个)去掉,剩下的就是指针所指向的数据类型
int *p; // int
int **p; //int *
int ***p;//int **
2、指针的数据类型就是就变量名去掉,剩下的就是数据类型
int *p;//int *
int **p;//int **
int ***p;//int ***
3、*p所能访问的空间大小,有p指向的数据类型来决定
char *p;//*p所能访问的内存空间的大小为1byte
int *p;//*p所能访问的内存空间的大小为4byte
int **p;//**p所能访问的空间的大小为4byte
作业
int a[5] = {0};
1-------数组的输入
2-------数组的输出
3-------求数组中的次大值
4-------排序
-1 -----exit
#include <stdio.h>
#define N 5
#include <stdlib.h>
void menu();
void input();
void output();
void sub();
void swap();
void quit();
int main(void)
{
int fun = 0;
int a[N] = {0};
while(1)
{
menu();
printf("请选择功能");
scanf("%d",&fun);
switch(fun)
{
case 1:
input(a);
break;
case 2:
output(a);
break;
case 3:
sub(a);
break;
case 4:
swap(a);
break;
case -1:
quit();
}
}
return 0;
}
void menu()
{
printf("功能菜单\n");
printf("1-数组的输入\n");
printf("2-数组的输出\n");
printf("3-求数组中的次大值\n");
printf("4-排序\n");
printf("-1-exit\n");
}
//输入
void input(int * a)
{
for(int i = 0; i <N; i++)
{
scanf("%d",&a[i]);
}
}
//输出
void output(int * a)
{
for(int i = 0; i <N; i++)
{
printf("%d\n",a[i]);
}
}
//次大值
void sub(int * a)
{
int i,max,mid;
for(i = 0;i<5;i++)
{
if(a[i]>max)
{
mid = max;
max =a[i];
}
else if(a[i]>mid&&a[i]<max)
{
//另一种情况 arr[i]在两者之间*/
mid = a[i];
// 把arr[i]赋给mid
}
}
printf("次大值:%d\n",mid);
}
//排序
void swap(int * a)
{
int i,j,tmp = 0;
for(i=1;i<N;i++)
{
for(j=0;j<N-i;j++)
{
if(a[j]>a[j+1])
{
tmp=a[j];
a[j]=a[j+1];
a[j+1]=tmp;
}
}
}
}
//退出
void quit()
{
exit(0);
}