一、取地址运算
1、数据类型
C语言的数据类型有:
·整数:char、short、int、long、long long
·浮点数:floot、double、long double
·逻辑:bool
·指针
·自定义类型
2、内存、地址与运算符&
数据储存在内存中,每个数据在内存中的位置需要根据数据的地址与对应的储存空间大小。
运算符&的作用:获得变量的地址,它的操作数必须是变量。
注:&不能对没有地址的东西取地址。例如,&(a+b);&(a++);都是错的。&后必须接一个明确的变量。
二、指针
1、指针定义
保存地址的变量。 指针本身是一个变量,普通变量的值是实际的值,而指针变量的值是具有实际值的变量的地址。
例如,int *p=&i; 意味着变量p保存了变量i的地址,变量p作为一个指针,指向了变量i。其中p的值是i的地址,*p的值是地址所指变量也就是变量i的值。
2、声明指针
数据类型*变量名; 如 int *p;
*是一个单目运算符,用来访问指针的值所表示的地址上的变量。
三、指针与函数
1、在函数内部访问、修改变量
当变量传入函数时,传递的仅仅是一个值,导致函数并没有办法真正访问、修改这个变量。而通过指针,我们将变量的地址传入,在函数内部也可以访问、修改变量。例如,
#include <stdio.h>
void f(int *x);
int main(void)
{
int a;
a=4;
f(&a);
printf("%d",a);
return 0;
}
void f(int *x){
*x=*x+4;
}
运行结果: 8 代表变量a在函数中被改变了。
注:使用函数时,参数表中要放地址,而不能直接放指针。
2、函数返回多个值
当函数要返回多个值时,某些值就只能通过指针返回。而传入函数的参数实际上是需要保存带回结果的变量。例如在交换变量时,两个变量的值都需要改变,且从函数返回。
#include <stdio.h>
void swap(int *x,int *y);
int main(void)
{
int a=1,b=2,c=3,d=4;
printf("%d %d %d %d\n",a,b,c,d);
swap(&a,&d);
swap(&b,&c);
printf("%d %d %d %d",a,b,c,d);
return 0;
}
void swap(int *x, int *y)
{
int t;
t=*x;
*x=*y;
*y=t;
}
运行结果: 1 2 3 4
4 3 2 1
3、检测函数运算状态
当要检测函数运算状态时,常用的套路是让函数返回特殊的不属于有效范围内的值来表示运算出错。但当任何数值都是有效的可能结果时,我们可以让函数返回运算状态,而结果通过指针返回。此时可以单独对函数返回值进行判断。例如,
#include <stdio.h>
int divide(int a,int b,int *p);
int main(){
int a,b,c;
scanf("%d %d",&a,&b);
if((divide(a,b,&c)))
printf("%d/%d=%d",a,b,c);
else printf("出错");
return 0;
}
int divide(int a,int b,int *p)
{
int ret=1;
if(b==0)
ret=0;
else *p=a/b;
return ret;
}
输入:1 0 输出:出错
输入:4 2 输出:2
四、指针与数组
1、指针与数组关系
实际上,数组变量是const指针,不能被赋值。即数组变量本身就是地址,所以int *p=a; 时,a不需要取地址。 但是,数组单元表达的是变量,需要用&取地址。同时,a的地址就是a[0]的地址,即a==&a[0]。
[ ]可以对指针做,而*p也可以对数组做。即*p==p[0],a[0]==*a。
2、在函数中传递数组
在函数头的参数表中,传递数组实质上是在传递指针。
#include <stdio.h>
void fmin(int a[],int len,int *min);
int main(void)
{
int a[]={2,5,7,11,13};
int min;
int len=sizeof(a)/sizeof(a[0]);
fmin(a,len,&min);
printf("%d",min);
return 0;
}
void fmin(int a[],int len,int *min)
{
int i;
*min=a[0];
for(i=1;i<len;i++){
if(*min>a[i])
*min=a[i];
}
}
运行结果: 2
五、指针与const
1、指针是const
const在*之后(int *const q=&i):表示指针一旦得到了某个变量的地址,不能再指向其他变量。(i的值可以变,但指针q一定得指向变量i)。
即 int *const q=&i;
*q=&j; 不能进行 , *q=26;可以进行。
2、所指是const
const在*之前(const int *p=&i):表示不能通过指针去修改那个变量,但不代表那个变量成为const。
即const int *p=&j;
*p=26; 不能进行,*p=&i可以进行。
3、一般使用情况
当要传递的参数类型比地址大时,用指针传递值给函数更方便。而const int *p可以使函数内部不改变x的值。例如
#include <stdio.h>
#include <math.h>
void f(int a);
int main(void)
{
long long i;
i=pow(10,10);
long long*x=&i;
printf("%lld\n",i);
void f (const int *x);
printf("%lld",i);
return 0;
}
void f(int a){
a=pow(10,9);
}
运行结果:10000000000
10000000000
变量的值没有通过指针改变。
六、指针运算
前提:指针要指向一片连续分配的空间,如数组,对指针进行运算才有意义。
1、指针算数运算
对指针进行算数运算大概有三个类型。
第一,两个指针相减,所得到的结果是 两个地址之差/sizeof(相应数据类型)。
#include <stdio.h>
int main()
{
int a[]={1,2,3,4};
char b[]={1,2,3,4};
int *m=a;
int *n=&a[1];
char *p=b;
char *q=&b[2];
printf("%d\n",*n-*m);
printf("%d",*q-*p);
return 0;
}
运行结果:1
2
第二,给指针加减一个整数,本质上是在给指针“挪位置”。例如,*(p+1)就是指向*p所指向的变量在数组中的下一个变量。
#include <stdio.h>
int main()
{
int a[]={0,1,2,3};
int *p=a;
printf("%d",*(p+1));
return 0;
}
运行结果: 1
第三,指针递增递减。比如*p++,是指取p所指的数据,完事之后顺便把p移到下一个位置。注意,优先级为++>*,且p++的值仍然为p。常用于数组类的连续空间操作,便于遍历整个数组。(常常要在数组中设定一个数,使for循环结束)
#include <stdio.h>
int main()
{
int a[]={0,1,2,3,4,5,-1};
int *p=a;
do
{
printf("%d",*p++);
}while(*p!=-1);
return 0;
}
运行结果: 012345
2、指针比较
由于数组中的单元的地址使线性递增的,比较指针也就是比较它们在内存中的位置。
七、动态内存分析
1、void*
void*指不知道指向什么的指针,计算时与char*相同。
注意:int *p=&i; void*q=(void*)p;中,后一句并没有改变i的类型,而是让后人用不同的眼光通过p看它所指的变量。即
p所指向的变量i的类型为int,而q所指向的变量i不知道是什么类型,但i本身类型没有被改变。
2、用malloc输入数据
这里以输入数组为例。
要通过malloc输入数组,首先,要另外一个头文件
#include <stdlib.h>
其次,要告诉指针输入数据所占内存大小。
int *a=(int*)malloc(number *sizeof(int); 其中number为数组长度。
然后,把指针直接当作数组,利用for循环输入每个数组每个变量的值。
int i=0;
for(i=0;i<number;i++){
scanf("%d",&a[i]);}
最后,用free释放向系统借的动态内存。
free(a); 注:这里free要还的是首地址!
注意:返回的值是void,如果申请动态地址失败则会返回0或者NULL。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int number;
scanf("%d",&number);
int *a=(int*)malloc(number*sizeof(int));
int i=0;
for(i=0;i<number;i++){
scanf("%d",&a[i]);}
for(i=0;i<number;i++){
printf("%d",a[i]);
}
free(a);
return 0;
}
输入:5
1 2 3 4 5
运行结果:12345