2.1指针的使用
2.2指针的计算
2.3字符串操作
2.4字符串函数的实现
2.1指针的使用
指针应用场景一
交换两个变量的值
1 void swap(int *pa, int *pb) 2 { 3 int t = *pa; 4 *pa = *pb; 5 *pb = t; 6 }
指针应用场景二a
函数返回多个值,某些值就只能通过指针返回
传入的参数实际上是需要保存带回的结果的变量
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 void minmax(int a[], int len, int *max, int *min); 7 8 main() 9 { 10 int a[] = { 1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55, }; 11 int min, max; 12 13 minmax(a, sizeof(a) / sizeof(a[0]), &max, &min); 14 15 printf("min=%d,max=%d\n", min, max); 16 17 system("pause"); 18 } 19 20 void minmax(int a[], int len, int *max, int *min) 21 { 22 int i; 23 *min = *max = a[0]; 24 25 for (i = 1;i < len;i++) 26 { 27 if (a[i] < *min) 28 { 29 *min = a[i]; 30 } 31 if (a[i] > *max) 32 { 33 *max = a[i]; 34 } 35 } 36 }
指针应用场景二b
函数返回运算的状态,结果通过指针返回
常用的套路是让函数返回特殊的不属于有效范围内的值来表示出粗:
-1或0(在文件操作会看到大量的例子)
但是当任何数值都是有效的可能结果时,就得分开返回了
状态用函数return返回,结果值通过指针参数返回。
好处:容易把状态放到if语句
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 int divide(int a, int b, int *result); 7 8 main() 9 { 10 int a = 5; 11 int b = 2; 12 int c; 13 14 if (divide(a, b, &c)) 15 { 16 printf("%d/%d=%d\n", a, b, c); 17 } 18 19 system("pause"); 20 } 21 22 int divide(int a, int b, int *result) 23 { 24 int ret = 1; 25 26 if (b == 0) 27 { 28 ret = 0; 29 } 30 else 31 { 32 *result = a / b; 33 } 34 return ret; 35 }
指针最常见的错误
定义了指针变量,还没有指向任何变量,就开始是使用指针
p指向了一个未知地址,赋值可能会使得程序崩溃
严重性 代码 说明 项目 文件 行 禁止显示状态
错误 C4700 使用了未初始化的局部变量“p”
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 main() 7 { 8 int i = 6; 9 int *p; 10 int k; 11 k = 12; 12 *p = 12; 13 14 system("pause"); 15 }
数组参数
以下四种函数原型是等价的:
1 int sum(int *ar, int n); 2 int sum(int *,int); 3 int sum(int ar[], int n); 4 int sum(int[], int);
数组变量是特殊的指针
数组变量本身表达地址,所以
int a[10]; int *p=a;//无需用&取地址
但是数组的单元表达的是变量,需要用&取地址
a == &a[0];
[]运算符可以对数组做,也可以对指针做:
p[0]<==>a[0]
1 int min = 2; 2 3 int *p = &min; 4 5 printf("%d,%d\n", *p, p[0]);
*运算符可以对数组做,也可以对指针做:
*a = 25;
数组变量是const的指针,所以不能被赋值
指针是const
表示一旦得到了某个变量的地址,不能再指向其他变量
严重性 代码 说明 项目 文件 行 禁止显示状态
错误 C2166 左值指定 const 对象
1 int i = 1; 2 int * const q = &i;//q是const 3 4 *q = 26;//OK 5 6 q++;//ERROR
所指是const
表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const)
1 int i = 1; 2 int j = 1; 3 4 const int *p = &i;//ERROR (*p)是const 5 6 i = 26;//OK 7 8 p = &j;//OK
判断哪个被const了的标志是const在*的左边还是右边
1 int i = 1; 2 3 const int *p1 = &i; 4 int const *p2 = &i; 5 int * const p3 = &i;
转换
总是可以把一个非const的值转换成const的
比如:结构体
1 int a = 15; 2 f(&a); 3 const int b = a; 4 5 f(&b);//OK 6 7 b = a + 1;//ERROR
当要传递的参数的类型比地址大的时候,这是常用的手段:既能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改
const数组
const int a[]={1,2,3,4,5,6,};
数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int
所以必须通过初始化进行赋值
保护数组值
因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
为了保护数组不被函数破坏,可以设置参数为const
int sum(const int a[], int length);
2.2指针的计算
指针运算
这些算术运算可以对指针做:
给指针加、减一个整数(+, +=, -, -=)
递增递减(++/--)
两个指针相减
1 int a[] = { 1,2,3,4,5,6,7,8 }; 2 3 int *p1 = &a[0]; 4 int *p2 = &a[4]; 5 6 printf("%d\n", p2 - p1);//4
*p++
取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
*p的优先级虽然高,但是没有++高
常用于数组类的连续空间操作
在某些CPU上,这可以直接被翻译成一条汇编指令
指针比较
<, <=, ==, >, >=, !=都可以对指针做
比较它们在内存中的地址
数组中的单元的地址肯定是线性递增的
0地址
当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
所以你的指针不应该具有0值
因此可以用0地址来表示特殊的事情:
返回的指针是无效的
指针没有被真正初始化(先初始化为0)
NULL是一个预定定义的符号,表示0地址
有的编译器不愿意你用0来表示0地址
指针的类型转换
void *表示不知道指向什么东西的指针
计算时与char *相同(但不相通)
指针也可以转换类型
int *p=&i;
void *q=(void *)p;
这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量
用指针来做什么
需要传入较大的数据时用作参数
传入数组后对数组做操作
函数返回不至一个结果
需要用函数来修改不止一个变量
动态申请的内存
输入数据
如果输入数据时,先告诉你个数,然后再输入,要记录每个数据
C99可以用变量做数组定义的大小,C99之前呢?
int *a=(int *)malloc(n*sizeof(int));
malloc
#include <stdlib.h>
void * malloc(size_t size);
向malloc申请的空间的大小是以字节为单位的
返回的结果是void *,需要类型转换为自己需要的类型
(int *)malloc(n*sizeof(int))
1 #define _CRT_SECURE_NO_WARNINGS 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 6 main() 7 { 8 int num; 9 int *a; 10 int i; 11 scanf("%d", &num); 12 13 a = (int *)malloc(num*sizeof(int)); 14 15 for (i = 0;i < num;i++) 16 { 17 scanf("%d", &a[i]); 18 } 19 20 for (i = num - 1;i >= 0;i--) 21 { 22 printf("%d ", a[i]); 23 } 24 25 free(a); 26 27 system("pause"); 28 }
没空间了?
如果申请失败则返回0,或者叫做NULL
free()
把申请得来的空间还给“系统”
申请过的空间,最终都应该要还
只能还申请来的空间的首地址
常见问题
申请了没free->长时间运行内存逐渐下降
新手:忘了
老手:找不到合适的free的时间
free过了再free
地址变过了,直接去free
办法:
写了一个malloc,马上写上free
1 int a[] = { 0 }; 2 int *p = a; 3 4 if (p == a[0])//false 5 { 6 printf("true\n"); 7 } 8 else 9 { 10 printf("false\n"); 11 } 12 13 if (p == &a[0])//true 14 { 15 printf("true\n"); 16 } 17 else 18 { 19 printf("false\n"); 20 } 21 22 if (*p == a[0])//true 23 { 24 printf("true\n"); 25 } 26 else 27 { 28 printf("false\n"); 29 } 30 31 if (p[0] == a[0])//true 32 { 33 printf("true\n"); 34 } 35 else 36 { 37 printf("false\n"); 38 }
2.3字符串操作
putchar
int putchar(int c);
向标准输出写一个字符
返回写了几个字符,EOF(-1)表示写失败
getchar
int getchar(int c);
向标准输入读入一个字符
返回类型是int是为了返回EOF(-1)
Windows->Ctrl-Z
Unix->Ctrl-D
字符串数组和字符型指针数组不一样
char ch[3][5] <> char *pa[3]
程序参数
int main(int argc, char const *argv[])
argv[0]是命令本身
当使用Unix的符号链接时,反映符号链接的名字
2.4字符串函数的实现
strlen
size_t strlen(const char *s);
返回s的字符串长度(不包括结尾的0)
strcmp
int strcmp(const char *s1, const char *s2);
比较两个字符串,返回:
0: s1==s2
1: s1>s2
-1: s1<s2
strcpy
char *strcpy(char *restrict dst, const char *restrict src);
把str的字符串拷贝到dst
restrict表明stc和dst不重叠(C99)
返回dst
为了能链起代码来
字符串中找字符
char *strchr(const char *s, int c);
char *strrchr(const char *s, int c);
返回NULL表示没有找到
字符串中找字符串
char *strstr(const char *s1, const char *s2);
char *strcasestr(const char *s1, const char *s2);