指针 :
指针也就是内存地址,指针变量是用来存放内存地址的变量,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。 —— 百度百科
#include<stdio.h>
void f(int *);
int main()
{
int i=1;
int a=2;
int *p=&i;
/*
表示int是一个指针,它指向一个int,现在把i的地址交给了p
它同等于:
int *p;
p=&i;
但注意的是,不能像下面这样写:
int *p;
*p=&i;
这样的用法是不对的。
*/
p=&i; //&符号的作用:取地址,是个取地址符(注意C语言没有引用,C++才有引用)
printf("p的值 %p\n",p);
printf("&i %p\n",&i);
/*
可以从下面的输出结果看到,p的值和i的地址是一样的,可见
在指针变量p里面放的是地址,而不是实际的值,从而说p是指向变
量i的一个指针(p指向了i)。
*/
printf("p=&i %d\n",*p);
*p=a;
/*
*是一个单目运算符,用来访问指针的值所表示的地址上的变量,
*p可以做右值也可以做左值。p的值是a的地址,*p代表了a。
所以可以通过*p来改变*p所指向的变量的值。在输出结果中所示,
因为p是指向i的指针,经过*p=a结果f(&i)输出结果为2。
*/
printf("*p=a %d\n",*p);
f(&i);//指针做参数时传地址
return 0;
}
void f(int *p_)
{
printf("f(&i) %d\n",*p_);
}
运行结果:
左值之所以叫左值: 是因为出现在赋值号左边的不是变量,而是值,是表达式计算的结果。
a[0] = 2; * p = 3;
*是个运算符,*p代表去取得p这个指针它的地址所代表的那个变量。
数组的方括号[ ]也是一个运算符,是取下标的运算符。
如果对取得的地址去取它所代表的变量,那就是原来的那个变量
如果对一个指针所指的那个变量再去取地址那就是原来那个指针
void swap(int *m,int *n)//函数可以通过指针返回多个值
{
int tmp;
tmp=*m;
*m=*n;
*n=tmp;
}
数组与指针 :
数组变量本身表达地址
int a[10]; int *p=a;
无需用& 取地址 但是数组的单元表达的是变量,需要用&取地址 a==&a[0];
[ ]运算符可以对数组做,也可以对指针做。 p[0] <==> a[0] *运算符可以对指针做,也可以对数组做
数组变量是const的指针,所以不能被赋值 int a[ ] <==> int * const a=…
#include<stdio.h>
void minmax(int a[],int len,int *max,int *min);
// void minmax(int *a,int len,int *max,int *min);
int main()
{
int a[]={1,2,3,4,5};
int min,max;
printf("main sizeof(a)=%lu\n",sizeof(a));
minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);//a==&a[0]
printf("min=%d,max=%d\n",min,max);
printf("a[0]的地址 %p\n",&a[0]);
//数组的单元表达式是变量,需要用&取地址
printf("a的地址 %p\n",a);
//从下面的输出可知,a本身表达地址
// a==&a[0];
return 0;
}
void minmax(int a[],int len,int *min,int *max)
//void minmax(int *a,int len,int *min,int *max)
// a[]其实是个指针,等同于*a,所以可以换成*a
{
int i;
printf("minmax sizeof(a)=%lu\n",sizeof(a));
*min = *max = a[0];
for (int i=0;i<len;i++){
if(a[i]<*min){
*min = a[i];
}
if(a[i]>*max){
*max=a[i];
}
}
}
/*
函数参数表中的数组实际上是指针
sizeof(a)==sizeof(int *)
但是可以用数组的运算符进行运算
*/
输出结果(32位架构):
以下四种函数原型是等价的 :
int sum (int *ar,int n);
int sum (int *,int);
int sum (int ar[],int n);
int sum (int [],int);
数组变量是const指针:
#include<stdio.h>
int main(){
int a[]={1,2,3,4,5};
int b[10];
b=a;//这样是不行的!!!
/*
数组变量间是不能赋值的
int a[] 可以看作是 int * const a;
所以数组变量是个常量,被初始化出来了就不能变了,
所以数组是一个常量指针
*/
return 0;
}
指针与const :
如果const在 * 号的前面,表示指针所指向的地址不能被修改。如果在后面,则表示不能通过指针去修改它指的变量的值。
int *const p = &i;//p永远指向i的地址,它们的关系永远不变
*q = 12;//OK
q++;//ERROR
//int const *p = &i;
const int *p = &i;//表示不能通过指针p去修改那个变量。
*p = 12;//ERROR ( *p 是const )
i=12;//OK
p=&j;//OK
数组与const :
const int a[]={1,2,3};
- 表示数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int
- 所以必须通过初始化进行赋值
#include<stdio.h>
int main(){
const int a[]={1,2,3};//必须这样赋值
//等同于 int const a[]={1,2,3};
//const int a[3];//ERROR
a[0]=12; //ERROR
return 0;
}
int sum (const int a[])
//可以保护数组不被在函数里面修改值,也就是说函数里没有办法去修改数组的值
例如:
#include<stdio.h>
int sum (const int a[])
{
a[0]=12;//ERROR: 函数内部不能修改数组
return a[0];
}
int main()
{
int a[]={1,2,3};//注意这里没有用const
return 0;
}
指针的相关运算 :
指针的运算: 当取两个指针的减法时得到的是这两个地址的差除以sizeof(类型); 指针的比较:
- <,<=,==,>,>=,!= 都可以对指针做
- 比较它们在内存中的地址
- 数组中的单元的地址肯定是线性递增的 0地址:
- 内存中有0地址,但是0地址通常是个不能随便碰的地址
- 所以指针不应该有0值
- 因此可以用0地址来表示特殊的事情: * 返回的指针是无效的 * 指针没有被真正初始化(先初始化为0)
- NULL是一个约定定义的符号,表示0地址 * 有的编译器不愿意你用0来表示0地址
指针的类型转换:
- void*表示不知道指向什么东西的指针
- 计算时与char*相同(但不相通)
- 指针也可以转换类型
- int *p=&i; void *q=(void *)p ;
*这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量 - 我不当你int,我认为你就是个void
- int *p=&i; void *q=(void *)p ;
#include<stdio.h>
int main(){
char ac[]={0,1,2,3,4,5,6,7,8,9,-1};
char *p=ac;
char *p1=&ac[5];
printf("p = %p\n",p);
printf("p1 = %p\n",p1);
printf("p+1 = %p\n",p+1);
/*
p+1就是在p的地址上加sizeof(char);
所以下面的q+1就是p的地址+sizeof(int);
如果指针指的不是连续的内存空间,那么这种运算显然没有意义
*/
printf("p1-p = %d\n",p1-p);
/*
得到的是两个p1和p的地址的差再除以sizeof(char);
下面的q1-q同理
*/
printf("*(p+2) = %d\n",*(p+2));
/*
从下面的输出结果可知*(p+2)等同于ac[2]
*/
putchar('\n');
int ai[]={0,1,2,3,4,5,6,7,8,9};
int *q=ai;
int *q1=&ai[5];
printf("q = %p\n",q);
printf("q1 = %p\n",q1);
printf("q+1 = %p\n",q+1);
printf("q1-q = %d\n",q1-q);
printf("*(q+2) = %d\n",*(q1+2));
putchar('\n');
printf("sizeof(char) = %d\n",sizeof(char));
printf("sizeof(int ) = %d\n",sizeof(int));
putchar('\n');
while(*p!=-1){
printf("%d ",*p++);
}
/*
取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
*的优先级高,但是没有++高
常用于数组类的连续空间操作
在某些CPU上,这可以直接被翻译成一条汇编命令
*/
//q=p; ERROR 两个不同类型的值针不能相互赋值
return 0;
}
输出结果(32位架构):
动态内存分配:
#include<stdio.h>
#include<stdlib.h>//malloc的头文件
int main(){
int number;
int *a;
scanf("%d",&number);
a=(int*)malloc(number*sizeof(int));
/*
向malloc申请的空间的大小是以字节为单位的
之所以要用(int*) 强制类型转换,是因为malloc返回的是void*类型的,需要转换为我们需要的类型
*/
free(a);
/*
申请得来的空间需要要(还回去)释放
*/
a++;
free(a);//ERROR
/*
只能还申请来的空间的首地址
int i;
a=&i;
free(a);//ERROR
free是malloc配套的函数,申请的空间需要还回去
*/
return 0;
}
参考资料:
https://baike.baidu.com/item/%E6%8C%87%E9%92%88/2878304?fr=aladdin
https://www.icourse163.org/?from=study(C语言 — 翁恺老师)
http://www.imooc.com/article/30475