C语言 指针小记

最近重新拾起C语言,打算过一遍C Primer Plus的课后题,今天在第11章第10题时有点卡壳,犯了几个错误。在此记录一下,顺便梳理下指针的知识。

题目:

编写一个程序,读取输入,直到读入了10个字符串或遇到EOF,由二者中最先被满足的那个终止读取过程。这个程序可以为用户提供一个有5个选项的菜单:输出初始字符串列表、按ASCII顺序输出字符串、按长度递增顺序输出字符串、按字符串中第一个单词的长度输出字符串和退出。菜单可以循环,直到用户输入退出请求。当然,程序要能真正完成菜单中的各项功能。


解答:

初读题目,框架应该是首先读入几个字符串,然后输入一个菜单选项,根据输入的选项,执行相应操作。很自然的想到switch()语句。

先贴出来自己做错的函数:

#include <stdio.h>

int main(void)
{
	char ch;
	int t;
	int  ct1,ct2;
	char *temp;
	int i,j;
	char str[10][81];
	char *p[10];
	int n = 0;
	puts("Input string:");
	while( n<10  && gets(str[n]))
	{
		puts("Input string:");
		n++;
	}
	p = str;
		

	
	puts("1:输出字符串");
	puts("2:按 ASCII 顺序输出");
	puts("3:按长度递增");
	puts("4:按第一个单词的长度");
	puts("0:推出");
	
	while(getchar(ch))
	{
		while(getchar()!='\n')
			continue;
		switch(ch)
	{
	case '1':
			
		break;
	case '2':
		for(i=0;i<n;i++)
			for(j=i+1;j<n;j++)
			{
				if(strcmp(str[i],str[j])>0)
				{
					temp = *str[i];
					*str[i] = *str[j];
					*str[j] = temp;
				}
			}		
		break;
	case '3':
		for(i=0;i<n;i++)
			for(j=i+1;j<n;j++)
			{
				if(strlen(str[i])>strlen(str[j]))
				{
					temp = p[i];
					p[i] = p[j];
					p[j] = temp;
				}
			}
		break;
	case '4':
		for(i=0;i<n;i++)
			for(j=i+1;j<n;j++)
			{
				while(*str[i]!=' ')
					ct1++;
				while(*str[j]!= ' ')
					ct2++;
				if(ct1>ct2)
				{	
					temp = *str[i];
					*str[i] = *str[j];
					*str[j] = temp;
				}
			}
		break;
	case '0':
		puts("Bye!");
		return 0;
	default:
		puts("Please input ");
			break;
	}
	for (i=0;i<n;i++)
		puts(p[i]); 
	}
}
编译无所通过。提示
错误	3	error C2106: “=”: 左操作数必须为左值	d:\program files (x86)\microsoft visual studio 9.0\project\11-10\11-10\10.c	20	11-10

这个错误的原因是对字符串数组以及指针理解不清。错误的认为str和p都是首地址,通过把str赋给p,然p指向str。但这里p是一个包含10个指针的数组,重点是数组。虽然数组的名字和指针一样,都是数组首地址,但是数组名是常量,不可以改变。

改正的方法:p是一个有10个指针的数组,也就是说p[n]是指针,而指针是变量,因此可以这样改:

	while( n<10  && gets(str[n]))
	{
		p[n] = str[n];
		puts("Input string:");
		n++;
	}
再次编译,通过,运行,1没有问题,但是选2的时候仍然是按数序输入。通过调试发现,在switch()语句中,没有任何以个和输入的1相匹配,之后做如下修改:

while(scanf("%d",&c))

编译、运行,结果错误,每个字符串只改变了首个字符。问题出在*str[i]只代表第一个元素的值。做如下修改:

	case 2:
		for(i=0;i<n;i++)
			for(j=i+1;j<n;j++)
			{
				if(strcmp(str[i],str[j])>0)
				{
					temp = p[i];
					p[i] = p[j];
					p[j] = temp;
				}
			}		
		break;


编译、运行,1.2都能得到正确结果,但3仍然按原顺序给出。此处犯的错误依旧是对指针理解不透彻。我们通过
	while( n<10  && gets(str[n]))
	{
		p[n] = str[n];
		puts("Input string:");
		n++;
	}
使数组p中储存的每个指针依次指向str,也就是说p中每个元素储存的都是对应str的地址。错误的认为,改变p中元素的地址,str也相应改变了。这里用错了一个概念,也就是,虽然*p[n]和str[n]指向相同的位置,但是改变p[n],并不能改变*p[n]指向的值,改变的只是p[n]中储存的地址的值。

此处直接改变str[n]的值是不对的,如问题1一样,str是一个二维数组,str[n]是一个一位数组,她是一个常量。

关于这个问题的解决办法,考虑了两个错误的,一个是让*str[n] = *p[n],但问题是,这只改变了首元素的值;第二个是想用strcpy(str[n],p[n]),存在的问题是可能导致数据溢出。所以最简单的方法就是输出p。

经过接近一天的时间,终于把这个函数实现了,虽然程序不完美,但是能运行出结果。过程收获很多。

最终代码:

#include <stdio.h>

int main(void)
{
	int c;
	int t;
	int  ct1=0;
	int  ct2=0;
	char *temp;
	int i,j;
	char str[10][81];
	char *p[10];
	int n = 0;
	puts("Input string:");
	while( n<10  && gets(str[n]))
	{
		p[n] = str[n];
		puts("Input string:");
		n++;
	}

	puts("1:输出字符串");
	puts("2:按 ASCII 顺序输出");
	puts("3:按长度递增");
	puts("4:按第一个单词的长度");
	puts("0:推出");
	
	while(scanf("%d",&c))
	{
		while(getchar()!='\n')
			continue;
		switch(c)
	{
	case 1:
			
		break;
	case 2:
		for(i=0;i<n;i++)
			for(j=i+1;j<n;j++)
			{
				if(strcmp(str[i],str[j])>0)
				{
					temp = p[i];
					p[i] = p[j];
					p[j] = temp;
				}
			}		
		break;
	case 3:
		for(i=0;i<n;i++)
			for(j=i+1;j<n;j++)
			{
				if(strlen(str[i])>strlen(str[j]))
				{
					temp = p[i];
					p[i] = p[j];
					p[j] = temp;
				}
			}
		break;
	case 4:
		for(i=0;i<n;i++)
			for(j=i+1;j<n;j++)
			{
				while(*(str[i]+ct1)!=' ' && *(str[i]+ct1)!='\0')
					ct1++;
				while(*(str[j]+ct2)!= ' '&& *(str[j]+ct2)!='\0')
					ct2++;
				if(ct1>ct2)
				{	
					temp = p[i];
					p[i] = p[j];
					p[j] = temp;
				}
				ct1 = 0;
				ct2 = 0;
			}
		break;
	case 0:
		puts("Bye!");
		return 0;
	default:
		puts("Please input right");
			break;
	}
	for (i=0;i<n;i++)
		puts(p[i]); 
	}
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值