C语言入门-指针
程序员文章站
2022-06-21 08:58:45
终于到了精髓的地方了,这确实有点懵,总感觉这太麻烦了,而且写着也不爽,还是怀念py或者java,但也没办法,还是要继续学下去。 一、运算符& 1. scanf("%d" , &i); 里的& 2. 获取变量的地址,它的操作数必须是变量 3. 地址的大小是否与int相同取决于编译器 &不能取的地址 & ......
终于到了精髓的地方了,这确实有点懵,总感觉这太麻烦了,而且写着也不爽,还是怀念py或者java,但也没办法,还是要继续学下去。
一、运算符&
- scanf("%d" , &i); 里的&
- 获取变量的地址,它的操作数必须是变量
- 地址的大小是否与int相同取决于编译器
#include <stdio.h> int main(void) { int i = 0; printf("0x%x\n", &i); // 0x62fe4c return 0; }
&不能取的地址
&不能对没有地址的东西取地址
- &(a+b) ?
- &(a++) ?
二、指针
- 如果能够将获得变量的地址传递个一个函数,能否通过这个地址在那个函数内访问这个变量?
- scanf("%d" , &i);
- scanf()的原型应该是怎样的?我们需要一个参数保存别的变量的地址,如何表达能够保存地址的变量
指针
就是保存地址的变量 , *p
// p是一个指针,现在把i的地址交给了p int i; int* p = &i; // 下面两种形式一样,p是一个指针,而q是一个普通的int变量 int* p , q; int *p , q;
指针变量
- 变量的值是内存的地址
- 普通变量的值是实际的值
- 指针变量的值是具有实际值的变量的地址
作为参数的指针
- void f(int *p);
- 在被调用的时候得到了某个变量的地址
- int i = 0;f(&i);
- 在函数里面可以通过这个指针访问到外面的的这个i
#include <stdio.h> void f(int *p); int main(void) { int i = 6; printf("&i=%p\n", &i); f(&i); return 0; } void f(int *p) { printf(" p=%p\n", p); } // 可以看到这里获取的地址是相同的 // &i=000000000062fe4c // p=000000000062fe4c
访问那个地址上的变量*
- *是一个单目运算符,用来访问指针的值所表示的地址上的变量
- 可以是右值也可以是左值
- int k = *p;
- *p = k + 1;
*左值之所以叫左值
- 是因为出现在赋值号左边的不是变量,而是值,是表达式计算的结果
- a[0] = 2;
- *p = 3;
- 是特殊的值,所以叫左值
#include <stdio.h> // 声明两个函数 void f(int *p); void g(int k); int main(void) { int i = 6; printf("&i=%p\n", &i); f(&i); // 此时i的值已经发生了变化 g(i); return 0; } // 传入的是地址 void f(int *p) { printf(" p=%p\n", p); printf("*p=%d\n", *p); *p = 66; } // 传入的普通int void g(int k){ printf("k=%d\n", k); } // &i=000000000062fe4c // p=000000000062fe4c // *p=6 // k=66
三、指针的使用
指针应用场景一
交换两个变量的值
#include <stdio.h> void swap(int *pa , int *pb); int main() { int a = 5; int b = 10; swap(&a , &b); printf("a=%d , b=%d\n", a , b); return 0; } void swap(int *pa , int *pb){ int t = *pa; *pa = *pb; *pb = t; }
指针应用场景二
- 函数返回多个值,某些值就只能通过指针返回
- 传入的参数实际上是需要保存带回的结果的变量
#include <stdio.h> void minmax(int a[] , int len , int *min , int *max); int main(void) { int a[] = {1,2,3,4,5,6,7,8,9,12,13,14,15,34,35,66,}; int min , max; minmax(a , sizeof(a)/sizeof(a[0]) , &min , &max); printf("min = %d , max = %d \n", min , max); return 0; } void minmax(int a[] , int len , int *min , int *max) { int i; *min = *max = a[0]; for (i = 0; i < len; i++) { if (a[i] > *max) { *max = a[i]; } if (a[i] < *min) { *min = a[i]; } } }
指针应用场景二b
- 函数返回运算的状态,结果通过指针返回
- 常用的套路是让函数返回特殊的不属于有效范围内的值表示出错
- -1 或 0
- 但是当任何数值都是有效的可能结果时,就得分开返回了
#include <stdio.h> // 如果成功就返回1,否则就是0 int divide(int a , int b , int *result); int main(void) { int a = 5; int b = 2; int c; if (divide(a,b,&c)) { printf("%d/%d = %d\n", a, b, c); } return 0; } int divide(int a , int b , int *result) { int ret = 1; if ( b== 0) { ret = 0; }else{ *result = a/b; } return ret; }
指针常见的错误
定义了指针变量,还没有指向任何变量,就开始使用了
四、指针与数组
传入函数的数组成什么了?
- 函数参数表中的数组实际上是指针
- sizeof(a) == sizeof(int*)
- 但是可以用数组的运算符[]进行运算
下面的四种函数原型是等价的
int sum(int *ar , int n); int sum(int* , int); int sum(int ar[] , int n); int sum(int[] , int);
数组变量是特殊的指针
数组变量本身表达地址,所以
- int a[10]; int*p = a; // 无需用&取地址
- 但是数组的单元表达的是变量,需要用&取地址
- a == &a[0]
[]运算符可以对数组做,也可以对指针做
- p[0] <===> a[0]
*运算符可以对指针做,也可以对数组做
- *a = 25
数组变量是const的指针,所以不能被赋值
五、指针与const
- 表示一旦得到了某个变量的地址,不能再指向其他变量
// q内写的地址不能被改变 int *const q = &i; *q = 26; // ok q++; // error
- 表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const)
const int *p = &i; *p = 26; // error (*p是不能变的) i = 26; // ok p = &j; // ok
- 看懂下面的意思?
const int * p1 = &i; int const * p2 = &i; int *const p3 = &i;
判断那个被const的标志是const在的前面还是后面
前面两个p不能被修改,就像第二种情况
const数组
const int a[] = {1,2,3,4,5,6,};
- 数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int
- 所以必须通过初始化进行赋值
保护数组值
- 因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
- 为了保护数组不被函数破坏,可以设置参数为const
int sum(const int a[] , int length);
六、指针运算
1+1 = 2 ?那么指针加1等于什么呢
#include <stdio.h> int main(void) { char ac[] = {0,1,2,3,4,5,6,7,8,9,}; char *p = ac; printf("p =%p\n", p); printf("p + 1 =%p\n\n", p+1); // p =000000000062fe30 // p + 1 =000000000062fe31 // 相差了1 int ai[] = {0,1,2,3,4,5,6,7,8,9,}; int *q = ai; printf("q =%p\n", q); printf("q + 1 =%p\n\n", q+1); //q =000000000062fe00 //q + 1 =000000000062fe04 // 相差了4 return 0; }
我们不难发现,在char中,相差了1,在int中相差了4,这是怎么回事?
sizeof(char) = 1 , sizeof(int) = 4
对指针做一个加1的动作,意味着要把它移动到下一个单元去
int a[10]; int *p = a; *(p+1) --> a[1]
如果指针不是指向一片连续分配的空间,如数组,那么这运算没有意义
可以对指针的算术运算
- 给指针加、减、一个整数(+、+=、-、-=)
- 递增递减(++/--)
- 两个指针相减
#include <stdio.h> int main(void) { char ac[] = {0,1,2,3,4,20,6,7,8,9,}; char *p1 = &ac[5]; char *p2 = &ac[4]; printf("p1 - p2 = %d\n" , p1 - p2); printf("*p1 - *p2 = %d\n" , *p1 - *p2); // p1 - p2 = 1 也就是 5 -4 // *p1 - *p2 = 16 也就是 20 - 4 int ai[] = {0,1,2,3,4,5,6,7,8,18,}; int *q1 = &ai[9]; int *q2 = &ai[2]; printf("q2 - q1 = %d\n", q2 - q1); printf("*q2 - *q1 = %d\n", *q2 - *q1); // q2 - q1 = -7 也就是 2 - 9 // *q2 - *q1 = -16 也就是 2 - 18 return 0; }
*p++
- 取出p所指的那个数据来,完事之后顺便把p移动到下一个位置
- *的优先级虽然高,但没有++高
- 常用于数组类的连续空间操作
- 在某些cpu上,这可以直接被翻译成一条汇编指令
char ac[] = {0,1,2,3,4,20,6,7,8,9,-1}; char *p = &ac[0]; // 普通方法遍历数组 // int i ; // for (i = 0; i < sizeof(ac)/sizeof(ac[0]); i++) // { // printf("%d\n", ac[i]); // } // 普通指针遍历数组 // for (p = ac; *p != -1; p++) // { // printf("%d\n", *p); // } // printf("---------------\n"); // 使用*p++,快 while(*p != -1){ printf("%d\n", *p++); }
0地址
- 当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
- 所以你的指针不应该具有0值
- 因此可以用0地址来表示特殊的事情
- 返回的指针是无效的
- 指针没有被真正的初始化(先初始化为0)
- null是一个预定定义的符号,表示0地址
指针的类型
- 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
- 但是指向不同类型的指针是不能直接互相赋值的
- 只是为了避免用错指针
用指针来做什么
- 需要传入较大的数据时用作参数
- 传入数组后对数组做操作
- 函数返回不止一个结果
- 需要用函数修改不止一个变量
- 动态申请内存时
七、动态内存分配
输入数据
- 如果输入数据时,先告诉你个数,然后再输入,要记录每个数据
- c99可以用变量做数组定义的大小,c99之前呢?
#include <stdio.h> #include <stdlib.h> int main(void) { int number; int *a; int i; printf("请输入数量:\n"); scanf("%d" , &number); // 动态内存分配用malloc,此时a就相当于数组 a = (int*)malloc(number*sizeof(int)); for (i = 0; i < number; i++) { scanf("%d" , &a[i]); } for (i = number - 1; i >=0; i--) { printf("%d\n", a[i]); } // 最后释放空间 free(a); return 0; }
malloc函数
#include <stdlib.h> void* malloc(size_t size);
- 向malloc申请的空间的大小是以字节为单位的,需要什么类型就在sizeof里面写什么类型
- 返回的结果是void* , 需要类型转换为自己需要的类型
- 最后释放空间
(int*)malloc(n*sizeof(int));
没空间了?
- 如果申请失败则返回0 , 或者叫做null
- 你的系统可以给你多大的空间?
#include <stdio.h> #include <stdlib.h> int main(void) { void *p; int cnt = 0; while((p = malloc(100 * 1024 *1024))){ cnt ++ ; } printf("分配了%d00mb的空间\n", cnt); return 0; } // 分配了27200mb的空间
free()
- 把申请得来的空间还给系统
- 只能还申请的空间的首地址
- 一个malloc对应一个free