一、指针
1、运算符&
(1)在不同的编译器架构下,&i的大小分别为4字节和8字节,&取出的地址大小是否与int的大小相同与编译器的类型有关系,所以输出变量地址应该采用%p来进行输出,而不是将变量转化为Int类型,然后进行进制转换输出。
scanf("%d",&i);
& 运算符,取得变量的地址,操作数必须为一个变量。
#include<stdio.h>
int main(){
int i=1;
printf("0x%x\n",&i);
}
定义一个变量i,使用&取出i的地址,使用16进制进行输出:
查阅相关资料发现:在C语言中输出变量的地址采用%p来进行输出:
#include<stdio.h>
int main(){
int i=1;
printf("0x%x\n",&i);
printf("%p",&i);
}
使用一个变量来接受i的地址:
#include<stdio.h>
int main(){
int i=1;
int p;
p=(int)&i;
printf("0x%x\n",p);
printf("0x%x\n",&i);
printf("%p",&i);
}
当前的编译器选择的是32位架构,结果相同,将编译器改为64位架构:
#include<stdio.h>
int main(){
int i=1;
int p;
p=(int)&i;
printf("0x%x\n",p);
printf("0x%x\n",&i);
printf("%lu\n",sizeof(int));
printf("%lu\n",sizeof(&i));
}
64位架构:
32位架构:
(2)&的操作对象必须为一个变量,如果不是一个变量,那么就无法进行操作。
#include<stdio.h>
int main(){
int a=1,b=2;
int p;
p=(int)&(a+b);
printf("%p",p);
}
(3)相邻变量的地址,变量地址相邻,地址占据4个字节,先定义的变量在地址在后面。
#include<stdio.h>
int main(){
int a=1,b=2;
printf("%p\n",&a);
printf("%p\n",&b);
}
C在16进制为12,12-8=4,4字节刚好与int字节相同,同时ab为本地变量,存储在堆栈里面,堆栈存储变量的方式是从顶向下进行存储。
(4)数组与数组单元的地址:数组名称与第一个数组元素地址及数组地址相同,数组元素地址按从小到大顺序排列。
#include<stdio.h>
int main(){
int a[10];
printf("%p\n",&a);
printf("%p\n",a);
printf("%p\n",&a[0]);
printf("%p\n",&a[1]);
return 0;
}
定义了10个int类型元素的数组,a和&a、a[0]全部相等,而且数组按顺序从大到小排序,间隔4个字节。
2、指针
知道一个变量的地址,将地址传递给一个函数,是否可以通过地址来访问到变量。
scanf("%d",&a);
scanf也就需要一个东西来存储变量的地址,上述分析将地址交给一个整数随着编译器的变化,其也会发生变化,故C语言引入指针的概念。
(1)一个指针就是一个保存地址的变量。
int i;
int *p=&i;
p是一个指针,将i的地址交给了p(p指向了i)p的值就是i的地址。
(2)定义指针要一个一个的定义。
int *p,q;
上式:q为一个int类型的变量,*p为int类型,p为一个指针。
(3)普通变量的值就是实际的值,指针变量的值就是实际值的地址。
(4)函数应用指针时,应该给传递函数一个地址,外部函数应该使用指针接收,这样外部函数里面的指针就可以访问外面的变量。
void f(int *p){};
int main(){
int q;
f(&q);
}
#include<stdio.h>
void f(int *p){
printf("p=%p\n",p);
}
int main(){
int a=1;
printf("&a=%p\n",&a);
f(&a);
return 0;
}
通过指针在函数得到了外部变量a的地址, 其与a是有关系的,但是假如直接给函数传入一个a,那么函数就得到了一个数值,其与a并无联系。
(5)p为得到的地址,在C语言中引入单目运算符*来访问得到地址的值,然后进行读写操作。
#include<stdio.h>
void f(int *p){
int k=*p;
printf("*p: %d\n",*p);
printf("k: %d\n",k);//读变量的值
*p=9;//修改变量的值
}
int main(){
int a=1;
printf("a1:%d\n",a);
f(&a);
printf("a2: %d\n",a);
return 0;
}
先定义了一个变量a=1,使用地址传入,外部函数使用指针接收,读*p进行读写输出k=1,再进行了*p的修改,返回主函数时,该值修改为9。
int i;
scanf("%d",i);
不会报错是因为int 为4个字节,用32位编译的时候地址也是4个,scanf会用i的值作为地址去访问,将输入的值写入i那个地址里面,不会报错,但是使用会出错。
3、指针的使用
(1)交换值。
#include<stdio.h>
void f(int *p1,int *p2){
int t=*p1;
*p1=*p2;
*p2=t;
}
int main(){
int a=1,b=99;
printf("交换前:a=%d b=%d\n",a,b);
f(&a,&b);//交换变量
printf("交换后:a=%d b=%d\n",a,b);
return 0;
}
(2)函数要返回多个值,某些值要指针带回(返回最大最小值)。
#include<stdio.h>
void minmax(int a[],int len,int *min,int *max){
*min=*max=a[0];
int i;
for(i=0;i<len;i++){
if(a[i]<*min){
*min=a[i];
}
if(a[i]>*max){
*max=a[i];
}
}
}
int main(){
int a[]={1,23,234,34,12,22,13,341,332,33,4,5};
int len=sizeof(a)/sizeof(a[0]);
int min,max;
minmax(a,len,&min,&max);
printf("min:%d max:%d",min,max);
return 0;
}
(3)返回过程状态
#include<stdio.h>
//除法成功返回1,失败返回0
int cf(int a,int b,int *result){
int ret=1;
if(b==0){
ret=0;
}else{
*result=a/b;
}
return ret;
}
int main(){
int a=99,b=2;
int result;
if(cf(a,b,&result)){
printf("成功:%d",result);
}else{
printf("失败");
}
}
tips:
int *p;
*p=12;
*p随机指向一个地址,当赋值的时候,如果该地址的值可以修改,那么代码不会出错,但是不可修改,那么代码就会崩溃。
4、指针与数组
#include<stdio.h>
void minmax(int a[],int len,int *min,int *max){
*min=*max=a[0];
int i;
printf("minmaxsizeof:%lu\n",sizeof(a));
printf("minmax :%p\n",a);
for(i=0;i<len;i++){
if(a[i]<*min){
*min=a[i];
}
if(a[i]>*max){
*max=a[i];
}
}
}
int main(){
int a[]={1,23,234,34,12,22,13,341,332,33,4,5};
printf("mainsizeof:%lu\n",sizeof(a));
printf("main :%p\n",a);
int len=sizeof(a)/sizeof(a[0]);
int min,max;
minmax(a,len,&min,&max);
// printf("min:%d max:%d",min,max);
return 0;
}
发现传入的数组实际上为一个指针,sizeof(a)=sizeof(*int),将传入参数修改为指针:
#include<stdio.h>
void minmax(int *a,int len,int *min,int *max){
*min=*max=a[0];
int i;
printf("minmaxsizeof:%lu\n",sizeof(a));
printf("minmax :%p\n",a);
for(i=0;i<len;i++){
if(a[i]<*min){
*min=a[i];
}
if(a[i]>*max){
*max=a[i];
}
}
}
int main(){
int a[]={1,23,234,34,12,22,13,341,332,33,4,5};
printf("mainsizeof:%lu\n",sizeof(a));
printf("main :%p\n",a);
int len=sizeof(a)/sizeof(a[0]);
int min,max;
minmax(a,len,&min,&max);
// printf("min:%d max:%d",min,max);
return 0;
}
结果相同,在过程里面可以进行[]运算,[]与*在原型上面是等价的。
数组变量是特殊的指针,数组变量本身表示为地址,无须使用&;但是数组单元表示的是变量,需要使用&。
int arr[10];
int *p=arr;
[]除了可以对数组进行使用还可以对指针进行使用,*不仅可以对指针进行使用还可以对数组进行使用。
#include<stdio.h>
int main(){
int arr[10]={11,2,3,4,34,35,6,4,657,23};
int *p=arr;
printf("p[0]:%d\n",p[0]);//第一个元素
printf("p[1]:%d\n",p[1]);//第一个元素
printf("*arr:%d\n",*arr);
return 0;
}
数组变量本身为一个const指针,所以对数组变量不可以进行直接赋值。
int a[] --> int * const a
指针与const关系:
int * const p=&a;
*p=100;
p++;
const指针表示p的值不可以改变,p为a的地址,但是*p是可以进行修改的。
const int *p=&a;
*p=100;
a=200;
p=&j;
上述不可以,因为const让*p不可以修改,其它均可以修改,可以修改p所指的对象,以及其它操作。
二、指针的运算
#include<stdio.h>
int main(){
char a[]={0,1,2,3,4,5,6,7,8,9,};
char *p=a;
printf("p: %p\n",p);
printf("p+1: %p\n",p+1);
int b[]={0,1,2,3,4,5,6,7,8,9,};
int *q=b;
printf("q: %p\n",q);
printf("q+1: %p\n",q+1);
return 0;
}
对指针加1其对应的是在地址上加上sizeof(值) ,也就对指针加1,指针指向下一个单位元。
#include<stdio.h>
int main(){
char a[]={0,1,2,3,4,5,6,7,8,9,};
char *p=a;
printf("*(p+1): %d\n",*(p+1));
int b[]={0,1,2,3,4,5,6,7,8,9,};
int *q=b;
printf("*(q+1): %d\n",*(q+1));
return 0;
}
指向下一个单位元,1+1!=2 !!!下式等价。
*(p+i)=a[i];
*(q+i)=b[i];
两个指针相减,返回的是地址相减/对应的sizeof(值):
#include<stdio.h>
int main(){
char a[]={0,1,2,3,4,5,6,7,8,9,};
char *p=a;
char *p1=&a[5];
printf("p1-p:%d\n",p1-p);
int b[]={0,1,2,3,4,5,6,7,8,9,};
int *q=b;
int *q1=&b[5];
printf("q1-q:%d\n",q1-q);
return 0;
}
*p++遍历数组:
*p++:取出*p的值,随便将指针移向后一位:
#include<stdio.h>
int main(){
char a[]={0,1,2,3,4,5,6,7,8,9,-1};
char *p=a;
int i;
for(i=0;i<sizeof(a)/sizeof(a[0]);i++){
printf("%d\n",a[i]);//for循环遍历数组
}
while(*p!=-1){//条件可以在最后加一个值进行判断
printf("%d\n",*p++);//while循环指针遍历
}
return 0;
}
0地址:每个程序都有一个零地址,但是不可以触碰,C语言采用null表示0地址,可以使用其进行指针初始化,看指针是否有用。
不同类型的指针不可以相互赋值。
void* :不知道指向什么东西的指针。