第八章、指针(中)

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; //硬指向

请添加图片描述

  1. 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列。

  1. 行就代表的是数组名,

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

  1. 第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为地址
  1. 二维数组推论 (拿到的都是其地址)
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]了
  1. 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 字符指针变量与字符数组

  1. 字符数组是由若各干元素组成,每个元素中存放一个字符;

  2. 字符指针变量中存放的是字符串的首地址 。仅仅是首地址。

  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指向这个地址。
    
    
  4. 指针变量的值可以改变

char *a = "I love china!"; //字符指针变量仅仅存放的是这个常量的首地址。
a = a + 7; //跳过7个字节之后a指向了c  , 指针变量值可以改变。
printf("%s\n",a);  //打印的值为 china!

  1. 数组首地址不能变
char a[] = "I love china!";
a = a+7;  //这个是错的.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨优秀&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值