C语言之指针
- 指针大小与32位、64位系统: https://www.cnblogs.com/HOMEofLowell/p/12944900.html
64位计算机架构一般具有 64 位宽的整数型寄存器,而只有整数寄存器(integer register)才可存放指针值(内存数据的地址),因此指针的大小就自然是 8 个字节了。在 32 位系统中,地址的大小是 32 bit.
32位系统或者64位系统,实际上说的是 CPU 一次处理数据的能力。 - 64位系统,这个位数指的是CPU 里面的通用寄存器的数据宽度为64位,也就是说一个地址占二进制位数是64,所以sizeof(double *)==sizeof(int *)==sizeof(char *)==64/8==8
32位系统,同理,他的一个地址占32位二进制空间,sizeof(double *)==sizeof(int *)==sizeof(char *)==32/8==4
其实明白了两个系统的寻址原理就能明白,大体就是这个原因。
地址跟系统有关,但是基本数据类型占的大小是由C语言本身决定。
指针 : 保存地址的变量
- p代表自身值.
*p是用来操作指针的, 取指针指向的值. &p 操作任何变量, 用来取变量地址. int *p; p就是一个变量,存放一个值。这个值有点特殊,它是个存储空间的地址。这是,p成为指针变量。*p如果出现在表达式中,表示一个值,这个值为p中存放的地址处的内容。 int p;的情况下,p也是一个变量,存放一个整型值。&p也是一个特殊值,这个值为p这个变量在存储空间中的地址。
- 传指针 f(int * p)定义 ----> f (&i)传;
f(int * p) 的 p是地址 *p取对应地址的变量
运算符 &
地址跟整数是不一定是相等的 下面是证明 也就是影响sizeof
- 64位架构
#include<stdio.h> int main(){ int i = 0 ; printf("%d\n",sizeof(int));4 printf("%d\n",sizeof(&i));8 return 0; }
- 32位架构
#include<stdio.h> int main(){ int i = 0 ; printf("%d\n",sizeof(int));4 printf("%d\n",sizeof(&i));4 return 0; }
- 代码
#include<stdio.h> int main(){ int i = 0 ; int ans ; ans = (int)&i; printf("0x%x\n",ans); printf("%p\n",&i); return 0; } 64位架构 //0x813f35d0 //0x7ffc813f35d0 32位架构 //0x813f35d0 //0x813f35d0
- 当指针的前一个或下一个有意义的值时 才可以进行赋值
#include <stdio.h> int main() { int i = 2 ; int j; printf("%p<--- \n",&i); printf("%p<--- \n",&j); printf("%d<--- \n",sizeof(int)); return 0 ; } 0x7ffcbe7f207c<--- 0x7ffcbe7f2078<--- 4<---
数组
是常量指针#include <stdio.h> int main() { int arr [10] ; printf("%p<--- \n",&arr[0]); printf("%p<--- \n",&arr); printf("%p<--- \n",arr); printf("%p<--- \n",&arr[1]); printf("%d<--- \n",sizeof(int)); return 0 ; } 0x7ffc7c6ace40<--- 0x7ffc7c6ace40<--- 0x7ffc7c6ace40<--- 0x7ffc7c6ace44<--- 4<---
scanf : scanf("%d\n",&x) 记得加& 不然会放到不该放到地方去
int i;
int* p= &i;
int *p,q; : p是 一个地址变量 q是一个普通int类型
int* p,q; :p是 一个地址变量 q是一个普通int类型【没有int* 这种类型】
int * q, * p; :两者都是地址变量 具体实际值的变量的地址
void f(int * p ); // 再调用的时候得到某个变量的地址 int i = 0 ; f(&i); // 在函数里面可以通过这个指针访问外面的这个i
scanf("%d",i) ; 传入的是i未初始化的值 被当作地址使用 ,然后拿这个“地址”进行写值
访问那个地址上的变量*
- *是一个单目运算符,用来访问指针的值所表示的地址上的变量!!!!
- 可以做右值也可以为左值
- int k = * p;
- *p = k + 1
void f (int * p) { printf("%d\n", *p); printf("%p\n", p); *p = 26; } int main(void) { int i = 6; printf("%d\n",i); f(&i); //i的值被改 printf("%d\n",i); return 0; } //6 //6 //0x7ffde9dfbfbc //26
左值之所以叫左值
- 是因为出现在赋值号左边的不是变量,而是值,是表达式计算的结果
- a[0] = 2; a[0]不是变量
- *p = 3; *p不是变量
- 是特殊的值,所以叫做左值
指针的运算符 & *
- 互相反作用
- *&y -> *(&y) -> *(y) -> 得到那个地址上的变量 -> y
- &y -> &(y) -> &(y) -> 得到那个地址上的变量 -> y
♥ 应用场景swap(int *a ,int *b) 通过地址进行交换位置
#include <stdio.h> void swap(int *pa ,int *pb){ int t = *pa; printf("%d\n",t);//访问那个地址上的变量*, pa是地址 ,*pa是变量 *pa = *pb; *pb = t; printf("%p\n",pa); printf("0x%x\n",pa); //0x7ffc5d313ebc 64位架构 多了7ffc //0x5d313ebc } int main(){ int a = 1; int b = 2; swap(&a,&b); printf("%d\n",a); printf("%d\n",b); return 0; }
♥ 应用场景:例如int divide(...)里面出现除以0报错 可以用return 0; 来处理,而指针直接用来保存值
可以用于多个返回值,函数返回运算的状态,结果通过指针返回
int divide(int a, int b , int *res) { int ret = 1; if(b == 0 )ret = 0; else { * res = a/b; } return ret; } 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; }
常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错
-1 或者0
但是当任何数值都是有效的可能结果时,就得分来返回了
后续的语言c++ java采用的是异常机制来解决这个问题的
指针常见错误
p 的定义下来 *p也是被初始化
int p = 12;
*p = xxxx ;
如果直接int *p ; *p = 12 的话就会报错
- *p的初始值并非一个明确的值, 如果这样定义的话,会将某个不知道在哪里的区域赋值为12
上面的实例 如下
int main(void) { int a = 12 ; int *p = &a; //将a的地址给p 等价于定义一个(int*)p用来存放地址 p = &a printf("p=%d\n",p); printf("*p=%d\n",*p); *p = 2; //将p的地址对应的值更改为12 *p取出p对应地址的值 printf("p=%d\n",p); printf("*p=%d\n",*p); return 0; } 执行完成,耗时:4 ms p=2015583504 *p=12 p=2015583504 *p=2
示例二
#include <stdio.h> int main(){ int i = 1; int * q ; int * p ; p = &i; //地址赋值 int j = 2; printf("&i %p\n",&i); printf("q %p\n",q); printf("p %p\n",p); printf("&j %p\n",&j); return 0; } &i 0x7ffca350805 q (nil) p 0x7ffca350805 &j 0x7ffca350809
传入函数的数组成了什么
f(int arr[])
- 函数参数表中的数组实际上是指针
- sizeof(a) == sizeof(int *)
- 但是可以用数组的运算符[]进行运算
#include <stdio.h> void print(int arr[]){ //---> 指针!!! printf("siezof(arr) = %lu\n",sizeof(arr)); printf("p = %p\n",arr); } int main(){ int arr [10] = {1,3,4,5}; printf("siezof(arr) = %lu\n",sizeof(arr)); printf("siezof(arr) = %lu\n",sizeof(arr)/sizeof(arr[0])); printf("p = %p\n",arr); printf("\n"); print(arr); return 0; } siezof(arr) = 40 siezof(arr) = 10 p = 0x7ffc97cd4d1 siezof(arr) = 8 64位 指针为8个字节 p = 0x7ffc97cd4d1
int a(int arr[]) ... 格式不比java int[]arr 格式是错的
数组参数 下面四种是等价的
- int sum(int * ar ,int n )
- int sum(int * , int )
- int sum(int ar[] , int n)//看上去像一个数组 实际上是一个指针
- int sum(int [] , int )
数组变量是特殊的指针 可以用int * arr 代替 int arr[]
数组变量本身表达地址,所以
int a[10] ; int *p = a //无需用&取地址
但是数组的单元表达的是变量,需要用&取地址
a==&a[0]
[]运算符可以对数组做,也可以对指针做
p[0] = *p 且数组有效长度为1
void a(int *arr){ //直接对arr进行遍历 for(int i = 0 ; i < 10 ; i++) printf("%d ",arr[i]); //1 2 3 5 4 7 8 9 2 5 } int main(void) { int arr[10] = {1,2,3,5,4,7,8,9,2,5}; a(arr); return 0; }
运算符可以对指针做,也可以对数组做
*a = 12
数组变量是const的指针 不可变的,所以不能被赋值 !!!!!!!!!!!!
int b[] => int * const b
所以不能使用 即int b[] =x= arr
指针跟const
指针是const类型的话
- 表示一旦得到了某个变量的地址,不能再指向其他变量 / 或者指向别的地址
int * const q = & i ; // q是const *q = 26 ;//ok q++ ; // ERROR
所指是const
- 表示不能通过这个指针去修改那个变量(并不能使得那个变量成为了const)
const int * p = &i; 不能通过p去修改 *p = 26 ; //ERROR !(*p)是const i = 26 ; //OK p = & j ; // OK
解释:指针不可修改 / 不可用指针修改 看看 const 在* 号前还是后
- const在号前 const 所指的东西不能被修改 *p = 26 ; //ERROR
- const在*号后 *const 指针地址不能被修改 值可以改变
int i; const int* p1 = &i; 所指不能修改 = int const* p2 = &i; int * const p3 = &i; 指针不能修改
转换
- 总是可以把一个非const的值转化为const的
void f(const int * x); int a = 15; f(&a); //ok const int b =a ; f(& b) ; ok b = a +1 //Error
当要传递的参数类型比地址大的时候,这是常见的手段,既能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改 例如数组....传第一个 但是不能修改数组。
const数组
const int a [] = {1,2,3,4,5};
数组变量已经是const指针(a),这里的const表明数组的每个单元都是const int(1 2 3 4 5后面不能修改了)
所以必须通过初始化进行赋值,不然不能赋值
保护数组值
- 因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
- 为了保护数组不被函数破坏,可以设置参数为const
- int sum(const int a[] , int length);不会对数组进行修改
指针运算
这些算术运算可以对指针做
给指针加,减一个整数(+ += - -= ++ --)往后挪/往后挪
对指针互减
#include <stdio.h> int main(void) { char ac [] = {0,1,2,3,4,5,6,7,8,9,}; char *p = ac; //*p = ac[0] printf("p = %p\n", p); printf("p+1 = %p\n", p+1); int ai [] = {0,1,2,3,4,5,6,7,8,9,}; int *q = ai; //*p = ai[0] printf("q = %p\n", q); printf("q+1 = %p\n", q+1); return 0; } p = 0x7ffe0300e2d6 + sizeof(char ) p+1 = 0x7ffe0300e2d7 q = 0x7ffe0300e2a0 + sizeof(int) q+1 = 0x7ffe0300e2a4
#include <stdio.h> int main(void) { int ac [] = {0,11111111,2,3,4,5,6,7,8,9,}; int* p = ac;//而在地址上+1是没有意义的 char * o = ac 这就是地址加1 printf("p = %d\n", *(p+1)); ////我们写的代码 p+1 = p + sizeof(?) *(p+1) = > arr[x+1] return 0; } 11111111
一个指针加1 表示让指针指向下一个变量
int a[10]; int * p = a; *(P+1) - > a[1]如果指针不是指向一片连续分配的空间,如数据 则这种运算是没有任何意义的
*(p+n) ac[n]
#include int main(void) { int ac [] = {0,11111111,2,3,4,5,6,7,8,9,}; int *p = &ac[0]; int * q = &ac[6]; printf("q-p = %d\n", q-p); return 0; } q-p = 6 指针减去指针等于地址差处于sizeof(...)
*p++ --> p++ ---> *P
用于循环遍历
- 取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
- *的优先级虽然高 但是没有++高
- 常用于数组类的连续空间操作
- 在某些CPU上,这样直接被翻译成一条汇编指令
指针可以比较
- 相同?大于?小于?
0地址
- 内存都是有0地址的,通常0地址通常是不能碰的地址,所以你的指针不应该具有0值,是系统具有的
- 可以用0地址来表示特殊的事情 返回指针无效的,指针没有被真正初始化(先初始化为0)
- Null是一个预定定义的符号,表示0地址 有的编译器不愿意你用0来表示0地址
指针的类型
- 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
- 但是指向不同类型的指针是不能直接相互赋值的 ,这是为了避免用错指针 char的指针 int的指针之间强行转换后 char四个才是 int的一个 导致需要贡献出4个char来给int
char * q = ...; int * p = .... ; p =x= q;
指针可以强行转换
- void* 表示不知道指向什么东西的指针,计算时与char*相同(但不相通)
- 指针可以转换类型 int * p = &i; void* q = (void*)p ;
这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看到它所指的变量
我不再当你是int了,而是把你当成void
动态内存分配
int * a = (int)malloc(nsizeof(int)) 然后a就可以当作一个数组使用
- 借
#include <stdio.h> #include <stdlib.h> int main(void) { int length; scanf_s("%d", &length); int* arr = (int *)malloc(length * sizeof(int));//默认是void* 类型 for (int i = 0; i < length; i++) { scanf_s("%d", &arr[i]); } for (int i = length - 1; i >= 0; i--) { printf("%d ", arr[i]); } return 0; }
- 还 free() 因为地址需要初始化为int * p = 0;
#include <stdio.h> #include <stdlib.h> int main(void) { int length; scanf_s("%d", &length); int* p = (int *)malloc(length * sizeof(int)); //free(p + 1); //借来在那个地址就在哪个地址上free free(p); free(NULL); return 0; }
- 案例 计算机有多少MB
#include <stdio.h> #include <stdlib.h> int main(void) { void* p; int i = 0; while((p = malloc(100 * 1024 * 1024 ))){ i++; } printf("%d00MB\n", i); return 0; }
记录学习笔记