文章目录
8.3 数组的指针和指向数组的指针变量
8.3.1 指向数组元素的指针变量的定义和赋值
eg:
int a[5]; // 在内存中是连续存储的,能引用的是 a[0] - a[4]
//数组指针:是指数组的开始地址,数组元素的指针就是数组元素的地址。
a[3] = 5; //下标法引用数组元素,也可以用指针引用数组元素。
数组名就是数组起始地址,也就是a[0]的地址 → a == &a[0]
int a[5]; //只要是数组,那么数组元素的内存一定是挨着的;
a[0] = 5; a[1] = 6; a[2]= 8; a[3] = 9;
a == &a[0]
int main() {
int a[5];
int *p; //指针变量,整形指针变量,和数组a类型相同
p = &a[0]; // 把a的首地址赋值给指针变量p。即p指向数组a。
p = a;
}
定义指针时也可以给指针变量赋予初值:
int *p = &a[0]; //定义且赋初值才能这样做
int *p;
p = &a[0];
int *p = a;
8.3.2 通过指针引用数组元素
int a[5];
a[0] = 1; a[1]=2,a[2]=3,a[3]=4,a[4]=5;
int *p = &a[0];
int *p = a;
int *p;
p = &a[0];
例如:
a)
*p = 19; //对当前p所指向的数组元素赋值为19. => a[0] = 19;
b)
p = p + 1; // 是将这个指针变量所指向的地址按照类型再加1,
c)
i 指向下标 0-4
p+i; 或者 i + p;
假如p指向数组首地址,a是数组名。那么p + i 或 i +p就是数组元素a[i]的地址,
也就是说他们指向了数组a的第i个元素。
也就是说 p + 3 ,和 a + 3的值就是 &a[3] , 也就是都指向 a[3] 。
如果是整形 ——> &a + i*4 就是 p+3或a + 3的地址。
p = a;
结论:
p + i 或者 i + p 都是地址,既然是地址,就可以赋给指针变量。
int a[5]; int *p;
a[0] = 1; a[1]=2,a[2]=3,a[3]=4,a[4]=5;
p = a;
*p = 19; // *p = 19 --> &a[0] = 19;
p = a+3; // a+3 --> a + i * 4(int型*4); --> &a[3]
*p = 13; // *p = 13; --> 和 a[3] = 13;
d)
*(p + i ) 或者 * (a + i)
通过c知道d,这两个是 p + i 或 a + i所指向的数组元素,也就是 a[i]
*(p+2) = 14; <--> a[2] = 14;
*(a+4) = 20; <--> a[4] = 20;
*(p+3) = 10; <--> a[3] = 10;
e)
i 为数组元素下标。
p[i]
p[i] 与 *(p + i) 等价, 也就是跟 a[i]等价
当a[5]中的元素出现在等号左边的时候就是变量,在右边的时候代表其元素值。
p[3] = 155; <==> a[3] = 155
说明:
引用数组元素的方式很多。 例如:
-
a[i]
-
p[i]
-
*(p + i)
-
*(a+i)
int a[5]; //能够引用的下标是 0-4
int *p;
int i;
a[0] = 1; a[1]=2,a[2]=3,a[3]=4,a[4]=5;
printf("1.------------\n");
//带入了一个i,还要算地址。速度自然慢
for(i=0;i<5;i++){
printf("%d\n",a[i]);
}
printf("2.-----------\n");
for(i=0;i<5;i++) {
printf("%d\n",*(a+i));
}
printf("3.------------\n");
//效率最高的,它是直接操作内存的。
for(p=a;p<(a+5);p++){ // p 得到的是a的地址, a有4个地址,所以可以进行循环。
//然后得到了4次指针变量。
printf("%d\n",*p); // 用*p来得到其变量值。
}
注意事项:
1. 数组首地址(数组名)++是不行的,指针变量++可以,
2. 第三个for循环是直接操作内存的且速度最快,p++循环指向到&a[5]这个地址了,指向是可以的,但是却不代表你能够使用。你不能用它。非法使用。
f)
**P++ —>优先级相同,都是从右到左的结合。所以是 (P++)
作用:
p++是先用后加,所以(p++)整个作用是: 得到p指向的变量的值(P),然后再使p指针自加1,指向下一个数组元素。
int a[5];
int *p;
a[0] = 0,a[1]=1,a[2] = 2,a[3]= 3,a[4] = 4;
p = a;
// printf("%d",*p++); //先用后加,*p++ <==> a[0] = 0 打印完之后*P++ 指向a[1]
g)
++P 相当于(++p) . 先加后用。
int a[5];
int *p;
a[0] = 0,a[1]=1,a[2] = 2,a[3]= 3,a[4] = 4;
p = a;
printf("%d\n",*++p); **//先加后用,*++p <==> a[1] = 1; 打印完之后*++P指向了a[1],**
先加后用,++p <==> a[1] = 1; 打印完之后++P指向了a[1],
h)
(*p)++ ; 与 (*p)++; 表示p所指向的元素加1,如果p指向数组首地址,就等价于a[0]++ .
实际上就是数组元素值+1,不是指针+1;
(*p)++ ; //相当于a[0]++ ; -> a[0] =0
p++ ; // a[1]++; -> a[1] = 2;
*p++; //相当于a[1]++ ; a[1] = 2;
8.3.3 数组名作为函数参数
数组名代表数组首地址。
如果一个实参的数组,想在函数中改变此数组的元素的值,实参和形参的对应关系可以有4种。
(1)
void changevalue(int ba[]){
ba[3] = 25;
ba[4] = 42;
return;
}
int main(){
int a[5]; //引用的下标为0-4;
a[0]=0;a[1]=1;a[2]=2;a[3]=3;a[4]=4;
printf("a[0]=%d,a[1]=%d,a[2]=%d,a[3]=%d,a[4]=%d\n",a[0],a[1],a[2],a[3],a[4]);
changevalue(a);
printf("a[0]=%d,a[1]=%d,a[2]=%d,a[3]=%d,a[4]=%d\n",a[0],a[1],a[2],a[3],a[4]); // //使用自定义函数之后
总结: a和ba共用这一段内存,且指向同一个数组。
a是实参数组首地址,ba是形参数组首地址,a和ba共用一段内存。也就是说,在调用changevalue期间,a和ba指的是同一个数组。
(2)
实参用数组名,形参用指针变量。
void changevalue(int *p){
*(p+2) = 888; //等价于给a[2]赋值。
//这里要注意:一定要知道数组a的数组元素范围。在此处只能引用到 *(p+4)
}
int main(){
int a[5];
a[0]=0;a[1]=1;a[2]=2;a[3]=3;a[4]=4;
printf("a[0]=%d,a[1]=%d,a[2]=%d,a[3]=%d,a[4]=%d\n",a[0],a[1],a[2],a[3],a[4]); //未使用自定义函数之前
changevalue(a);
printf("a[0]=%d,a[1]=%d,a[2]=%d,a[3]=%d,a[4]=%d\n",a[0],a[1],a[2],a[3],a[4]); //使用自定义函数之后
}
(3)
实参和形参都用指针变量,这个和(2)很相似
void changevalue(int *p){
*(p+2) = 888; //等价于给a[2]赋值。
//这里要注意:一定要知道数组a的数组元素范围。在此处只能引用到 *(p+4)
}
int main(){
int a[5];
a[0]=0;a[1]=1;a[2]=2;a[3]=3;a[4]=4;
int *pa = a; //把数组a的首地址给pa,传递pa过去
printf("a[0]=%d,a[1]=%d,a[2]=%d,a[3]=%d,a[4]=%d\n",a[0],a[1],a[2],a[3],a[4]);
changevalue(pa); //实参是指针变量,则该指针变量必须有确定的值;
printf("a[0]=%d,a[1]=%d,a[2]=%d,a[3]=%d,a[4]=%d\n",a[0],a[1],a[2],a[3],a[4]); // //使用自定义函数之后
}
(4)
实参为指针,形参为数组名
把指针传递给数组名,那这个数组名就相当于这个数组的首地址。
换个理解: 把形参数组名当成指针看待,是一个指向数组a的首地址的指针。
void changevalue(int ba[]){
//注意,ba引用数组下标时,也不能超过main中定义的a数组。
ba[3] = 24;
ba[4] = 34;
return;
}
int main(){
int a[5];
a[0]=0;a[1]=1;a[2]=2;a[3]=3;a[4]=4;
int *pa = a; //把数组a的首地址给pa,传递pa过去
printf("a[0]=%d,a[1]=%d,a[2]=%d,a[3]=%d,a[4]=%d\n",a[0],a[1],a[2],a[3],a[4]);
changevalue(pa); //实参是指针变量,则该指针变量必须有确定的值;
printf("a[0]=%d,a[1]=%d,a[2]=%d,a[3]=%d,a[4]=%d\n",a[0],a[1],a[2],a[3],a[4]); // //使用自定义函数之后
}
**实参或形参是数组名还是指针都可以,**间接的通过一个函数改变数组a的内容,也就是值。
8.3.4 回顾二维数组和多维数组的概念
例如:
int a[3][4] ;
//第一维 0-2 , 第二维0-3
多维数组
eg:
int a[2][3][4];
// 第一维0-1,第二维0-2, 第三维0-3;
a[0][0][0]; a[0][0][1]; a[0][0][2]; a[0][0][3];
a[0][1][0]; a[0][1][1]; a[0][1][2]; a[0][1][3];
a[0][2][0]; a[0][2][1]; a[0][2][2]; a[0][2][3];
a[1][0][0]; a[1][0][1]; a[1][0][2]; a[1][0][3];
a[1][1][0]; a[1][1][1]; a[1][1][2]; a[1][1][3];
a[1][2][0]; a[1][2][1]; a[1][2][2]; a[1][2][3];
8.3.5 指向多维数组的指针和指针变量探究
探究地址是从1000开始的。
例如:
a[3]
a[0],a[1],a[2];
a[3][4]
a[0][0],a[0][1],a[0][2],a[0][3]
a[1][0],a[1][1],a[1][2],a[1][3]
a[2][0],a[2][1],a[2][2],a[2][3]
a[3][4]
以下:
a[3] //这个是行
a[0]
a[1]
a[2]
a[4] //这个是列
a[0] a[1] a[2] a[3]
则有:
a[0]a[0],a[0]a[1],a[0]a[2],a[0]a[3]
a[1]
a[2]
则有: (先写行的a[3] ,在直接填列的a[4] )
a[0][0] a[0][1] a[0][2] a[0][3]
a[1]
a[2]
可以理解为有3页,每一页有4个元素
二维数组名,也是整个二维数组的首地址。我们可以认为第0行的首地址是1000.
int a[3][4]; //数组名a同样代表数组的首地址
int a[3][4];
int *p;
// p = a; //这个不行,
p = (int *)a; //硬指向
- a+1, a+2 分别代表 a[1] ,a[2]的地址 ,也就是第一行和第二行的首地址。
所以a+1跳过16个字节的,也就是a = 1000, a + 1 = 1016, a[2] = 1032;
int a[3][4]
int *p; // a 1000
p = (int *)(a+1); // a+1 1016
p = (int *)(a+2); // a+2 1032
a[3][4] 代表的是3行4列。
- 行就代表的是数组名,
c语言规定数组名代表数组的首地址,
a[0] => &a[0][0] // 首地址为1000, 是第0行元素首地址。
a[1] => &a[1][0] // 首地址为1016, 是第1行元素首地址。 int为4个字节,列为4列,则第1行首地址为 第0行地址 +(4*4)
a[2] => &a[2][0] // 首地址为1032, 是第2行元素首地址。 int为4个字节,列为4列,则第2行首地址为 第1行地址 + (4*4)
int a[3][4];
int *p;
p=a[0];
p = &a[0][0]; //地址为 0x061fde0
p = a[1]; //地址为 0x061fdf0
fdd0
fde4
- 第0行第一列元素的地址表示:
&a[0][1];
a[0] + 1 表示跳过一个元素. 1004
a[0] + 2 表示跳过两个元素. 1008
*a + 1 也是表示跳过一个元素, 1004
p = &a[0][1]; // 1004为地址
p = a[0] + 1; // 1004为地址
- 二维数组推论 (拿到的都是其地址)
a[0] == *a
&a[0][1] == a[0] + 1 == *a + 1
a[1] == *(a+1)
&a[1][2] == a[1] + 2 == *(a+1) + 2
//注意: 不要把 *(a+1)+2写成*(a+3),那就变成a[3]了
- a[0] + 1 == *a + 1 == a[0][1] ⇒ 1004
*(a[0] + 1) => 代表a[0][1]的值
*(*a+1)
a[0][1]
*(a[1] + 2) =>代表第一行第二列的值
*(*(a+1) +2 ) =>也是
*(&a[1][2]) => 也是
a[i] 的性质问题: 如果a是一维数组,
a[i] 和 &a[i] 地址值是一样的。
a[i][j] ⇒
实践
int a[3][4];
int i,j;
for(i=0;i<3;i++) {
for(j=0;j<4;j++)
{
a[i][j] = 86;
}
}
int *p;
p = (int *)(a+1); //第一行首地址;
*p = 56;
p++; //走4个字节;
*p = 78; //相当于 a[1][1] = 78; //再次赋值78给到p,此时证明p++之后确实为78;
8.3.6 指针数组和数组指针
int *p[10]; //指针数组,能引用的下标是0-9*
理解:
这是个数组,数组中有10个元素,每个元素都是指针,所以相当于定义了10个指针变量,也即是p[0] - p[9] ;
定义了10个指针变量,都可以指向某一个地址 。
int *p[4]; //能引用的下标是 0-3
int i,j;
int a[3][4];
for(i=0;i<3;i++)
{
for(j=0;j<4;j++)
{a[i][j] = 87;}
}
a[0][1] = 98;
p[0] = &a[0][0];
p[1] = &a[0][1];
p[2] = &a[0][2];
p[3] = &a[0][3];
for(j = 0;j<4;j++)
{
printf("%d\n",*p[j]);
数组指针
数组指针: 用的不多,
int (*p)[10]; //加了括号之后就是数组指针,这个
//指针变量用来指向 含有10个元素的一维数组。
此处就是定义了一个数组有10个元素,然后将这个数组指针指向第0个数组元素
也就是指向这个数组的首地址,然后在一个一个的对应。
例如:
int (*p)[10];
int a[10];
int i,j;
for(i=0;i<10;i++)
{
a[i] = i;
}
p = &a; //得用地址符号。 其实&a和a的值应该相同。
//例如: int *p; p=a; 这样就可以,
//这里应该是为了区分数组指针和普通指针。
int *q;
q = (int *)p; //指向指针的指针变量q,
//这里使用的是强制转换,原因猜测是:
// 我们定义的是一个数组指针,而这个数组指针用来指向含有10个元素的
// 一维数组。
//那q到底指向哪个呢? q也不确定,但是我们人知道这个这样做是对的,
// 也可以是这样 q=(*p); 这个整体就是数组首地址。但是这样又跟前面
// *p单独成行=元素值 冲突,所以为了方便就使用强制转换。
// 测试 q = &(*p)[10]; 这样是不行的,指针变量特殊一点,这样写不行
// 本来就是个指针,这个指针有10个元素,所以不能再用&
for(i=0;i<10;i++)
{
printf("%d\n",*q);
q++;
}
int i,j;
int (*p)[10];
int a[3][10];
for(i=0;i<3;i++)
{
for(j=0;j<10;j++)
{
a[i][j] = i+j;
}
}
p = a; //二维数组名可以直接赋值给数组指针。
int *q;
q = (int *)p;
for(i=0;i<3;i++)
{
for(j=0;j<10;j++)
{
printf("%d ",*q);
q++;
}
printf("\n----------");
**p++;
q=(int *)p;**
}
int i,j;
int (*p)[10];
int a[3][10];
for(i=0;i<3;i++)
{
for(j=0;j<10;j++)
{
a[i][j] = i+j;
}
}
p = a; //二维数组名可以直接赋值给数组指针。
int *q;
q = (int *)p;
for(i=0;i<3;i++)
{
**q = *(p+i); //每次指向一个新行**
for(j=0;j<10;j++)
{
printf("%d ",*q);
q++;
}
printf("\n----------");
}
int i,j;
int (*p)[10];
int a[3][10];
for(i=0;i<3;i++)
{
for(j=0;j<10;j++)
{
a[i][j] = i+j;
}
}
p = a; //二维数组名可以直接赋值给数组指针。
int *q;
q = (int *)p;
for(i=0;i<3;i++)
{
for(j=0;j<10;j++)
{
**printf("%d ",*((*p+i)+j));**
}
printf("\n----------");
}
多维数组指针做函数参数
int (*p)[3][4][5];
8.3.7 多维数组的指针做函数参数
8.4 字符串的指针和指向字符串的指针变量
8.4.1字符串表示形式
一: 字符串表示形式
char mystr1[] = "I love you "; //直接拷贝
char mystr2[] = "I love you ";
//虽然这两个打印的东西是一样的,但是他们两个变量的地址却是不同的。
printf("%s\n",mystr);
char *pmystr1 = "I love you"; //不是拷贝,本行相当于把内存中这个字符串常量
//的首地址赋给了指针变量pmystr1;
char *pmystr2 = "I love you"; //把内存中这个字符串常量
//的首地址赋给了指针变量pmystr2;
//c语言中对字符串常量 有特殊的处理,会在内存中开辟出一段类似字符数组
//的东西来存放字符串常量。 所以 “I love you” 是存在于内存中,并且有一个
//内存地址的。
//这个常量定义完了只能读,不能改。
字符数组是拷贝,字符指针是给到首地址。
// mystr1 == &mystr1[0];
// mystr2 == &mystr2[0];
如何实现对字符串的存取呢?
1.方法
char a[] = "I love china!";
char b[100];
int i ;
for(i=0; *(a+i) != '\0';i++) // *(a+i) 相当于 a[i]
{
*(b+i) = *(a+i); // b[i] = a[i];
}
*(b+i) ='\0'; // b[i] = '\0';
printf("string a is %s\n",a);
printf("string b is %s\n",b);
2.方法
char a[] = "I love china!";
char b[100];
char *p1,*p2;
int i;
p1 = a;
p2 = b;
for(;*p1 != '\0';p1++,p2++)
{
*p2 = *p1;
}
*p2 = '\0';
printf("string a is %s\n",a);
printf("string b is %s\n",b);
return 0;
}
8.4.2 字符串指针做函数参数
用字符数组名做参数
#include <stdio.h>
void copystr(char from[], char to[]) { // 字符数组名做参数,将实参首地址传递给form, to 就和实参指向同一个单元
int i = 0;
while (from[i] != '\0')
{
to[i] = from[i]; //逐个字符拷贝,必须保证to比from大。
i++;
}
to[i] = '\0';
}
int main() {
char a[] = "this is source content";
char b[] = "this is a special test hehe,look carefully";
printf("a=%s\n", a);
printf("b=%s\n", b);
copystr(a, b);
printf("a=%s\n", a);
printf("b=%s\n", b);
}
换个写法:
#include <stdio.h>
void copystr(char* from, char* to) { **// 字符数组名做参数,将实参首地址传递给form, to 就和实参指向同一个单元**
int i = 0;
while (from[i] != '\0')
{
to[i] = from[i]; //逐个字符拷贝,必须保证to比from大。
i++;
}
to[i] = '\0';
}
int main() {
char a[] = "this is source content";
char b[] = "this is a special test hehe,look carefully";
printf("a=%s\n", a);
printf("b=%s\n", b);
copystr(a, b);
printf("a=%s\n", a);
printf("b=%s\n", b);
}
再换
#include <stdio.h>
void copystr(char* from, char* to) { // 字符数组名做参数,将实参首地址传递给form, to 就和实参指向同一个单元
int i = 0;
for (; *from != '\0'; from++, to++)
{
*to = *from; //b[i] = a[i]
}
to[i] ='\0';
}
int main() {
char a[] = "this is source content";
char b[] = "this is a special test hehe,look carefully";
printf("a=%s\n", a);
printf("b=%s\n", b);
copystr(a, b);
printf("a=%s\n", a);
printf("b=%s\n", b);
}
再换:
#include <stdio.h>
void copystr(char* from, char* to) { // 字符数组名做参数,将实参首地址传递给form, to 就和实参指向同一个单元
int i = 0;
while(*from){
*to++ = *from++; //优先级相同。char型占用一个字节,所以就是一个一个走的。
}
*to = '\0';
}
int main() {
char a[] = "this is source content";
char b[] = "this is a special test hehe,look carefully";
printf("a=%s\n", a);
printf("b=%s\n", b);
copystr(a, b);
printf("a=%s\n", a);
printf("b=%s\n", b);
}
8.4.3 字符指针变量与字符数组
-
字符数组是由若各干元素组成,每个元素中存放一个字符;
-
字符指针变量中存放的是字符串的首地址 。仅仅是首地址。
-
赋值方式:
char str[100] = “I love China!”;
//定义时初始化,相当于拷贝字符串内容到str中
//str = “I love china!” 这是错的
想要拷贝只能使用拷贝函数 strcpy
char str[100] = "I love china!"; strcpy(str,"I love China!"); char *a; a = "I love China"; // 字符串常量在内存中是有固定地址的, //这里的赋值只是让字符指针a指向这个地址。
-
指针变量的值可以改变
char *a = "I love china!"; //字符指针变量仅仅存放的是这个常量的首地址。
a = a + 7; //跳过7个字节之后a指向了c , 指针变量值可以改变。
printf("%s\n",a); //打印的值为 china!
- 数组首地址不能变
char a[] = "I love china!";
a = a+7; //这个是错的.