指针和数组参在一块确实很让人头疼,特别是对于我这种菜鸟级别的,简直一个晕字了得。
可是再难也得学啊,那就慢慢捋捋吧。。。。。。
一、指针
1. 在计算机内存中,每一个内存单元都有一个地址,在c语言中这个地址就大概可以充当指针。
2. &和*
&是对变量取地址,*是对取变量的值。互为逆运算。
3. 地址的打印;
#include <stdio.h>
int main(void)
{
int a = 3;
int *p = &a;
printf("%p\n",&a);//打印a的地址需用p%
printf("%p\n",p);//此时p = &a,即为打印a的地址需用p%
printf("%p\n",&p);//p本身是指针变量,即为一个地址,&p是对p取地址,也就是对a的地址再取地址
printf("%d\n",*p);//打印a的值;
return 0;
}
//运行结果:
//0xbfe758b8
//0xbfe758b8
//0xbfe758bc
//3
- 指针大小
#include <stdio.h>
int main(void)
{
char *p = "hello world";//指针p中只存放了字符串的首地址;
int *q = NULL;//初始化指针q;
double *p1 = NULL;
//32位操作系统,指针变量的大小恒为4个字节,不论数据类型是什么;
printf("%d,%d,%d\n",sizeof(p),sizeof(q),sizeof(p1));
return 0;
}
//运行结果:
//4,4,4
- 二级指针
只有变量才有地址,常量放在寄存器中,没有地址;
二级指针:地址的地址,常量没有地址。
一级指针和二级指针的比较:
#include<stdio.h>
int fun(char *str)
{
str = "helloworld";
return 0;
}
int main(int argc, const char *argv[])
{
char *str = NULL;
printf("%p\n",str);
fun(str);
printf("%p\n",str);
puts(str);
return 0;
}
==========================================
#include<stdio.h>
int fun(char **str)
{
*str = "helloworld";
return 0;
}
int main(int argc, const char *argv[])
{
char *str = NULL;
printf("%p\n",str);
fun(&str);
printf("%p\n",str);
puts(str);
return 0;
}
二、数组
1. 例如:int a[5] = {1,2,3,4,5};
char b[6] = “hello”;
其中数组名代表数组的首地址,也就是表示这个数组的开始地址。
当数组为字符串的时候,数组最后一位有’\0’结束符。
2. 指针与数组
#include <stdio.h>
int main(void)
{
int a[5] = {1,2,3,4,5};
int *p = a;
printf("%d,%d,%d,%d\n",a[1],*(p+1),*(a+1),p[1]);
//此时,a[1] = *(p+1) = *(a+1) = p[1];
return 0;
}
//结果:
//2,2,2,2
- 二维数组
二维数组:a(行地址指针) —->*a(降级变为列指针)—->**a(再降级变为 元素)
**a—>&(**a)(*a升级为列地址)—–>&*a(a 升级为行地址)
#include <stdio.h>
int main()
{
char ch[3][3] = {1,2,3,4,5,6,7,8,9};
int i,j;
for (i = 0; i < 3; i ++) {
for (j = 0; j < 3; j ++)
printf("%12d",ch[i][j]);
putchar(10);
}
for (i = 0; i < 3; i ++) {
for (j = 0; j < 3; j ++)
printf("%12p",&ch[i][j]);
putchar(10);
}
#if 1
/* 二维数组中ch代表行地址,ch[0]代表列地址;ch[0][0]代表是元素 */
printf("&ch + 1 = %#x\n",&ch + 1); // char(*)[3][3]; // ch代表行地址,取&升级为数组,数组+1,指向整个数组下一个地址 &ch代表整个数组的首地址,而ch代表数组首元素的地址,虽然两者值相等,但意义不一样。
printf("&ch[1][0] = %#x\n",&ch[1][0]); // 取ch[1][0]的地址
printf("ch + 1 = %#x\n",ch + 1); // ch代表行地址,+1,表示行地址加1,换一行;
printf("ch[0] = %#x\n",ch[0]); // ch[0]代表是ch[0][0]的地址
printf("ch[0] + 1= %#x\n",ch[0] + 1); // ch[0]是列地址,+1,表示列地址加1;ch[0][1]的地址
printf("ch[1] + 3 = %#x\n",ch[1] + 3); // ch[1]是列地址;+3,表示列地址加3;ch[1][3];即为ch[2][0]的地址
printf("*(ch[0] + 2) = %#x\n",*(ch[0] + 2)); // ch[0]是列地址;+2,代表列地址+2;ch[0][2]的地址;
printf("*ch[1] = %#x\n",*ch[1]); // ch[1]是列地址;*ch[1],降级为元素;ch[1][0]的元素
printf("&ch[1] + 1 = %#x\n",&ch[1] + 1);// ch + 1 + 1 // ch[1]是列地址;&ch[1],升级为行地址;&ch[1]+1;行地址加1;ch[2][0] 地
址
printf("*(*(ch + 1) + 1) = %#x\n",*(*(ch + 1) + 1)); // (ch+1)行地址加1;*(ch+1),降级为列地址ch[1];(*(ch+1)+1),为ch[1][1]地址;
printf("*(*(&ch[0] + 1) + 1) + 1 = %#x\n", *(*(&ch[0] + 1) + 1) + 1 ); //
printf("ret = %#x\n",*(*(ch + 1) + 2) + *(*(&ch[1] + 1)) + 1 ); //
#endif
return 0;
}
运行结果:
三、指针数组、数组指针
指针数组为一个数组,
char *p[3] = {“hello”,”world”,”hi”};
p[0] = “hello”的地址, p[1] = “world”的地址, p[2] = “hi”的地址。
当用“%s”打印p[0]这个地址指向的值,即为“hello”;当用“%p”打印时,则是打印p[0]的地址。
数组指针(也称行指针)
定义 int (*p)[n];
()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
所以数组指针也称指向一维数组的指针,亦称行指针。
指针数组
定义 int *p[n];
[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
如要将二维数组赋给一指针数组:
int *p[3];
int a[3][4];
for(i=0;i<3;i++)
p[i]=a[i];
这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
所以要分别赋值。
这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
四、函数指针,指针函数
函数指针:实质上是一个指针,指向一个函数。
指针函数:实质上是一个函数,返回值是一个指针。