指针
指针初始化
首先,了解两个符号: ‘*’ 为取值运算符, ‘&’为取址运算符;
*p:代表取变量p的值,&a代表取变量a的地址;
初始化指针:
int *p, a = 4; //定义一个整型指针*p,整型变量a=4
p = &a; //取a的地址赋给p,这样p存放的即为变量a的地址
//初始化也可写为 int *p = &a 这和上面两句是等价的,当然要把定义a放在这一句前面
printf("*p is %d\n", *p); //*p为取p的值,p为a的地址,所以等同于取a的值
printf("p address is : %d\n", p);
printf("a is %d\n", a); //即*p等于a
printf("a address is : %d\n", &a);
运行结果为:
可以看到p值即为a的地址,*p等于a;
指针与数组
数组名a代表数组的首地址,将地址赋给指针p,那*p就代表数组的第一个元素;
int a[5] = {1, 8, 2, 3, 5};
int *p;
p = a;
printf("a is %d\n", a);
printf("p is %d\n", p);
printf("a[0] is %d\n", a[0]);
printf("*p is %d\n", *p);
printf("*(p+1) is : %d\n", *(p+1));
printf("*(p+2) is : %d\n", *(p+2));
system("pause");
return 0;
结果如下:
可以看到* (p+1)代表数组从首地址偏移一个整型地址的值,* (p+n)代表数组从首地址偏移n个整型地址的值;所以可以用指针去访问数组的值;
可以看到(p+1)的值在p的值上增加了4,即偏移了一个整型地址大小;
注意若只移动一个整型地址大小,写法为*(p+1); 而* p+1代表首地址的值加1;
指针数组和数组指针
指针数组(:存放指针的数组,指针数组存放的是地址)
形如:int *p[5];
[]的优先级高于*,所以每个元素的为整型指针变量 int *
这样;
如下初始化一个指针数组并打印数组的值:
int a = 1, b = 3, c = 5, d = 7, e = 9;
int *p[5] = {&a, &b, &c, &d, &e};
int i;
for(i = 0;i<5;i++)
{
printf("a-e number is %d\n", *p[i]);
}
system("pause");
return 0;
在例如定义指针数组存放5个字符串:
char *p[5] = {"abc", "def", "ghi", "jkl", "mno"}; //初始化指针数组存放5个字符串的地址
int i;
for(i = 0;i < 5;i++)
{
printf("a-e string is %s\n", p[i]); //打印指针数组,这里因为上面存放的是字符串的地址所以不用加*取值
}
system("pause");
return 0;
数组指针(指向数组类型的指针,通过指针引用数组)
形如:int (*p2)[5];
代表一个指向5个元素数组的指针;
如下定义一个数组指针:
int temp[5] = {1, 2, 3, 4, 5};
int (*p)[5] = &temp;
int i;
for(i = 0;i < 5;i++)
{
printf("*(*p+i) is %d\n", *(*p+i));
printf("(*p)[i] is %d\n", (*p)[i]);
}
结果如下,可以看到*(*p+i)和(*p)[i]是等价的:
指针和二维数组
定义如下4行5列二维数组:
整型二维数组存储形式和存储地址如下:
int array[4][5] = {0};
int i, j, k = 0;
for(i=0;i<4;i++)
{
for(j=0;j<5;j++)
{
array[i][j] = k++;
}
}
printf("array is %d\n", array); //array代表第0行第0列元素的地址
printf("&array[0][0] is %d\n", &array[0][0]); //打印第0行第0列元素的地址
printf("*array is %d\n", *(array+0)+1);
printf("&array[0][1] is %d\n", &array[0][1]);
printf("*(array+1) is %d\n",*(array+1)); //打印第二行第一列的地址
printf("&array[1][0] is %d\n", &array[1][0]); //打印第二行第一列的地址
system("pause");
return 0;
结果如下:
看出:*(array+0)+1 == &array[0][1];
故: *(array+m)+n == &array[m][n] 为m+1行n+1列的地址;
*(array+1) == &array[1][0];
*(array+m) == &array[m][0];为第m+1行第一列的地址;
若要得到上述元素的值再进行取值操作即可如: * ( *(array+m)+n) 和 *( *(array+m))
为取第m+1行第n+1列的元素的值;
数组指针和二维数组
定义如下二维数组和数组指针:
int array[4][5] = {0};
int i, j, k = 0;
for(i=0;i<4;i++)
{
for(j=0;j<5;j++)
{
array[i][j] = k++;
}
}
int (*p)[5] = array; //数组指针初始化
printf("**(p+1) is %d\n", *(*(p+1)+0));
printf("**(array+1) is %d\n", *(*(array+1)+0));
printf("array[1][0] is %d\n", array[1][0]);
system("pause");
return 0;
结果如下:
三种方式的访问结果完全相同;
void指针
void指针被称之为通用指针,可以指向任意类型的数据,也就是说,任何类型的指针都可以赋值给void指针,可以进行不同类型指针的转换;
void指针使用:
int num = 128;
int *p = # //初始化整型指针
char *ps = "giao"; //初始化字符型指针
void *pv; //定义void指针
pv = p; //将int型指针赋给void指针
printf("p arress is: %p, pv address is: %p\n", p, pv);
//printf("*pv is %d\n", *pv);没有进行类型转换的错误写法
printf("*pv is %d\n", *(int *)pv); //类型转换成整型指针
pv = ps; //将char型指针赋给void指针
printf("ps arress is: %p, pv address is: %p\n", ps, pv);
//printf("*pv is %s\n", pv);
printf("*pv is %s\n", (char *)pv); //类型转换成字符型
system("pause");
return 0;
运行成功结果如下:
可以看到int和char型的指针都可以赋值给void指针;但是,注意输出值的时候需要进行类型转换,将void型转换成需要的类型;
NULL指针
不指向任何地址的指针,不清楚将指针初始化为什么地址时,请将它初始化为NULL;
int *p1; //没有初始化的野指针
int *p2 = NULL;
printf("*p1 value is %d\n", *p1); //打印p1所指向的值
//printf("*p2 value is %d\n", *p2); //打印p2所指向的值,因为是空指针所以使用取值会报错
printf("p2 value is %d\n", p2); //打印p2的值
system("pause");
return 0;
结果如下:
看到野指针随机指向一个值,而NULL指针为空地址;
指向指针的指针
套娃:
定义一个指向指针*p的指针 *pp
int num = 123;
int *p = # //指针*p指向num,p的值存放num的地址
int **pp = &p; //指针**pp指向*p,*pp的值存放*p的地址
printf("num address is %d\n", &num);
printf("p adress is %d\n", p);
printf("*pp is %d\n", *pp);
printf("&p is %d\n", &p);
printf("pp address is %d\n", pp);
printf("num is %d\n", num);
printf("*p is %d\n", *p);
printf("**pp is %d\n", **pp);
system("pause");
return 0;
结果如下:
看出pp的地址 *pp 和 * p的地址p和num的地址&num是相同的地址;
故pp的值和 * p的值和num的值是相同的;
常量与指针
《1》指向常量的指针
1.指针可以修改为指向不同的常量;
2.指针可以修改为指向不同的变量;
3.可以通过解引用来读取指针指向的数据;
4.不可以通过解引用修改指针指向的数据;
《2》指向非常量的常量指针:
指针自身不可以被修改;
指针指向的值可以被修改;
《3》指向常量的常量指针:
指针自身不可以被修改;
指针指向的值也不可以被修改;
int num = 520;
const int nnum = 123;
const int cnum = 880;
const int *const pv = &nnum; //指向常量的常量指针
int *const p = # //指向非常量的常量指针
const int *pc = &cnum; //指向常量的指针
const int *const *pvv = &pv; //指向常量的常量指针的指针
printf("cnum is %d, cnum address is %p\n", cnum, &cnum);
printf("*pc is %d, address pc is %p\n", *pc, pc);
printf("*p is %d, address p is %p\n", *p, p);
//*pv = 111; //试图修改指向的值会报错
*p = 1024; //指向的值可以修改但自身不能修改
//p = &cnum; //自身不能被修改,加上这句会报错
//*pc = 1024; //*pc和const num一样不可被修改,加上这句会报错
pc = # //修改指针指向变量num的地址
//pv = # //试图修改自身的值也会报错
printf("*pc is %d, address pc is %p\n", *pc, pc);
printf("*p is %d, address p is %p\n", *pc, pc);
num = 1000;
printf("*pc is %d, address pc is %p\n", *pc, pc);
结果:
参数与指针
传值和传址:
例如:
void swap(int x,int y);
void swap1(int *x,int *y);
int main()
{
int x = 2, y = 3;
swap(x, y);
printf("互换后的x: %d, y: %d\n", x, y);
swap1(&x,&y);
printf("互换后的x: %d, y: %d\n", x, y);
system("pause");
return 0;
}
void swap(int x,int y)
{
int tmp;
tmp = x;
x = y;
y = tmp;
}
void swap1(int *x,int *y)
{
int tmp;
tmp = *x;
*x = *y;
*y = tmp;
}
swap函数使用后,因为变量的作用域原因x,y并不能交换;
swap1使用后,因为指针存储的是变量的地址,故可以交换;
可变参数传入:
int sum(int n, ...);
int sum(int n, ...) //...表示参数个数不确定
{
int i, sum = 0;
va_list vap;
va_start(vap, n); //传入
for(i=0;i<n;i++)
{
sum+=va_arg(vap, int); //获取每一个参数的值,int代表参数类型
}
va_end(vap); //关闭参数列表
return sum;
}
int main()
{
int res;
res = sum(3,1,5,4); //3代表三个参数,后面为传入的值
printf("res is %d\n", res);
system("pause");
return 0;
}