C和指针第6章编程练习

★★★1.编写一个函数,它在一个字符串中进行搜索,查找在一个给定字符集合中出现的所有
字符。这个函数的原型如下:
char °Eind_ char( char const "source,char const *chars )
它的基本想法是查找source字符串中匹配chars字符串中任何字符的第1个字符,然后返回一个指向source中第1个匹配所找到的位置的指针。如果source中的所有字符均不匹配chars中的任何字符,就返回一个 NULL指针。如果任何一个参数为NULL,或任何一个参数所指向的字符串为空,函数也返回一个NULL指针。
举个例子,假定source指向ABCDEF,如果chars指向XYZ、JURY或QQQQ,函数就返回一个NULL指针:如果chars指向XRCQEF,函数就返回个指向 source中C字符的指针。参数所指向的字符串是绝不会被修改的。
碰巧,C函数库中存在-个名叫strpbrk的函数,它的功能几乎和这个要编写的函数一模一样。但这个程序的目的是让你自己练习操纵指针,所以:
a. 不应该使用任何用于操纵字符串的库函数(如strcpy、 strcmp、 index等);
b.函数中的任何地方都不应该使用下标引用。

#include<stdio.h>
#include<string.h>
#define SLEN 100
char* find_char(char const* source,char const* chars);
char* s_gets(char* st, int n);
int main(void) {
	char source[SLEN]={0};
	char chars[SLEN]={0};
	char* p;
	while (s_gets(source, SLEN) != NULL && source[0] != '\0') {
		s_gets(chars, SLEN);
		p = find_char(source, chars);
		if (p != NULL)
			printf("%c\n", *p);
		else
			puts("000\n");
	}
	return 0;
}
char* find_char(char const* source,char const* chars) {
	char* p=chars;/*记录chars[0]地址*/
	for (; *source != '\0';source++) {
		while (*chars != '\0') {
			if (*source != *chars)
				chars++;
			else 
				return source;
		}
		chars = p;
	}
	return NULL;
}
char* s_gets(char* st, int n) {
	char* ret_val;
	char* find;
	ret_val = fgets(st, n, stdin);/*fegst和gets基本一样不同的是  第二个参数表示读取n个字符 第三个参数表示从标准输入流读取字符stdin*/
	if (ret_val) {
		find = strchr(st, '\n');/*查找指定字符*/
		if (find)
			*find = '\0';/*将指定字符赋值为\0*/
		else while (getchar() != '\n')/*筛选掉多余的字符*/
			continue;
	}
	return ret_val;
}

★★★2. 编写一个函数,删除一个字符串的一部分。 函数的原型如下:
int del_ substr( char *str, char const substr )
函数首先应该判断substr是否出现在str中。如果它并未出现,函数就返回0;如果出现,函数应该把str 中位于该子串后面的所有字符复制到该子串的位置,从而删除这个子串,然后函数返回1.如果substr多次出现在str中,函数只删除第1次出现的子串。函数的第2个参数绝不会被修改。
举个例子,假定str指向ABCDEFG,如果substr指向FGH、CDF或XABC,函数应该返回0, str 未作任何修改;如果substr指向CDE,函数就把str修改为指向ABFG,方法是把F、G和结尾的NUL字节复制到C的位置,然后函数返回1。不论出现什么情况,函数的第2个参数都不应该被修改。
和上题的程序一样:
a.不应该使用任何用于操纵字符串的库函数(如strcpy、strcmp 等);
b.函数中的任何地方都不应该使用下标引用。
一个值得注意的地方是,空字符串是每个字符串的一个子串,在字符串中删除一个空子串字符串不会产生变化。

#include<stdio.h>
#include<string.h>
#define SLEN 100
int del_substr(char* str, char const* substr);
char* s_gets(char* st, int n);
int main(void) {
	char str[SLEN];
	char substr[SLEN];
	while (s_gets(str, SLEN) != NULL && substr[0] != '\0') {
		s_gets(substr, SLEN);
		if (del_substr(str, substr))
			puts(str);
		else
			printf("0\n");
	}
	return 0;
}
int del_substr(char* str, char const* substr) {
	int i=0;
	char* p = substr;/*记录substr[0]地址*/
	while (*str != '\0') {
		if (*str == *substr)
			break;
		str++;
	}
	if (*str != '\0') {
		while (*substr != '\0') {
			if (*str != *substr) {
				substr = p;
				i = 0;
			}
			substr++;
			str++;
			i++;
		}
		if (*substr == '\0') {
			while (*str != '\0') {
				*(str - i) = *str;
				str++;
			}
			*(str - i) = *str;
			return 1;
		}
	}
	return 0;
}
char* s_gets(char* st, int n) {
	char* ret_val;
	char* find;
	ret_val = fgets(st, n, stdin);
	if (ret_val) {
		find = strchr(st, '\n');
		if (find)
			*find = '\0';
		else while (getchar() != '\n')
			continue;
	}
	return ret_val;
}

★★★3.编写函数reverse_string, 它的原型如下:
void reverse_ string( char *string );
函数把参数字符串中的字符反向排列。请使用指针而不是数组下标,不要使用任何C函数库中用于操纵字符串的函数。提示:不需要声明一个局部数组来临时存储参数字符串。
 

#include<stdio.h>
#include<string.h>
#define SLEN 100
void reverse_string(char* string);
void s_gets(char* st, int n);
int main(void) {
	char string[SLEN];
	s_gets(string, SLEN);
	reverse_string(string);
	puts(string);
	return 0;
}
void reverse_string(char* string) {
	char ch;
	char* p = string;
	while (*string != '\0')
		string++;
	string--;
	while (p <=string) {
		ch = *p;
		*p = *string;
		*string = ch;
		string--;
		p++;
	}
	return;
}
void s_gets(char* st, int n) {
	char* ret_val;
	char* find;
	ret_val = fgets(st, n, stdin);
	if (ret_val) {
		find = strchr(st, '\n');
		if (find)
			*find = '\0';
		else while (getchar() != '\n')
			continue;
	}
	return;
}

★★★4.质数就是只能被1和本身整除的整数。Eratosthenes 筛选法是一 种计算质数的有效方法。这个算法的第1步就是写下所有从2至某个上限之间的所有整数。在算法的剩余部分,遍历整个列表并剔除所有不是质数的整数。
后面的步骤是这样的。找到列表中的第1个不被剔除的数(也就是2),然后将列表后面所有逢双的数都剔除,因为它们都可以被2整除,因此不是质数。接着,再回到列表的头部重新开始,此时列表中尚未被剔除的第1个数是3,所以在3之后把每逢第3个数(3的倍数)剔除。完成这一 步之后,再回到列表开头,3后面的下一个数是4,但它是2的倍数,已经被剔除,所以将其跳过,轮到5,将所有5的倍数剔除。这样依此类推,反复进行,最后列表中未被剔除的数均为质数。
编写一个程序,实现这个算法,使用数组表示你的列表。每个数组元素的值用于标记对应的数是否2被剔除。开始时数组中所有元素的值都设置为TRUE,当算法要求“剔除”其对应的数时,就把这个元素设置为FALSE。如果你的程序运行于16 位的机器上,小心考虑是不是需要把某个变量声明为long. 开始先使用包含1000 个元素的数组。如果使用字符数组,使用相同的空间将会比使用整数数组找到更多的质数。可以使用下标来表示指向数组首元素和尾元素的指针,但应该使用指针来访问数组元素

注意,除了2之外,所有的偶数都不是质数。稍微多想-下, 你可以使程序的空间效率大为提高,方法是数组中的元素只对应奇数。这样,在相同的数组空间内,可以寻找到的质数的个数大约是原先的两倍。

#include<stdio.h>
#include<stdlib.h>
#define SIZE 1000
void PirmeNumber(char* pirme,int number);
int main(void) {
	char pirme[SIZE];
	int i = 0;
	int number = 3;
	PirmeNumber(pirme,number);
	printf("%5d", 2);
	while (i < SIZE) {
		if (pirme[i])
			printf("%5d", number);
		number += 2;
		i++;
	}
}
void PirmeNumber(char* pirme,int number) {
	char* p = pirme;/*记录&pirme[0]的地址*/
	int i = 0;
	for (int i = 0; i < SIZE; i++)
		*(pirme + i) = 1;
	while (number) {
		int n = (number - 3) / 2;
		pirme = p + n;/*+n表示地址 + n, pirme[n]地址  记住p始终是pirme[0]的地址 +n没赋值*/
		if (pirme < p + SIZE)/*如果我们将这里改成  (pirme<pirme+SIZE )当然编程肯定通不了想想为什么(我也是脑袋蒙了一会在这里) */
			for (pirme += number; pirme < p + SIZE; pirme += number)
				*pirme = 0;
		else
			break;
		number += 2;
	}
	return;
}

★★5.修改前一题的Eratosthenes 程序,使用位的数组而不是字符数组,这里要用到第5章编程练习4中所开发的位数组函数。这个修改使程序的空间效率进一步提高, 不过代价是时间效率降低。在你的系统中使用这个方法,你所能找到的最大质数是多少?

#include<stdio.h>
#include<stdlib.h>
#define SIZE 100
#define END_MAX 8
void PirmeNumber(char* pirme);
void clear_bit(char bit_array[], unsigned bit_number);
int test_bit(char bit_array[], unsigned bit_number);
int main(void) {
	char pirme[SIZE];
	PirmeNumber(pirme);
	printf("%5d", 2);
	for (int i = 3; i < SIZE * END_MAX; i += 2) {
		if (test_bit(pirme + i / END_MAX, i))
			printf("%5d", i);
	}
}
void PirmeNumber(char *pirme) {
	for (int i = 0; i < SIZE; i++)
		*(pirme + i) = 0xfff;
	for (int i = 3; i < SIZE * END_MAX; i += 2) {
		if (test_bit(pirme + i / END_MAX, i))/*查看指定位*/
			for (int j = i * 2;j < SIZE * END_MAX; j += i)
				clear_bit(pirme + j / END_MAX, j);/*指定位清0*/
	}
}
void clear_bit(char bit_array[], unsigned bit_number) {
	*bit_array &=~(1 << bit_number % END_MAX);
}
int test_bit(char bit_array[], unsigned bit_number) {
	if (*bit_array & (1 << bit_number % END_MAX))
		return 1;
	else return 0;
}

★★6.大质数是不是和小质数一样多?换句话说,在50000~51000 之间的质数是不是和类型
100000~ 1001000之间的质数一样多?使用前面的程序计算0~1000之间有多少质数? 1000~ 2000之间有多少个质数?以此每隔1000 类推,到1000000 (或是你的代码块机器上允许的最大正整数)有多少个质数?每隔1000个数中质数的数量呈什么趋势?

#include<stdio.h>
#include<string.h>
#define SIZE 10000
void PiremNumber(char* pirem, int number);
int main(void) {
	char pirem[SIZE];
	int number = 3;
	int i = 1,count=1000;
	memset(pirem, 1, sizeof(pirem));/*将数组初始化为1*/
	PiremNumber(pirem, number);
	while (number < SIZE) {
		if (pirem[number])
			i+=1;
		number += 2;
		if ( number> count){
			printf("%d\n", i);
			i = 0;
			count += 1000;
		}
	}
	return 0;
}
void PiremNumber(char* pirem, int number) {
	while (number < SIZE) {
		if (*(pirem + number))
			for (int j = number * 2; j < SIZE; j += number)
				*(pirem + j) = 0;
		number += 2;
	}
	return;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值