C语言日记 25 指针与数组

本文详细介绍了C++中数组与指针的基本操作方法,包括使用下标法、地址法和指针变量来访问一维及二维数组元素,并通过实例演示了如何利用指针进行字符串操作。

 例6-3

假设有一个整型数组a,有10个元素,输出数组中的全部元素。

要输出各元素的值有以下三种方法:

(1)下标法。

#include<iostream>
using namespace std;
int main()
{
	int a[10];
	int i;
	for (i = 0; i < 10; i++)cin >> a[i]; cout << endl;
	for (i = 0; i < 10; i++)cout << a[i] << ""; cout << endl;
	return 0;
}

(2)地址法。

将上面程序中的“a[i]”改为“*(a+i)”,运行情况与(1)相同。

#include<iostream>
using namespace std;
int main()
{
	int a[10];
	int i;
	for (i = 0; i < 10; i++)cin >> * (a + i); cout << endl;
	for (i = 0; i < 10; i++)cout << *(a + i) << ""; cout << endl;
	return 0;
}

 或者下表和地址混用也可以:

#include<iostream>
using namespace std;
int main()
{
	int a[10];
	int i;
	for (i = 0; i < 10; i++)cin >> *(a + i); cout << endl;
	for (i = 0; i < 10; i++)cout << a[i] << " "; cout << endl;
	return 0;
}

(3)用指针变量指向数组元素。

#include<iostream>
using namespace std;
int main()
{
	int a[10];
	int i, * p = a;
	for (i = 0; i < 10; i++)
		cin >> *(p + i); 
	cout << endl;
	for (p = a; p < a + 10; p++)
		cout << *p;
	cout << endl;
	return 0;
}

对于书上P95的

int a[10],*p=a;//指针变量p的初值为&a[0]

cout<<*(p+10);//要输出a[10]的值

补充为完整程序,试看结果:

#include<iostream>
using namespace std;
int main()
{
	int a[10] = {1,2,3,4,5,6,7,8,9,0}, * p = a;
	cout << *(p + 10);
}

结果:

 (估计)输出的应该就是一个系统自动分配的随机值

例6-4用指针访问二维数组。

(1)用一级指针变量输出二维数组值:

源程序:

#include<iostream>
using namespace std;
int main()
{
	int a[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
	int* pl, i, j;
	pl = a[0];
	cout << "输出单个元素:\n";
	cout << "p1[8]=" << pl[8] << "	a[2][2] = " << a[2][2] << endl;
	cout << "用一级指针变量输出二维数组值:\n";
	for (i = 0; i < 15; i++)
		cout << pl[i] << "   ";
}

结果:

虽然运行成功,但是在这个程序里面我们有大量的(之前不理解的)变形可以做以及他多不懂的地方需要解释:

变形一:

#include<iostream>
using namespace std;
int main()
{
    int a[3][6] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15 } };
    int* pl, i, j;
    pl = &a[0][0];
    cout << "输出单个元素:\n";
    cout << "p1[8]=" << pl[8] << "    a[2][2] = " << a[2][2] << endl;
    cout << "用一级指针变量输出二维数组值:\n";
    for (i = 0; i < 18; i++)
        cout << *(pl + i) << pl[i] << "   "; cout << endl;
}

#include<iostream>
using namespace std;
int main()
{
	int a[3][6] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15 } };
	int* pl, i, j;
	pl = a[0];
	cout << "输出单个元素:\n";
	cout << "p1[8]=" << pl[8] << "	a[2][2] = " << a[2][2] << endl;
	cout << "用一级指针变量输出二维数组值:\n";
	for (i = 0; i < 18; i++)
		cout << *(pl + i) << pl[i] << "   "; cout << endl;
	for (int i = 0; i < 18; i++) cout << *pl++ << ' '; cout << endl;

}

结果:

从这个变形中我们可以看出(得到):

1.从把程序段

    int a[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };

改为

    int a[3][6] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15 } };

结果是“数组中每行末尾的元素输出的值全部都为0”中,我们可以看出:(当然我们之前也已经提到过)

实参如果没有赋值的话,输出时自动默认赋值为零。

2.这里的

    pl = a[0];

 等价于

    pl = &a[0][0];

也就是说:

这里的“a[0]”不是代表数组的首元素a[0][0]的值

而是一个传达地址的指针:

a[0]是一个指向a[0][0]元素的地址的指针(只不过我们没有说他是一个变量)

所以在我们把"a[0]"改为a[0][0]时,在前面还必须在加上一个“&”

表示指向元素的地址而不是指向这个元素的值本身

但是在这里我们(也)要注意:

虽然书上写了“ a[0]等价于&a0[0]”,但是在程序里面,我们切记:千万不要这样写,写了程序只会报错。

并且,这里我们的传址过程不同于前面例6-3:(3)用指针变量指向数组元素当中那样(一个一个赋值过去)

而是:

先把第0行的首地址(第0行第0列元素的地址,实际上即a[0][0]的地址)a传到指针变量pl当中

由于数组在电脑中存储的形式其实就是一列,

所以后面(如果我们想要读取(pl+1),(pl+2),(pl+3)......)

直接在cout语句当中输入该指令即可

其中后面输出语句中的“*(pl + i)”就是这个意思

顺其自然从这里的,我们自然就引出了下面要讲的“ *(pl + i)和pl[i] ”:

3.在这里的结果我们不难发现:

我们这里输入的“ *(pl + i)和pl[i] ”最后在每轮循环中的输出都一样

也就是说:*(pl + i)和pl[i] 等价

或者我更简单直白的说,就是编书的那几个糟老头子坏得很,就喜欢装b把自己的智慧博学凌驾于“我不告诉你但是我知道”的垄断式学术优越感之上

所以,我们可以说:

不仅数组名作为首地址可以直接用下标法(a[i])和地址法(*(a+i))

传址过后的指针变量也一样可以用下标法(p[i])和地址法(*(p+i))

而为什么我不说*pl++跟他们一样呢?这是因为

 cout << *pl++ << ' ';

放在最后,实际上如果把他们仨放在一起循环一定会打乱顺序导致整个循环混乱:

#include<iostream>
using namespace std;
int main()
{
	int a[3][6] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15 } };
	int* pl, i, j;
	pl = a[0];
	cout << "输出单个元素:\n";
	cout << "p1[8]=" << pl[8] << "	a[2][2] = " << a[2][2] << endl;
	cout << "用一级指针变量输出二维数组值:\n";
	for (i = 0; i < 18; i++)
		cout << *(pl + i) << pl[i] << *pl++ << "   "; cout << endl;
}

结果:

#include<iostream>
using namespace std;
int main()
{
	int a[3][6] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15 } };
	int* pl, i, j;
	pl = &a[0][0];
	cout << "输出单个元素:\n";
	cout << "p1[8]=" << pl[8] << "	a[2][2] = " << a[2][2] << endl;
	cout << "用一级指针变量输出二维数组值:\n";
	for (int i = 0; i < 18; i++) cout << *pl++ << ' '; cout << endl;
	for (i = 0; i < 18; i++)
		cout << *(pl + i)<< pl[i] << "   "; cout << endl;
}

结果更是:

 为什么会这样?

???

当然了,这里也同时说明了:(*pl++)等价于*(pl++)

(2)用二级指针输出二维数组值

#include<iostream>
using namespace std;
int main()
{
    int a[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
    int i, j;
    int **p;
    p = a;
    for (i = 0; i < 3; i++)
        for (j = 0; j < 5; j++)
            cout << *(*(p + i) + j) << endl;
}

结果:

据邹旺同学所说,以前原来老版本的VS这样写是可以运行的,而且不会报错,不知道搞得是什么原因,现在语法上要求不允许这样写了

而实际上这个程序也就等价于:

#include<iostream>
using namespace std;
int main()
{
	int a[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
	int i, j;
	int (*p)[5];
	p = a;
	for (i = 0; i < 3; i++)
		for (j = 0; j < 5; j++)
			cout << *(*(p + i) + j) << "   ";
}

 结果:

对二级指针以及二维数组的理解,详情见:

C语言日记 25(2) 补充:二维数组与指针理解具体过程详解

另一方面,如果我们把程序末尾的输出语句改为:

#include<iostream>
using namespace std;
int main()
{
	int a[3][5] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
	int i, j;
	int(*p)[5];
	p = a;
	for (i = 0; i < 3; i++)
		for (j = 0; j < 5; j++)
			cout << p [i][j] << "   ";
}

结果:

例6-5 利用指针访问字符串。

源程序:

#include<iostream>
using namespace std;
void main()
{
	char *str = "A string";
	cout << str;
} 

结果:

同样的,在有些编译器上可以运行,有些不行

如果一定要这样写出来,可以写成

#include<iostream>
using namespace std;
int main()
{
	char s[] = "A string";
	char *str = s;
	cout << str << endl;
}

或者

#include<iostream>
using namespace std;
int main()
{
	char s[] = "A string";
	char* str = &s[0];
	cout << str << endl;
}

 结果:

例6-6将字符串b复制到字符串a中。

源程序:

#include<iostream>
using namespace std;
void copy_string(char*, char*);
void main()
{
	char a[20], b[20];
	cout << "输入字符串a";
	cin >> a;
	cout << "输入字符串b";
	cin >> b;
	copy_string(a, b);
	cout << "复制后的字符串 a为:" << a << endl;
}
void copy_string(char* to, char* from)
{
	for (; *from != '\0'; from++, to++)
		*to = *from;
	*to = '\0';
}

最后这句“    *to = '\0';”不能删去。

否则结果如下:
 

据邹旺同学所说:

因为他程序后面没有值了,而他这个又不是一个字符而是一个字符串不会自动赋空字符“\0”,所以他只会发疯随机乱输出任意值,而从本例(这一例题)中,我们还是非常容易看出来:

字符和字符串还是不一样的:

字符形式末尾后面自动会赋空字符“\0”。

而字符串形式则不会。

如果(我们)一定要删去*to = '\0';”,那就必须先把这个数组改为全局变量:(那就可以省略最后一句“    *to = '\0';”)

#include<iostream>
using namespace std;
char a[20], b[20];
void copy_string(char*, char*);
void main()
{
	cout << "输入字符串a";
	cin >> a;
	cout << "输入字符串b";
	cin >> b;
	copy_string(a, b);
	cout << "复制后的字符串 a为:" << a << endl;
}
void copy_string(char* to, char* from)
{
	for (; *from != '\0'; from++, to++)
		*to = *from;
}

书P99:

#include<iostream>
using namespace std;
int main()
{
	char* name[3] = ("java", "basic", "c++");
}

同样的:
据邹旺同学所说,以前原来老版本的VS这样写是可以运行的,而且不会报错,不知道搞得是什么原因,现在语法上要求不允许这样写了

实际上想表达的意思:

#include<iostream>
using namespace std;
int main()
{
	char name[3][6] = { "java", "basic", "c++" };
	//至少为6,不然后面“basic”部分存不下就会开始报错
}

而这里实际上自然而然地(仿照前面的例子)我们很容易自然的想到把程序改为:

#include<iostream>
using namespace std;
int main()
{
	char (*name)[6] = { "java", "basic", "c++" };
}

(可是)结果:

 这又是怎么回事(这又是为什么呢)?

???

那接下来我们来看我们如何把这些字符输出来:

如果只要输出第一排的这第一组“java”字符:

#include<iostream>
using namespace std;
int main()
{
	char name[6] = "java";
	//至少为6,不然后面“basic”部分存不下就会开始报错
	char* n = &name[0];
	while (*n != '\0')
	{
		cout << *n;
		n++;
	}
}

亦等价于:

#include<iostream>
using namespace std;
int main()
{
	char name[6] =  "java";
	//至少为6,不然后面“basic”部分存不下就会开始报错
	char* n = &name[0];
	for (; *n != '\0'; n++) {
		cout << *n;
	}
}

 结果:

如果想把三组字符全部都输出:

#include<iostream>
using namespace std;
int main()
{
	char name[][6] = { "java", "basic", "c++" };
	for (int i = 0; i < 3; i++) cout << name[i] << endl;
	cout << endl;
	//法一
	for (int i = 0; i < 3; i++) cout << *(name + i) << endl;
	cout << endl;
	//法二
	char(*p)[6] = name;
	for (int i = 0; i < 3; i++) cout << p[i] << ' ';
	cout << endl << endl;
	//法三
	int i = 0;
	while (i++ < 3) 
		cout << *p++ << endl;
	//法四
}

 结果:

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值