一.取地址运算
1.sizeof
-
是一个运算符,给出某个类型或变量在内存中所占据的字节数
-
sizeof(int)
-
sizeof(i)
-
2.运算符&
-
scanf("%d", &i);里的&
-
获取变量的地址,它的操作数必须是变量
-
int i; printf("%x", &i);
-
-
地址的大小是否与int相同取决于编译器
-
int i; printf("%p", &i);
-
3.&不能取得地址
-
&不能对没有地址的东西取地址
-
&(a+b)?
-
&(a++)?
-
&(++a)?
-
4.指针
-
就是保存地址的变量
-
int i;
-
int* p = &i;
-
int* p, q; // *p是一个int, p是一个指针
-
int *p, *q;
5.指针变量
-
变量的值是内存的地址
-
普通变量的值是实际的值
-
指针变量的值是具有实际值的变量的地址
-
6.作为参数的指针
-
void f(int *p);
-
在被调用的时候得到了某个变量的地址:
-
int i=0; f(&i);
-
-
在函数里面可以通过这个指针访问外面的这个i
7.访问那个地址上的变量*
-
*是一个单目运算符,用来访问指针的值的所在地址上的变量
-
可以做右值也可以做左值
-
int k = *p;
-
*p = k + 1;
-
二.指针的使用
1.指针应用场景一
-
交换两个变量的值
void swap(int *pa, int *pb) { int t = *pa; *pa = *pb; *pb = t; }
2.指针应用场景二
-
函数返回多个值,某些值就只能通过指针返回
-
传入的参数实际上是需要保存带回的结果的变量
-
3.指针应用场景二B
-
函数返回运算的状态,结果通过指针返回
-
常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错:
-
-1 或 0 (在文件操作会看到大量的例子)
-
-
但是当任何数值都是有效的可能结果时,就得分开返回了
4.指针最常见的错误
-
定义了指针变量,还没有指向任何变量,就开始使用指针
三.指针与数组
1.传入函数的数组,成了什么?
int isPrime(int x, int knownPrimes[], int numberOfKnownPrimes) { int ret = 1; int i; for ( i=0; i<numberOfKnownPrimes; i++){ if( x % knownPrimes[i] ==0 ){ ret = 0; break; } } return ret; }
-
函数参数表中的数组实际上是指针
-
sizeof(a) == sizeof(int*)
-
但是可以用数组的运算符[]进行运算
-
2.数组参数
-
以下四种函数原型是等价的:
-
int sum(int *ar,int n);
-
int sum(int *,int);
-
int sum(int ar[], int n);
-
int sum(int [], int);
-
3.数组变量是特殊的指针
-
数组变量本身表达地址,所以
-
int a[10];int *p=a; // 无需用&取地址
-
但是数组的单元表达的是变量,需要用&曲地址
-
a == &a[0]
-
-
[]运算符可以对数组做,也可以对指针做:
-
p[0] <==> a[0]
-
-
*运算符可以对指针做,也可以对数组做:
-
*a = 25;
-
-
数组变量是const的指针,所以不能被赋值
-
int a[] <==> int *const a=...
-
四.指针与const
1.指针值const
-
表示一旦得到了某个变量的地址,不能再指向其他变量
-
int* const q = &i;// q 是 const
-
*q = 26; //OK
-
q++; //ERROR
-
2.所指是const
-
表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const)
-
const int *p = &i;
-
p = 26; // ERROR! (P)是const
-
i = 26; // OK
-
p = &j; // OK 判断哪个被const了的标志是const在*的前面还是后面
-
3.转换
-
总是可以把一个非const的值转换成const的
void f(const int *x); int a = 15; f(&a); // OK const int b = a; f(&b); // OK b = a + 1; // Error!
-
当要传递的参数的类型比地址大的时候,这是常用的手段:既能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改
4.const数组
-
const int a[] = {1,2,3,4,5,6,};
-
数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int
-
所以必须通过初始化进行赋值
5.保护数组值
-
因为把数组传入函数时传递的是地址,所以函数内部可以修改数组的值
-
为了保护数组不被函数破坏,可以设置参数为const
-
int sum(const int a[], int length);
-
五.指针运算
1. 1+1=2?
-
给一个指针加1表示要让指针指向下一个变量
-
int a[10];
-
int *p = a; *(p+1) ——> a[1]
-
-
如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义
2.指针计算
-
这些算数运算可以对指针做
-
给指针加、减一个整数(+,+=,-,-=)
-
递增递减(++,--)
-
两个指针相减
-
3.*p++
-
取出p所指的那个数据,完事之后顺便把p移到下一个位置去
-
*的优先级虽然高,但没有++高
-
常用于数组类的连续空间操作
-
在某些CPU上,这可以直接被翻译成一条汇编指令
4.指针比较
-
<,<=,==,>,>=,!=都可以对指针做
-
比较它们在内存中的地址
-
数组中的单元的地址肯定是线性递增的
5. 0地址
-
当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
-
所以你的指针不应该具有0值
-
因此可以用0地址来表示特殊的事情:
-
返回的值是无效的
-
指针没有被真正的初始化(先初始化为0)
-
-
NULL是一个预定定义的符号,表示0地址
-
有的编译器不愿意你用0来表示0地址
-
6.指针的类型
-
无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
-
但是指向不同类型的指针是不能直接相互赋值的
-
这是为了避免用错指针
7.指针的类型转换
-
void* 表示不知道指向什么东西的指针
-
计算时与char*相同(但不相通)
-
-
指针也可以转换类型
-
int *p = &i; void *q=(viod *)p;
-
-
这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量
-
我不再当你是int啦,我认为你就是个void!
-
8.用指针来做什么
-
需要传入较大的数据时用作参数
-
传入数组后对数组做操作
-
函数返回不止一个结果
-
需要用函数修改不止一个变量
-
动态申请的内存
六.动态内存分配
1.输入数据
-
如果输入数据时,先告诉你个数,然后在输入,要记录每个数据
-
C99可以用变量做数组定义的大小。
-
int *a =(int *)malloc(n *sizeof(int));
2.malloc
-
#include <stdlib.h>
-
void* malloc(size_t size);
-
向malloc申请的空间的大小是以字节为单位的
-
返回的结果时viod*,需要类型转换为自己需要的类型
-
(int)malloc(nsizeof(int))
-