指针的使用
指针的使用:指针有什么用?
指针应用场景一
交换两个变量的值
void fnSwapValue(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
指针应用场景二a
- 函数返回多个值,某些值就只能通过指针返回
- 传入的参数实际上是需要保存带回的结果的变量
void fnSearchMinMax(int target[], int length, int *min, int *max)
{
*min = *max = target[0];
for (int i = 1; i < length; i++)
{
if (target[i] < *min)
{
*min = target[i];
}
if (target[i] > *max)
{
*max = target[i];
}
}
}
指针应用场景二b
- 函数返回运算的状态,结果通过指针返回
- 常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错:
-1
和0
(在文件操作中会看到大量的例子)- 但是当任何数值都是有效的可能结果时,就得分开返回了
- 后续的语言(
C++, Java
)采用了异常机制来解决这个问题
int fnDevide(int a, int b, int *result)
{
int status = 1;
if (b == 0)
{
status = 0;
}
else
{
*result = a / b;
}
return status;
}
指针最常见的错误
- 定义了一个指针,但是并没有让他指向任何变量,就开始使用指针
指针与数组:为什么数组传入函数后的sizeof
不对了?
- 函数参数表中的数组实际上是指针
sizeof(a) == sizeof(int *);
- 但是可以用数组的运算符
[]
进行运算
数组参数
- 以下四种函数原型是等价的:
int sum(int *arr, int n);
int sum(int *, int);
int sum(int arr[], int n);
int sum(int [], int);
数组变量是特殊的指针
- 数组变量本身表达地址,所以
int a[10]; int *p = a; // 无需用&取地址
- 但是数组的单元表达的是变量,需要用
&
取地址a == &a[0]
[]
运算符可以对数组做,也可以对指针做
p[0] -> a[0]
*
运算符可以对指针做,也可以对数组做:
*a = 25;
- 数组变量
const
指针,所以不能被赋值
int b[] = int * const b;
- 常量指针
指针与const
指针是const
- 表示一旦得到了某个变量的地址,不能再指向其他变量
int * const q = &i; // q是const
*q = 26; // OK
q++; // ERROR
q
的值不能被改变
所指是const
- 表示不能通过这个指针去修改那个变量(并不能使得那个变量成为
const
)
const int *p = &i;
*p = 26; // ERROR !(*p)是const
i = 26; //OK
p = &i; // OK
这是什么意思?
int i;
const int* p1 = &i; // 定义一个所指是const的地址变量
int const* p2 = &i; // 同上
int *const p3 = &i; // 定义一个指针本身是const的地址变量
注意:判断那个被const
的标志是const
在*
的前面还是后面
转换
我们总是可以把一个非
const
的值转换成const
的
void f(const int * x);
int a = 14;
f(&a); // OK
const int b = a;
f(&b); // Ok
b = a + a; // Error!
- 当要传递的参数的类型比地址大的时候,这是常用的手段:既能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改
const
数组
const int a[] = {1, 2, 3, 4, 5, 6};
- 数组变量已经是
const
的指针了,这里的const
表明数组的每个单元都是const int
- 所以必须通过初始化进行赋值
指针运算
- 这些算术运算可以对指针做:
- 给指针加、减一个整数(
+, +=, -, -=
)- 递增递减(
++/--
)- 两个指针相减
1+1=2?
- 给一个指针加
1
表示要让指针指向下一个变量
int a[10];
int *p = a;
*(p+1) -> a[1]
- 如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义
int main()
{
char target[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
char *p = target;
printf("p = %p\n", p);
printf("p+1 = %p\n", p+1);
return 0;
}
// p = 000000000061FE0F
// p+1 = 000000000061FE10
// sizeof(char) == 1
int main()
{
int target[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = target;
printf("p = %p\n", p);
printf("p+1 = %p\n", p+1);
return 0;
}
// p = 000000000061FDF0
// p+1 = 000000000061FDF4
// sizeof(int) == 4
两个指针相减
- 地址的差除以
sizeof(type)
- 中间有几个这样的
type
int main()
{
char target[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
char *p = target;
char *p1 = &target[5];
printf("p1 - p = %p\n", p1 - p);
return 0;
}
// p1 - p = 0000000000000005
int main()
{
int target[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int *p = target;
int *p1 = &target[5];
printf("p1 - p = %p\n", p1 - p);
return 0;
}
// p1 - p = 0000000000000005
*p++
- 取出
p
所指的那个数据来,完事之后顺便把p
移到下一个位置去
- 取出
p
那个位置值,顺便把p
加1
*
的优先级虽然高,但是没有++
高- 常用于数组类的连续空间操作
- 在某些
CPU
上,这可以直接被翻译成一条汇编指令(非常省事)
指针比较
<, <=, ==, >=, !=
都可以对指针做- 比较它们在内存中地址
- 数组中的单元的地址肯定是线性递增的
0
地址
- 当然你的内存中有
0
地址,但是0
地址通常是个不能随便碰的地址- 所以你的指针不应该具有
0
值- 因此可以用
0
地址表示特殊的事情
- 返回的指针是无效的
- 指针没有被真正初始化(先初始化为
0
)NULL
是一个预定定义的符号,表示0
地址- 有的编译器不愿意你用
0
来表示0
地址
指针的类型
- 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
- 但是指向不同类型的指针是不能直接互相赋值的
- 这是为了避免用错指针
指针的类型转换
void *
表示不知道指向什么东西的指针
- 计算时与
char *
相同(但不相通)- 指针也可以转换类型
int *p = &i; void *q = (void *)p;
- 这并没有改变
p
所指的变量的类型,而是让后人用不同的眼光通过p
看它所指的变量
- 我不再当你是
int
啦,我认为你就是个void
用指针来做什么
- 需要传入较大的数据时用作参数
- 传入数组后对数组做操作
- 函数返回不止一个结果
- 需要用函数来修改不止一个变量
- 动态申请的内存
动态内存分配
输入数据
- 如果输入数据时,想告诉你个数,然后再输入,要记录每个数据
C99
可以用变量做数组定义的大小,C99
之前呢?int *a = (int *) malloc (n *sizeof(int));
int main()
{
int number = 10;
int *p;
p = (int *)malloc(number * sizeof(int));
for (int i = 0; i < number; i++)
{
scanf("%d", &p[i]);
}
for (int i = number - 1; i >= 0; i--)
{
printf("%d", p[i]);
}
free(p);
return 0;
}
malloc
函数
#include <stdlib.h>
void * malloc(size_t size);
- 向
malloc
申请的空间的大小时以字节为单位的- 返回的结果是
void*
,需要类型转换为自己需要的类型
(int *)malloc(n * sizeof(int));
没空间了?
- 如果申请失败则返回
0
,或者叫做NULL
- 你的系统能给你多大的空间?
int main()
{
void *p;
int count = 0;
while (p = malloc(100 * 1024 * 1024))
{
count++;
// free(p);
}
printf("分配了%d00MB的空间", count);
return 0;
}
// 分配了19900MB的空间 (没有还)
// 如果分配之后,再free(),则程序会一直运行下去
free()
函数
- 把申请的来的空间还给“系统”
- 申请过的空间,最终都应该还
- 混出来的,迟早要还的
- 只能还申请过来的空间的首地址
free(++p);
会出错free(NULL);
不会出错
常见问题
- 申请了没
free()
->长时间运行内存逐渐下降
- 新手:玩了
- 老手:找不到合适的
free
的时机free
过了再free
- 地址变过了,直接去
free
字符串操作
单字符输入输出
putchar
int putchar(int c);
- 向标准输出写一个字符
- 返回写了几个字符,
EOF(-1)
表示写失败
getchar
int getchar(void);
- 从标准输入读入一个字符
- 返回类型是
int
是为了返回EOF(-1)
Windows->Ctrl+Z
Unix->Ctrl+D
int main()
{
int ch;
while ((ch == getchar()) != EOF)
{
putchar(ch);
}
printf("EOF");
return 0;
}
字符串数组
字符串数组
char **p;
p
是一个指针,指向另一个指针,那个指针指向一个字符(串)char a[][];
程序参数
int main(int argc, char const *argv[])
argv[0]
是命令本身
- 当使用
Unix
的符号链接时,反映符号链接的名字
int main(int argc, char const * argv[])
{
for (int i = 1; i < argc; i++)
{
printf("%d:%s\n", i, argv[i]);
printf("%d\n", argc);
}
return 0;
}
字符串函数的实现
string.h
int fnStringLength(const char * str)
{
int count = 0;
int flag = 0;
while (str[flag] != '\0')
{
flag++;
count++;
}
return count;
}
strcmp
int strcmp(const char *s1, const char *s2);
- 比较两个字符串,返回:
0 : s1 == s2
1 : s1 > s2
-1 : s1 < s2
- 注意:数组的比较永远是
false
int main()
{
char str1[] = "abc";
char str2[] = "bbc";
printf("%d\n", str1 == str2);
printf("%d\n", strcmp(str1, str2));
return 0;
}
// 0
// -1
自定义strcmp
int fnMyStrCmp(const char *s1, const char *s2)
{
// int flag = 0;
// while (s1[flag] == s2[flag] != '\0')
// {
// if (s1[flag] != s2[flag])
// {
// break;
// }
// else if (s1[flag] == s2[flag])
// {
// break;
// }
// flag++;
// }
while (*s1 == *s2 && *s1 != '\0')
{
s1++;
s2++;
}
return *s1 - *s2;
}
strcpy
char * strcpy(char *restrict dst, const char *restrict src);
- 把
src
的字符串拷贝到dst
restrict
表明src
和dst
不重叠(c99
)- 返回
dst
- 为了能链起代码来
复制一个字符串
char *dst = (char *)malloc(strlen(src) + 1); // '\0'也占用一个空间
strcpy(dst, src)
char *fnMyCpy(char *dst, const char *src)
{
int flag = 0;
while (src[flag] != '\0')
{
dst[flag] = src[flag];
flag++;
}
// while (*dst++ = *src++);
dst[flag] = '\0';
return dst;
}
字符串搜索函数
char * strchr(const char *s, int c);
char * strrchr(const char *s, int c);
- 返回
NULL
表示没有找到
- 如何找第二个?
字符串中找字符串
char * strstr(const *s1, const char *s2);
char * strcasestr(const char *s1, const char *s2);