/*
1.请编写一个函数,它在一个字符串中进行搜索,查找所有在一个给定字符集合中出现的字符。这个函数的原型应该如下:
char *find_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.函数中的任何地方都不应该使用下标引用。
*/
char * find_char(char const *source, char const *chars)
{
printf("source:%s\n",source);
printf("chars:%s\n",chars);
char const *str;
while(*source != '\0')
{
str = chars; //===
while(*str != '\0')
{
if(*source == *str )
{
return source;
}
str++;
}
source++;
}
}
//6.19
/*
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.函数中的任何地方都不应该使用下标引用。
一个值得注意的是,空字符串是每个字符串的一个子串,在字符串中删除一个空子串字符串不会产生变化。
*/
int del_substr( char *str, char const *substr )
{
char const *substr_temp = substr;
char *str_temp = str;
int substr_len = 0;
char *str_current;
for(; *str_temp != '\0'; str_temp++)
{
if(*str_temp == *substr_temp)
{
str_current = str_temp;
str_temp++;
substr_temp++;
for(;*substr_temp != '\0';substr_temp++)
{
if(*substr_temp == *str_temp)
{
str_temp++;
}
else
{
return 0;
}
}
goto DEL;
}
}
return 0;
DEL:
/* get substr len */
substr_temp = substr;
while(*substr_temp != '\0')
{
substr_len++;
substr_temp++;
}
/* str offset */
while(*(str_current+substr_len) != '\0')
{
*str_current = *(str_current+substr_len);
str_current++;
}
*str_current = '\0';
return 1;
}
/*
3.编写函数reverse_string,它的原型如下:
void reverse_string( char *string );
函数把参数字符串中的字符反向排列。请使用指针而不是数组下标,
不要使用任何C函数库中用于操纵字符串的函数。
提示:不需要声明一个局部数组来临时存储参数字符串。
*/
void reverse_string( char *string )
{
char temp;
char *str_temp = string;
int str_len = 0;
while(*str_temp != '\0')
{
str_len++;
str_temp++;
}
for(int i = 0; i < (str_len/2); i++)
{
temp = *(string + i);
*(string + i) = *(string +str_len-1 - i);
*(string + str_len-1 - i) = temp;
}
}
/*
4.质数就是只能被1和本身整除的整数。
Eratosthenes筛选法是一种计算质数的有效方法。
这个算法的第1步就是写下所有从2至某个上限之间的所有整数。
在算法的剩余部分,你遍历整个列表并剔除所有不是质数的整数。
后面的步骤是这样的。找到列表中的第1个不被剔除的数(也就是2),
然后将列表后面所有逢双的数都剔除,因为它们都可以被2整除,
因此不是质数。接着,再回到列表的头部重新开始,
此时列表中尚未被剔除的第1个数是3,所以在3之后把每逢第3个数(3的倍数)剔除。
完成这一步之后,再回到列表开头,3后面的下一个数是4,
但它是2的倍数,已经被剔除,所以将其跳过,轮到5,将所有5的倍数剔除。
这样依次类推、反复进行,最后列表中未被剔除的数均为质数。
编写一个程序,实现这个算法,使用数组表示你的列表。
每个数组元素的值用于标记对应的数是否已被剔除。
开始时数组所有元素的值都设置为TRUE,当算法要求“剔除”其对应的数时,
就把这个元素设置为FALSE。如果你的程序运行于16位的机器上,
小心考虑是不是需要把某个变量声明为long。一开始先使用包含1000个元素的数组。
如果你使用字符数组,使用相同的空间,你将会比使用整数数组找到更多的质数。
你可以使用下标来表示指向数组首元素和尾元素的指针,但你应该使用指针来访问数组元素。
注意除了2之外,所有的偶数都不是质数。稍微多想一下,
你可以使程序的空间效率大为提高,方法是数组中的元素只对应奇数。
这样,在相同的数组空间内,你可以寻找到的质数的个数大约是原先的两倍。
*/
void Eratosthenes(long n)
{
int count = 0;
bool array[n+1];
array[0] = false;
array[1] = false;
for(long i = 2; i < n ; i++)
{
array[i] = true;
}
for(long i = 2; i < n ; i++)
{
if(array[i] == true)
{
for(long j = (i+i); j < n; j = j+i)
{
array[j] = false;
}
}
}
for(long i = 0; i < n ; i++)
{
if(array[i] == true)
{
count++;
printf("%d\t",i);
if(count >= 10 )
{
count = 0;
printf("\n");
}
}
}
}
/*
5.修改前一题的Eratosthenes程序,使用位的数组而不是字符数组,
这里要用到第5章编程练习中所开发的
位数组函数。这个修改使程序的空间效率进一步提高,
不过代价是时间效率降低。在你的系统中,使用这个方法,
你所能找到的最大质数是多少?
*/
/*
1.
Polynomials(厄密多项式)是这样定义的:
H(n)(x) = 1 n <= 0
= 2*x n = 1
= 2xH(n-1)(x) - 2(n-1)H(n-2)(x) n > 1
例如,H3(2)的值是40。请编写一个递归函数,计算Hn(x)的值。你的函数应该与下面的原型匹配:
int hermite( int n, int x)
*/
int hermite(int n, int x )
{
if(n <= 0 )
{
return 1;
}
else if(n == 1)
{
return 2*x;
}
else
{
return (2*x*hermite(n-1,x)) - (2*(n-1)*hermite(n-2,x));
}
}
/*
2
两个整型值M和N(M、N均大于0)的最大公约数可以按照下面的方法计算:
gcd(M,N) = M%N = 0 : N
= M%N = R,R>0 : gcd(N,R)
请编写一个名叫gcd的函数,它接受两个整型参数,并返回这
两个数的最大公约数。如果这两个参数中的任何一个不大于零,函数应该返回零。
*/
int gcd( int m, int n)
{
unsigned int r;
if(m <= 0 || n <= 0)
{
return 0;
}
if(n > m)
{
r = m;
m = n;
n = r;
}
while(n > 0)
{
r = m % n;
m = n;
n = r;
}
return m;
}
/*
3
为下面这个函数原型编写函数定义:
int ascii_to_integer( char *string );
这个字符串参数必须包含一个或多个数字,
函数应该把这些数字字符转换为整数并返回这个整数。
如果字符串参数包含了任何非数字字符,函数就返回零。
请不必担心算术溢出。提示:这个技巧很简单——你每发现一个数字,
把当前值乘以10,并把这个值和新数字所代表的值相加。
*/
int ascii_to_integer( char *string )
{
int sum = 0;
while(*string != '\0')
{
if(*string >= '0' && *string <= '9')
{
sum *= 10;
sum += *string - 0x30;
}
else
{
return 0;
}
string++;
}
return sum;
}
/*
4.编写一个名叫max_list的函数,
它用于检查任意数目的整型参数并返回它们中的最大值。
参数列表必须以一个负值结尾,提示列表的结束。
*/
int max_list(int n_values, ...)
{
va_list v_list;
int n = 0;
int max = 0;
/* 访问可变参数 */
va_start(v_list,n_values);
n = va_arg(v_list, int);
while(n > 0)
{
if(n > max)
{
max = n;
}
n = va_arg(v_list, int);
}
va_end(v_list);
return max;
}
/*
5.实现一个简化的printf函数,它能够处理%d、%f、%s和%c格式码。
根据ANSI标准的原则,其他格式码的行为是未定义的。
你可以假定已经存在函数print_integer和print_float,
用于打印这些类型的值。对于另外两种类型的值,使用putchar来打印。
*/
/*
6.编写函数
void written_amount( unsigned int amount, char *buffer );
它把amount表示的值转换为单词形式,并存储于buffer中。
这个函数可以在一个打印支票的程序中使用。例如,如果amount的值是16 312,
那么buffer中存储的字符串应该是
SIXTEEN THOUSAND THREE HUNDRED TWELVE
调用程序应该保证buffer缓冲区的空间足够大。
有些值可以用两种不同的方法进行打印。
例如,1 200可以是ONE THOUSAND TWO HUNDRED或TWELVE HUNDRED。
你可以选择一种你喜欢的形式。