指针定义
地址:内存中每一个单元格的编号(门牌号)
指针:地址,内存地址
指针变量:存储地址的变量
一级指针
定义格式
存储类型 数据类型 *指针变量名;
int *p;
int *:表示变量 p 存放的是一个 int 类型的地址
例: int *p; //定义指针变量p
int a=10;
int *p=&a; //p中存储的就是a的地址
访问值:*指针变量名 *p
修改值:*指针变量名=新值
指针操作符
&:取地址
*:取内容,取地址内容 // 后面往往跟着的是地址
int a = 10;
*&a == a; // &和*互为逆运算
&*a; // 错误,单目运算符,执行顺序是从右向左执行,*a 错误,a 是变量不是地址
初始化
- 将普通变量赋给指针
int a=20;
int *p=&a; //定义时同时赋值
int *p; //野指针 乱指向
int *p=NULL; //指针指向空
int b=10;
p=&b; //先定义后赋值
- 将数组的地址赋值给指针
char str[]="hello";
char *p=str;
- 将指针变量的值交给另一个指针变量
int a=20;
int *p=&a;
int *q=p;
指针运算
算数运算
++ -- + -
int a[5] = {1, 2, 3, 4, 5};
int *p = a; // a++; ; 错误,a 是常量,不能为左值
p++; ++p; 等价于 p = p + 1; 指针向高地址方向移动一个数据单位
p+n:指针向高地址方向访问 n 个数据单位
p-n:指针向低地址方向访问 n 个数据单位
数据单位:指针的类型大小
q-p:两个地址之间相隔数据元素的个数
q 和 p 两者的数据类型相同,q 为高地址,p 为低地址
最好是在同一个数组下进行
关系运算
> >= < <= == !=
用来比较地址的高低
高地址大于低地址
数据类型要相同,在同一存储区域
在同一数组中比较才有意义
赋值运算
= += -=
指针大小
sizeof(指针变量名); // 指针大小
32 位操作系统当中:4 字节
64 位操作系统当中:8 字节
指针修饰
const:常量化 read only //可以修饰变量、可以修饰指针
int const a = 10; // 变量a只读
int const *p; // *p 指针指向的内容不能修改,指针指向可以修改
int *const p; // p 指针的指向不能修改,指针指向的内容可以修改
const int *const p; // 指针的指向和指针的内容都不能修改
void:不能修饰变量,可以修饰指针
void *p; // 任意类型的指针
一般用在函数的参数以及返回值中,在使用时需要进行强制类型转换
强制类型转换:格式:
(强转的数据类型)要强转的变量名
注意:强转仅在当前这一条语句生效,对其他语句无效
段错误
- 野指针
指针变量没有赋值
指针置空之后 对空间赋值
堆空间free之后 指针没有及时置空
int *p;
int a;
p = &a;
scanf("%d", p);
printf("%d\n", *p);
- 内存泄漏
内存空间溢出或者程序未释放
对非法空间操作
char s[] = "hello";
char *p = s;
scanf("%s", p);
printf("%s\n", p);
大小端
大端:在低地址存放高字节数据,在高地址存放低字节数据
小端:在低地址存放低字节数据,在高地址存放高字节数据
int 4字节
int a=0x12345678; 起始地址:0x100
0x100
0x101
0x102
0x103
大端
0x12
0x34
0x56
0x78
小端
0x78
0x56
0x34
0x12
二级指针
一级指针:存放变量的地址
二级指针:存放一级指针的地址
定义格式
存储类型 数据类型 **指针变量名
int a = 10;
int *p = &a;
int **q = &p;
访问 a 的值:
a **q *p
访问 a 的地址:
&a p *q
访问 p 的地址:
&p q
指针和数组
直接访问:按变量的地址存取变量的值(通过数组名访问)
间接访问:通过存放变量地址的变量去访问变量(通过指针访问)
指针和一维数组
int a[]={1,2,3};
int *p=a;
直接访问:
间接访问:
注意:a 和 p 本质上不同,a 是地址常量,p 是变量,a 不能执行++操作,但是 p 可以
访问数组元素 a[i]的值:
直接访问:a[i] *(a+i)
间接访问:p[i] *(p+i)
访问数组元素 a[i]的地址:
直接访问:&a[i] a+i
间接访问:&p[i] p+i
指针和二维数组
int a[2][2]={1,2,3,4};
int *p=a;
a:数组名,表示第一行的首地址
a+1:第二行的首地址在
a 前面*,表示将行地址降级为列地址
*a:第一行第一列的地址
*a+1:第一行第二列的地址
*(a+1):第二行第一列的地址
*(a+1)+1:第二行第二列的地址
直接访问:
访问数组元素的地址:
&a[i][j] *(a+i)+j a[i]+j
访问数组元素的值:
a[i][j] *(*(a+i)+j) *(a[i]+j)
数组指针
定义:本质上是指针,指向的是数组(又称行指针)
格式:存储类型 数据类型 (*指针变量名)[列数];
int a[2][3] = {1, 2, 3, 4, 5, 6};
int (*p)[3] = a;
p 可以代替 a 进行元素访问,但是本质不同
访问 a[i][j] 地址: *(p+i)+j p[i]+j
访问 a[i][j] 内容: *(*(p+i)+j) *(p[i]+j)
数组指针的大小: sizeof(p) = 8;
指针数组
定义:本质上是数组,里面存放的是指针
格式: 存储类型 数据类型 *数组名[元素个数];
- 用于存放普通变量的地址
int a = 10, b = 20, c = 30;
int *p[3] = {&a, &b, &c};
访问 b 的地址: *(p+1) p[1]
访问 b 的值: *(*(p+1)) *p[1]
- 用于存放二维数组每一行第一列的地址
int a[2][3] = {1, 2, 3, 4, 5, 6};
int *p[2] = {a[0], a[1]};
访问 a[1][2] 的地址: p[1]+2 *(p+1)+2
访问 a[0][1] 的元素: *(p[0]+1) *(*(p+0)+1)
- 用于存放字符串
char *p[3] = {"hello","world","hq"};
printf("%s\n",p[0]); //hello
printf("%s\n",p[1]); //world
printf("%s\n",*(p+2)); //hq
printf("%c\n",*(*(p+1)+2)); //r
printf("%c\n",p[2][1]); //q
- 命令行参数
int main(int argc, char const *argv[])
{
return 0;
}
argv:就是一个指针数组,里面存放的是命令行传递的字符串
argc:表示 argv 指针数组里面存放的数据的个数,即命令行传递字符串的个数
指针编程练习
示例一:编写一个程序实现功能:将字符串“Computer Science”赋值给一个字符数组
从第一个字母开始间隔的输出该字符串,用指针完成。结果:Cmue cec
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char buf[] = "Computer Science";
char *p = buf;
// for (int i = 0; i < strlen(buf); i += 2)
// {
// printf("%c", p[i]);
// }
while(p < buf+strlen(buf))
{
printf("%c", *p);
p += 2;
}
printf("\n");
return 0;
}
示例二:用指针实现字符串倒置
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char buf[]="hello";
char *p = buf;
char *q = p + strlen(buf) - 1;
for (int i = 0; i < strlen(buf) / 2; i++)
{
char temp = *(p+i);
*(p+i) = *(q-i);
*(q-i) = temp;
}
printf("%s\n", buf);
}
示例三:循环输入一个五位数,判断它是不是回文数(指针完成)
#include <stdio.h>
int main(int argc, char const *argv[])
{
int i;
int *p=&i;
while (1)
{
printf("input:\n");
scanf("%d", &i);
if (*p == 0)
{
break;
}
else if ((*p % 10)== (*p / 10000) && (*p % 100 / 10) == (*p / 1000 % 10))
{
printf("%d是回文数\n", i);
}
else
{
printf("%d不是回文数\n", i);
}
}
return 0;
}
示例四:将字符串转换成整型数字输出。用指针实现
要求:字符串为0-9组成,输出数据为一个整型数
例:char s[10]="123";printf(“%d\n",num);//num=123;
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char str[32] = "123456";
int num=0;
// scanf("%s", str);
char *p = str;
for (int i = 0; i < strlen(str); i++)
{
num = num * 10 + *(p + i) - 48;
}
printf("%d\n", num);
return 0;
}
示例五:输入一个字符串,获取字符串中a字符首次出现的下标(用指针实现)
#include <stdio.h>
int main(int argc, char const *argv[])
{
char str[32];
char *s = str;
int n=0;
printf("请输入一个字符串:\n");
scanf("%s", str);
while(*s!='a')
{
s++;
n++;
}
printf("%c %d",*s,n);
putchar('\n');
// char *p[3] = {"hello", "world", "ak"};
// for (int i = 0; i < 3; i++)
// {
// for (int j = 0; p[i][j] != '\0'; j++)
// {
// if (p[i][j] == 'a')
// {
// printf("%c [%d][%d]\n", *(*(p + i) + j), i, j);
// break;
// }
// }
// }
return 0;
}
示例六:已知字符数组a[10]和b[10]中元素的值递增有序,用指针实现将两个数组中元素按照递增顺序输出。如:char a[10]="acdgimno”; char b[10]="befhil”;--->"abcdefghilmno”
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char a[10] = "acdgjmno";
char b[10] = "befhil";
char *p = a, *q = b;
while (*p != '\0' && *q != '\0')
{
if (*p < *q)
{
printf("%c", *p);
*p++;
}
else
{
printf("%c", *q);
*q++;
}
}
if (*p == '\0')
printf("%s", q);
else
printf("%s", p);
printf("\n");
return 0;
}
示例七:用数组指针遍历二维数组
#include <stdio.h>
int main(int argc, char const *argv[])
{
int i, j;
int arr[2][3] = {1, 2, 3,4, 5, 6};
int (*p)[3]=arr;
for (i = 0; i < 2; i++)
{
for (j = 0; j < 3; j++)
{
printf("%d ",*(*(p+i)+j));
}
printf("\n");
}
return 0;
}
示例八:用指针数组遍历二维数组
#include <stdio.h>
int main(int argc, char const *argv[])
{
int i, j;
int arr[2][3] = {1, 2, 3, 4, 5, 6};
int *p[2] = {arr[0],arr[1]};
for (i = 0; i < 2; i++)
{
for (j = 0; j < 3; j++)
{
printf("%d ", p[i][j]);
}
printf("\n");
}
return 0;
}