指针
优点
1、使程序更简洁、紧凑、高效
2、有效的表达更复杂的数据结构
3、动态分配内存
4、得到多于一个数的函数返回值
概念
地址:
内存中每个字节单位都有一个编号
指针:
指针就是地址
指针变量:
用于存放地址的变量就叫做指针变量
定义格式
<存储类型> <数据类型> *<指针变量名>
存储类型指的是auto,register, static,extern。其中 auto 可以省略。
指针变量的变量名是“ * ”后面的内容,“ * ”只是说明定义的是一个指针变量。
int a = 5;
int *p = &a;
char b = 'v';
char *q = &b;
printf("%p\n", p); // 整型变量 a 的地址
printf("%p\n", q); // 字符型变量 b 的地址
指针操作符
&: 取地址符,取变量的地址
*: 取内容,取地址里面存放的内容
*&a:取变量 a 的值 *&a <=> *p <=> a
( &*a:错误用法 )
区分:
p:指针变量,内容是地址。
*p:指针所指向的对象,内容是数据。
&p:指针变量所占用的 存储区域的 地址。
初始化
指针变量在使用在不仅要定义还要初始化。指针变量的值只能是变量的地址,不能是其他数据,否则将引起错误。未初始化的指针变量不能随便使用,会产生“野指针”(随机指向某个内存单元)。
将普通变量的地址赋值给指针变量
int a =10;
1)int *p = &a; // 定义的同时赋值
2)int *p = NULL; // 先定义后赋值
p = &a;
int a = 10;
int *p = NULL;
p = &a;
printf("%d %d\n", a, *p); // 10 10
printf("%p %p\n", &a, p); // 打印的都是变量a地址
*p = 3;
printf("%d %d\n", a, *p); // 3 3
printf("%p %p\n", &a, p); // 打印的地址没有发生改变
将数组的首地址赋值给指针变量
char s[10] = "hello";
char *p = s; // 指针指向数组的首地址
printf("%c\n", *p); // h
printf("%s\n", *p); // !!!!段错误!!!!
将指针变量里面保存的地址赋值给另一个指针变量
float a = 1.3;
float *p = &a;
float *q = p;
printf("%p %p\n", p, q); // 打印的都是变量a地址
指针运算
算术运算
// p++; p--;
char s[32] = "hello";
char *p = s;
printf("%c\n", ++p); // p 现在指向了 e
int *p; p++; // 移动 4 字节,指针指向发生变化
double *p; p++; // 移动 8 字节,指针指向发生变化
// p + n 或 p - n,指针指向不发生变化。
// p+n; p-n;
// p + n 表示的实际内存单元的地址量是:p + sizeof(p的类型) * n
// p - n 表示的实际内存单元的地址量是:p - sizeof(p的类型) * n
int arr[32] = {1,2,3,5};
int *p = arr;
int num;
p = p+3; // 指向5
int *q = arr; // 指向1
printf("%d\n", p-q); // 3
// 两个地址之间的差 = 两个地址之间相差元素的个数
// 即,p-q 事实上等于 (p-q) / sizeof(p或q的类型)
// p-q;
int m = 100;
int *p1 = NULL, *p2 = NULL;
p1 = &m;
p2 = p1 + 2;
printf("p1=%p p2=%p\n", p1, p2); // 0x地址,0x地址+2*4
printf("p2-p1=%d\n", p2-p1); // 2
double n = 200;
double *p1 = NULL, *p2 = NULL;
p1 = &n;
p2 = p1 + 2;
printf("p1=%p p2=%p\n", p1, p2); // 0x地址,0x地址+2*8
printf("p2-p1=%d\n", p2-p1); // 2
运行结果如下:
关系运算
> < >= <= == !=
指针之间关系运算比较的是它们指向地址的高低
指向高地址的指针大小 大于 指向低地址的指针
char s[10] = "hello";
char *p1 = &s[1];
char *p2 = &s[3];
if(p1 < p2)
printf("p2大"); // 执行
else
printf("p1大"); // 不执行
注意:
指向不同类型的数组指针关系运算没有意义,
指向不同区域的指针关系运算也没有意义 (应同一个数组间进行比较)
指针与一般整数变量之间的关系运算没有意义,但可以和 0 比较是否相等,判断指针是否为空
赋值运算
见上面,初始化。
指针的大小
int a = 5;
int *p1 = &a;
short b = 2;
short *p2 = &b;
double c = 3.333;
double *p3 = &c;
double *p4 = NULL;
printf("%d\n", sizeof(p1)); // 4
printf("%d\n", sizeof(p2)); // 4
printf("%d\n", sizeof(p3)); // 4
printf("%d\n", sizeof(p4)); // 4
32位操作系统:指针为四个字节
// 32位:表示单次运算可以处理最大数据的位数
64位操作系统:指针为八个字节
// 类似地,...
内存图
内存地址是固定的,但是变量的地址不固定(栈区变量随机分配)。
段错误 Segmentation fault (core dumped)
1)“野指针”,没有规定指向的指针,会随机指向一个内存单元。
“野指针”产生原因,主要有两种:
① 指针变量没有初始化;
② 指针 p 被 free 之后,没有置NULL,程序会误以为 p 是合法指针
解决方法:int *p = NULL;
2)内存泄漏,对非法空间进行赋值或访问。
练习
1)字符串 ——> 整型数字
要求:字符串为 0-9 组成,输出数据为一个整型数
char s[10] = “123456”;
…
printf(“%d\n”, num); // num = 123456
#include <stdio.h>
#define N 10
int main(int argc, char const *argv[])
{
char s[N];
int num = 0;
char *p;
scanf("%s", s); // 不能循环输入单个字符,否则效果不能实现
p = s;
while (*p){
num = num * 10 + (*p - 48);
p++;
// printf("%d %d\n", *p, num);
}
printf("%d\n", num);
return 0;
}
运行结果如下:
2)字符串倒置
字符串倒置(用指针实现) 如: hello ->olleh
思路:定义两个指针分别指向字符串的开头和结尾,前面的指针向后走,后面的指针向前走
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char str[20];
gets(str);
char *p = str, *q = str + strlen(str)-1;
char temp;
for (int i = 0; i < strlen(str)/2; i++){ // while (p < q)
temp = *p;
*p = *q;
*q = temp;
p++;
q--;
}
puts(str);
return 0;
}
运行结果如下:
3)间隔输出字符串
将字符串 "Computer Science"赋值给一个字符数组,
然后从第一个字母开始间隔的输出该字符串,用指针完成,
结果:Cmue cec
#include <stdio.h>
int main(int argc, char const *argv[])
{
char str[] = "Computer Science";
char *p = str;
for (int i = 0; *p != '\0'; i++){
printf("%c", *p); // 省略数组长度时,最好使用 *p != '\0' 而不是 str[i] != '\0'
p += 2; // 也可以直接 while(*p)
}
putchar('\n');
return 0;
}
4)合并字符串
将两个字符串合并为一个字符串并且输出,用指针实现
char str1[32] = "hello ", str2[32] = “world”
结果:hello world
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
char str1[32] = "hello ", str2[32] = "world";
char *p = str1 + strlen(str1), *q = str2;
for (; *q != '\0';){
*p = *q;
p++;
q++;
}
puts(str1);
return 0;
}