AutoLeaders控制组——指针学习笔记
C语言指针笔记
一.取地址运算
运算符&
scanf(“%d”,&i);里的&
&用来获取变量的地址
地址用十六进制表达比较方便
%x能输出十六进制
%p能以十六进制输出变量的地址
#include <stdio.h>
int main()
{
int i=0;
printf("%p\n",&i); //输出的为地址
return 0;
}
地址的大小,它的数据类型和int是否相等取决与编译器
&只能对变量取地址,不能对i++,++i,p+i这类取地址
数组的地址
#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]);
//与上面三个地址差距是4
return 0;
}
&a=&a[0]
数组相邻单元地址相差是4
二.指针
就是保存地址的变量
int *p,q;
int* p,q;
//以上两个意义相同,且只有p是指针
普通变量的值是实际的值
指针变量的值是具有实际值的变量的地址
#include <stdio.h>
void f(int *p);
int main()
{
int i=6;
printf("&i=%p\n",&i);
f(&i);
return 0;
}
void f(int *p)
{
printf("p=%p\n",p); //输出的为变量i的地址
printf("*p=%d\n",*p); //输出的为变量i的值
*p=26; //实际改了i的值
//p是一个指针指向i这个变量
}
*是一个单目运算符,用来访问指针的值所表示的地址上的变量
可以做右值也可以做左值
如:int k=*p ; *p=k+1
int i=3;
scanf("%d",i);//会误将i的值当成地址去处理
三.指针的使用
应用场景1
函数返回多个值,某些值就只能通过指针返回
#include <stdio.h>
void swap(int *pa,int *pb);
//交换a与b的值
int main(void)
{
int a=5;
int b=6;
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;
}
应用场景2
函数返回运算的状态,结果通过指针返回
#include <stdio.h>
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; //返回0即表示不可除
else{
*result = a/b;
}
return ret; //返回1表示满足条件
}
指针最常见错误
定义了指针变量,还没有指向任何变量,就开始使用指针
int *p;
*p=12 //不能直接这样写!
四.指针与数组
函数中输入数组:
函数参数表中的数组实际上是指针,故数组输入函数时[]里不写数
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];intp=a; //无需用&取地址
- 但是数组的单元表达的是变量,需要用&取地址
- a==&a[0]
- []运算符可以对数组做,也可以对指针做:
- p[0]<==>a[0]
- 运算符可以对指针做,也可以对数组做:
- *a = 25;
- 数组变量是const的指针,所以不能被赋值
- int a[] <==> int * const a=…
五.指针与const
指针可以是const,指针指向的变量也可以const
1.指针是const
- 表示一旦得到了某一个变量的地址,不能再指向其他变量
- int * const q = &i //q是const
- *q = 26 //OK
- q++ //ERROR
2.所指是const
- 表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const)
- const int *p = &i
- *p = 26 //ERROR
- i=26 //OK
- p = &j //OK
//前两个为所指不能被修改
const int* p1 = &i;
int const* p2 = &i;
//第三个为指针不能被修改
int *const p3 = &i;
判断哪个被const了的标志是const在*的前面还是后面
转换
- 总是可以把一个非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,6,}
- 数组变量已经是const的指针了,这里的const表明数组的每一个单元都是const int
- 所以必须通过初始化进行赋值
- 可以设置参数为const,用来保护数组不被函数破坏
- 如:int sum(const int a[], int length)
指针运算
指针+1不是在地址值上+1而是在地址上加一个size of那个指针所指的类型
#include <stdio.h>
int main(void)
{
long ac [] = {0,1,2,3,4,5,6,};
long *p = ac;
printf("p = %p\n",p);
printf("p+1 = %p\n",p+1);
printf("*(p+1)=%d\n", *(p+1)); //*(p+n)<->ac[n]
int ai [] = {0,1,2,3,4,5,6,};
int *q = ai;
int *q1 = ai[6]
printf("q = %p\n",q);
printf("q+1 = %p\n",q+1);
printf("q1-1=%d\n",q1-q);
//结果为这两个地址的差除以sizeof的类型,表示在它们中间能放几个这样类型的东西
return 0;
}
*p++
- 取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
- *的优先级虽然高,但是没有++高
#include <stdio.h>
int main(void)
{
char ac [] = {0,1,2,3,4,5,6,-1,};
char *p = ac;
int i;
//以前的遍历方法
for ( i=0; i<sizeof(ac)/sizeof(ac[0]);i++){
printf("%d\n",ac[i]);
}
//用指针进行遍历
while (*p != -1){
printf("%d\n",*p++);
}
return 0;
}
指针比较
- <,<=,==,>,>=,!=都可以对指针做,
- 用来比较它们在内存中的地址,
- 数组中的单元的地址肯定是线性递增的
0地址
- 0地址通常是不能随便碰的地址
- 所以指针不应该具有0值
- NULL是一个预定定义的符号,表示0地址
指针的类型
- 无论指向什么类型,所以的指针的大小都是一样的,因为都是地址
- 指向不同类型的指针是不能直接相互赋值的(为了避免用错指针)
指针的用途
- 需要传入较大的数据时用作参数
- 传入数组后对数组做操作
- 函数返回不止一个结果
- 需要用函数修改不止一个变量
六.动态内存分配
malloc
#include<stdib.h>
void* malloc(size_t size);
- 向malloc申请的空间的大小是以字节为单位的
- 返回的结果是void*,需要类型转换为自己需要的类型
- (int*)malloc(n*sizeof(int))
#include <stdio.h>
#include <stdlib.h> //malloc的头文件
int main(void)
{
int number;
int *a;
int i;
printf("输入数量:");
scanf("%d",&number);
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",a[i]); //逆序输出数组
}
free(a); //要用free还回去
return 0;
}
free()只能还申请来的空间的首地址