例8-11 输入5个字符串,输出其中最小的字符串。
#include <stdio.h>
#include <string.h>
int main(void){
int i;
char sx[80], smin[80];
scanf("%s", sx);
strcpy(smin, sx);
for (i = 1; i < 5; i++) {
scanf("%s", sx);
if (strcmp(sx, smin) < 0) {
strcpy(smin, sx);
}
}
printf("min is %s\n", smin);
return 0;
}
C语言便准库中还有许多其他多种用于处理字符和字符串的函数,分别包含在ctype.h和string.h文件中,可参考附录A。
练习8-6
在使用函数 scanf() 时,输入阐述列表需要使用取地址操作符 &,但当参数为字符数组名时并没有使用,为什么?如果在字符数组名前加上取地址操作符 &,会发生什么?
答:因为字符数组名本身就是数组首项的地址,所以不需要使用地址操作符号。如果加上&的话,就是取数组第一项地址存放的地址(木知对不对)
练习8-7
C语言不允许用赋值表达式直接对数组赋值,为什么?
答:因为数组名本身就是一个指针,指向的地址是数组的第一项的地址,所以不能直接对其赋值。
练习8-8
输入一个字符串,把该字符串的前三个字母移到最后,输出变换后的字符串。比如输入"abcdef",输出为"defabc"
(用书上的指定的PTA平台做的题,补全下面的代码)
void Shift( char s[] ){
int num=strlen(s), i;
char s2[3];
for(i=0;i<=2;i++){
s2[i]=s[i];
}
for(i=0;i<=num-3;i++){
s[i]=s[i+3];
}
strcat(s,s2);
}
测试通过:
*8.5 任意个整数求和
例8-12 先输入一个正整数n,再输入任意n个整数,计算并输出这n个整数的和。要求使用动态内存分配方法为这n个整数分配空间。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n, sum, i, * p;
printf("Enter n:");
scanf("%d", &n);
if ((p = (int*)calloc(n, sizeof(int))) == NULL) {
printf("Not able allocate memory.\n");
exit(1);
}
printf("Enter %d integers:", n);
for (i = 0; i < n; i++) {
scanf("%d", p + i);
}
sum = 0;
for (i = 0; i < n; i++) {
sum = sum + *(p + i);
}
printf("The sum is %d \n", sum);
free(p);
return 0;
}
首先通过输入的n的值,使用函数calloc()申请能存放n个int型数据的内存单元。如果申请成功,就得到动态内存的首地址,将该地址存放在指针p中,通过移动指针存入n个整数,再通过移动指针去除各个数并计算它们的和。动态内存申请的存储空间没有名字,只有首地址的连续存储空间,相当于一个一维数组。
此动态内存的首地址经过强制类型转换成int型存放在指针变量p中,通过移动p来存取各个数据。
8.5.2 使用指针实现内存动态分配
程序中需要使用各种变量来保存被处理的数据和各种状态信息,变量在使用前必须被定义且安排好存储空间(起始地址和存储单元大小)。C语言的全局变量、静态局部变量的存储时在编译时确定的,其存储空间的实际分配在程序开始执行前完成。
局部自动变量在执行进入变量定义所在的复合语句时为它们分配存储单元,这种变量的大小也是静态确定的。
静态方法安排存储的好处时实现方便,效率高,程序执行中需要做的事情较简单,但是如例8-7所描述的问题,每次求和的项数可能不同,可能的方法就是定义一个很大的数组,以保证输入的项数不超过数组能容纳的范围。
当在一般情况时,运行中的很多存储要求在写程序时无法确定,所以需要一种机制,可以根据运行时的实际存储需求适当分配存储区,用于存放那些在运行中才能确定数量的数据。C语言为此提供了动态存储管理机制,允许程序动态申请和释放存储空间。
C语言中的两种方法使用内存:
1. 由编译系统分配的内存区
2. 用内存动态分配方式,留给程序动态分配的存储区。动态分配的存储区在用户的程序之外,不是由编译系统分配的,而是由用户在程序中通过动态分配获取的。使用动态内存分配有效地使用内存,同一段存储区域可以被多次使用,使用时申请,用完就释放。
动态内存分配的步骤:
(1) 了解需要多少内存空间。
(2) 利用C语言提供的动态分配函数来分配所需要的存储空间。
(3) 使指针指向获得的内存空间,以便用指针在该空间内实施运算或操作
动态内存分配的函数(stdlib.h中):
(1) 动态存储分配函数malloc():
函数原型是:void *malloc (unsigned size)
功能:在内存的动态存储区中分配一连续空间,长度为size。如果申请成功,则返回指向所分配内存空间的其实地址的指针;如果申请内存不成功,则返回NULL(值为0)。malloc()的返回值为(void * )类型。在具体使用中,将malloc()的返回值转换为特定指针类型,赋给一个指针。
例题8-12中的申请内存可改为:
if ((p = (int*)malloc(n*sizeof(int))) == NULL) {
printf("Not able allocate memory.\n");
exit(1);
}
这里的存储块是动态分配的,但是它的大小在分配后是确定的。不要越界使用。
(2) 计数动态存储分配函数calloc():
函数原型是:void *calloc (unsigned n, unsigned size)
功能:在内存的动态存储区中分配n个连续空间,每一个存储空间的长度为size,并且分配后还把存储里全部初始化为0。若申请成功,则返回一个指向被分配内存空间的起始地址的指针;若申请内存空间不成功,则返回NULL(值为0)
malloc()分配存储块后不做任何事,calloc()对整个区域进行初始化
(3) 动态存储释放函数free():
函数原型是:void free (void *ptr)
功能:释放由动态存储分配函数申请到的整块内存空间,ptr为指向要释放空间的首地址。如果ptr的值是空指针,则free什么都不做。
(4) 分配调整函数realloc():
函数原型是:void *realloc (void *ptr, unsigned size)
功能:更改以前的存储分配。ptr必须是以前通过动态存储分配得到的指针。参数size为现在需要的空间大小。如果分配失败,则返回NULL,同时原来ptr指向存储块的内容不变,如果成功,返回一片能存放大小为size的区块,并保证该块的内容与原块的一致。如果size小于原块的大小,则内容为原块前四则范围内的数据;如果新块更大,则原有数据存在新块的前一部分。
练习8-9
使用动态内存分配的方法实现例8-5的冒泡排序。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i, n, j, temp;
int* p;
printf("Enter n:");
scanf("%d", &n);
if ((p = (int*)calloc(n, sizeof(int))) == NULL) {
printf("Not able allocate memory.\n");
exit(1);
}
printf("Enter data:");
for (i = 0; i < n; i++) {
scanf("%d", (p + i));
}
for (i = 0; i < n; i++) {
for (j = 0; j < n-1; j++) {
if (*(p + j) < *(p + j + 1)) {
temp = *(p + j);
*(p + j) = *(p + j + 1);
*(p + j + 1) = temp;
}
}
}
for (j = 0; j < n; j++) {
printf("%d", *(p + j));
}
return 0;
}
习题8
程序设计题1
拆分实验的整数与小数部分:要求自定义一个函数void splitfloat(float x, int *intpart, float * fracpart),其中x是被拆分的实数,* interpart和* ftacpart分别是将实数x拆分出来的整数部分与小数部分。编写主函数,并在其中调用函数splitfloat()。
#include <stdio.h>
void splitfloat(float x, int* intpart, float* fracpart);
int main(){
float x, fracpart;
int intpart;
scanf("%f", &x);
splitfloat(x, &intpart, &fracpart);
printf("The integer part is %d\n", intpart);
printf("The fractional part is %g\n", fracpart);
return 0;
}
/* 你的代码将被嵌在这里 */
void splitfloat(float x, int* intpart, float* fracpart) {
*intpart = x * 1;
*fracpart = x - *intpart;
}
程序设计题2
在数组中查找指定元素:输入一个正整数n(1<n≤10),然后输入n个整数存入数组a中,再输入一个整数x,在数组a中查找x,若找到则输出相应的下标,否则显示"Not found"。要求定义和调用函数search(int list[], int n, int x),在数组list中查找元素x,若找到则返回相应的下标,否则返回-1,参数n代表数组list中元素的数量。
#include <stdio.h>
#define MAXN 10
int search(int list[], int n, int x);
int main(){
int i, index, n, x;
int a[MAXN];
scanf("%d", &n);
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
scanf("%d", &x);
index = search(a, n, x);
if (index != -1)
printf("index = %d\n", index);
else
printf("Not found\n");
return 0;
}
/* 你的代码将被嵌在这里 */
int search(int list[], int n, int x) {
int i, result=0;
for (i = 0; i < n; i++) {
if (list[i] == x) {
result = 1;
return i;
}
}
if (result == 0) {
return -1;
}
}
程序设计题3
循环后移:有n个整数,使前面各数顺序向后移m个位置,移出的数再从开头移入。编写一个函数实现以上功能,在主函数中输入n个整数并输出调整后的n个数。
#include <stdio.h>
#define MAXN 10
void ArrayShift(int a[], int n, int m);
int main(){
int a[MAXN], n, m;
int i;
scanf("%d %d", &n, &m);
for (i = 0; i < n; i++) scanf("%d", &a[i]);
ArrayShift(a, n, m);
for (i = 0; i < n; i++) {
if (i != 0) printf(" ");
printf("%d", a[i]);
}
printf("\n");
return 0;
}
/* 你的代码将被嵌在这里 */
void ArrayShift(int a[], int n, int m) {
int b[MAXN], c[MAXN];
int i;
m = m % n;
for (i = n - m; i < n; i++) {
b[i - n + m] = a[i];
}
for (i = 0; i < n - m; i++) {
c[i] = a[i];
}
for (i = 0; i <= m-1; i++) {
a[i] = b[i];
}
for (i = m; i < n; i++) {
a[i] = c[i - m];
}
}
程序设计题4
报数:有n个人围成一圈,按顺序从1到n编好号。从第一个人开始报数,包到m的人退出圈子,下一个人从1开始报数,报道m的人退出圈子。如此下去,直到留下最后一个人。输入整数n和m,并按退出顺序输出退出圈子的编号。
#include <stdio.h>
#define MAXN 20
void CountOff( int n, int m, int out[] );
int main()
{
int out[MAXN], n, m;
int i;
scanf("%d %d", &n, &m);
CountOff( n, m, out );
for ( i = 0; i < n; i++ )
printf("%d ", out[i]);
printf("\n");
return 0;
}
/* 你的代码将被嵌在这里 */
void CountOff(int n, int m, int out[]) {
int i, j, count = 0, index = 1, a[MAXN];
for (i = 0; i < n; i++) {
a[i] = 1;
}
while(index<=n) {
for (i = 0; i < n; i++) {
if (a[i] != 0) {
count++;
if (count == m) {
out[i] = index;
index++;
a[i] = 0;
count = 0;
}
}
}
}
}
程序设计题5
使用函数实现字符串赋值:输入一个字符串t和一个正整数m,将字符串t中从第m个字符开始的全部字符复制到字符串s中,再输出字符串s。要求自定义并调用函数void strmcpy (char* s, char *t, int m)
#include <stdio.h>
#define MAXN 20
void strmcpy( char *t, int m, char *s );
void ReadString( char s[] ); /* 由裁判实现,略去不表 */
int main()
{
char t[MAXN], s[MAXN];
int m;
scanf("%d\n", &m);
ReadString(t);
strmcpy( t, m, s );
printf("%s\n", s);
return 0;
}
/* 你的代码将被嵌在这里 */
void strmcpy(char* t, int m, char* s){
int count=0,i=m-1;
while(t[i]!='\0'){
count++;
i++;
}
for(i=m-1;i<=m-1+count;i++){
s[i-m+1]=t[i];
}
}
程序设计题6
删除字符:输入一个字符串,再输入一个字符ch,将字符串中所有的ch字符删除后输出该字符串,要求定义和调用函数delchar(s,c),该函数将字符串s中出现的所有c字符删除。
#include <stdio.h>
#define MAXN 20
void delchar( char *str, char c );
void ReadString( char s[] ); /* 由裁判实现,略去不表 */
int main()
{
char str[MAXN], c;
scanf("%c\n", &c);
ReadString(str);
delchar(str, c);
printf("%s\n", str);
return 0;
}
/* 你的代码将被嵌在这里 */
void delchar( char *str, char c ){
int i=0,j;
while(str[i]!='\0'){
if(str[i]==c){
for(j=i;str[j]!='\0';j++){
str[j]=str[j+1];
}
i--; //重要,如果碰到替换的会跳一次
}
i++;
}
}
程序设计题7
字符串排序:输入5个字符串,按由小到大的顺序输出
#include <stdio.h>
#include <string.h>
int main(void){
char ch[5][100];
int i, j, index, order[5]={0,1,2,3,4}, tmp; //使用一个整型数组存放排列顺序
for (i = 0; i < 5; i++) {
scanf("%s", ch[i]);
}
for (i = 0; i < 5; i++) { //用类似选择排序法的方法
index = i;
for (j = i+1; j < 5; j++) {
if (strcmp(ch[order[index]], ch[order[j]])>0) {
index = j;
}
}
tmp = order[i];
order[i] = order[index];
order[index] = tmp;
}
printf("After sorted:\n");
for (i = 0; i < 5; i++) {
printf("%s\n", ch[order[i]]);
}
return 0;
}
程序设计题8
判断回文:判断输入的一串字符是否为“回文”。所谓“回文”是指顺读和倒读都一样的字符串。
(没学Bool类型,但是PTA平台题目中给出来了...放在Dev-C++和VSCode里面无法运行...)
#include <stdio.h>
#include <string.h>
#define MAXN 20
typedef enum {false, true} bool;
bool palindrome( char *s );
int main()
{
char s[MAXN];
scanf("%s", s);
if ( palindrome(s)==true )
printf("Yes\n");
else
printf("No\n");
printf("%s\n", s);
return 0;
}
/* 你的代码将被嵌在这里 */
bool palindrome(char* s) {
int i, len;
len = strlen(s);
for (i = 0; i <= len/2; i++) {
if (s[i] != s[len - 1 - i]) {
return false;
}
}
return true;
}
程序设计题9
分类统计字数:输入一行文字,统计其中的大写字母、小写字母、空格、数字以及其他字符各有多少。
#include <stdio.h>
#define MAXS 15
void StringCount( char *s );
void ReadString( char *s ); /* 由裁判实现,略去不表 */
int main()
{
char s[MAXS];
ReadString(s);
StringCount(s);
return 0;
}
/* Your function will be put here */
void StringCount( char *s ){
int i=0;
int a,b,c,d,e;
a=b=c=d=e=0;
while(s[i]!='\0'){
if(s[i]>='A'&&s[i]<='Z'){
a++;
}else if(s[i]>='a'&&s[i]<='z'){
b++;
}else if(s[i]==' '){
c++;
}else if(s[i]>='0'&&s[i]<='9'){
d++;
}else{
e++;
}
i++;
}
printf("%d %d %d %d %d",a,b,c,d,e);
}
程序设计题10
输出学生成绩(动态分配):输入学生人数后输入每个学生的成绩,最后输出学生的平均成绩、最高成绩和最低成绩。要求使用动态内存分配来实现。
#include <stdio.h>
#include <stdlib.h>
int main(void){
int n, i, j, index, temp;
double* p, sum = 0;
scanf("%d", &n);
if ((p = (double*)calloc(n, sizeof(double))) != NULL) {
for (i = 0; i < n; i++) {
scanf("%lf", p + i);
}
for (i = 0; i < n; i++) {
sum += *(p + i);
}
for (i = 0; i < n; i++) {
index = i;
for (j = i + 1; j < n; j++) {
if (*(p + index) > *(p + j)) {
index = j;
}
}
temp = *(p + index);
*(p + index) = *(p + i);
*(p + i) = temp;
}
printf("average = %.2f\nmax = %.2f\nmin = %.2f", sum / n, *(p + n - 1), *p);
}
return 0;
}