C程序设计(第五版)

前言

鉴于写CSDN博客一篇一篇查找比较麻烦,所以特此把这些按照分类别成一个目录,后面把这些问题、知识点写到相关目录的下面,根据目录查找,复习,这个C语言系列就写到这篇上面了。后面如果写得太多,一篇博客写不完就把链接放到下面查找。

第1章 other

1、求下列运算结果
int main()
{
    char str[][10] = {"China","Beijing"},*p = (char*)str; 
    printf("%s",p+10);
}
//Bingjing
+---+---+---+---+---+---+---+---+---+---+
| C | h | i | n | a | \0| \0| \0| \0| \0|
+---+---+---+---+---+---+---+---+---+---+
| B | e | i | j | i | n | g | \0| \0| \0|
+---+---+---+---+---+---+---+---+---+---
总结:
1、这个如果是字符数组,则没有赋值的空间会被赋值为\0;
2、如果是整型数组,则为0;
3、如果是指针数组,则为NULL;

两个指针不可以进行加法运算//√
解析:由下面例子可以知道,两个指针进行加法运算
在 C/C++ 中,两个指针变量之间进行加法运算是合法的。当两个指针相加时,实际上是将它们所指向的内存地址相加,得到一个新的地址。例如:
int arr[5] = {1, 2, 3, 4, 5};
int *ptr1 = &arr[1];
int *ptr2 = &arr[3];
int *ptr3 = ptr1 + ptr2;  // ptr3 指向 arr[4]
在这个例子中,ptr1 指向数组 arr 的第二个元素,即 arr[1],而 ptr2 则指向数组 arr 的第四个元素,即 arr[3]。当我们将 ptr1 和 ptr2 相加时,得到了一个新的指针 ptr3,它指向数组 arr 的第五个元素,即 arr[4]。

需要注意的是,两个指针相加得到的结果并不一定指向有效的内存地址。例如,如果 ptr1 指向 arr 的最后一个元素,即 arr[4],那么 ptr1 + ptr2 的结果就指向了 arr 之外的内存地址,这是一种未定义的行为,可能会导致程序崩溃或产生其他意想不到的结果。因此,在进行指针加法运算时,必须确保指针所指向的内存地址是有效的,并且指针相加的结果也必须指向有效的内存地址。

不太会题目

第2章 运算符与表达式以及运算优先级

赋值

算术

关系

逻辑

逗号

条件

#include <stdio.h>
int main(int argc, char **argv) {
    int a = 1,b=4,c=3,d=2,e;
    e=a<b?a:c<d?c:d;
	printf("%d\n",e);
}
//结果是1, a<b?a:c<d?c:d————> a<b?a:(c<d?c:d)

在这里插入图片描述

第3章 顺序程序设计

  • 输入输出
    • 输入输出语句属于C语言嘛?不是,C语言本身不提供输入输出语句,而我们常见的scanf和printf是计算机属于IO接口这块,与硬件打交道的部分,这些函数由软件公司根据IO接口一系列标准编辑成一些具有功能的文件完成,C语言编辑器直接调用即可。(个人理解)
    • C语言中,即我们编写的源程序是怎么调用这些输入输出函数的?头文件#include<stdio.h>,std:standard标准,io:io接口,h:head files头文件。
    • 调用的两种区别:有#include<stdio.h>和#include"stdio.h"两种方式。第一种是由远到近的标准方式,第二种是有近到远的方式。这里的远指的是C编译系统的目录,就是放这些头文件的目录地方,近就是我们写源头程序的目录,即用户当前目录。
    • 计算机是方便人设计的,而与计算机交互中,最明显的就是视觉方面,在输入时我们希望以我们理解的方式输入,输出时以我们理解的方式输出,当然不能为所欲为,想怎么输出就怎么输出,这就要结合计算机能给你呈现什么?而计算机可以给你呈现的是我可以显示这个数据的大小,和多少进制,这就是输入输出格式。比如%d,这就是我们希望计算机用十进制整数格式输出,如果我们想让数据显示的范围更大一些,就用%f,这个规定显示更大一些,而且也包含小数。比如ASCII码中包含了写英文用到的符号这些,而这些是用二进制存在计算机里面的,如果我们想把这些二进制对应的符号输出来,就可以使用%c,单字符输出,如果字符比较多考虑%s,字符串输出。
    • 试一下%f小数的极限?一般实数的整数部分全部输出,小数部分只输出6位。所以在%m.nf中,精确度n>=6位是没有意义的。
      在这里插入图片描述
    • %f:%附加符,修饰符,起修饰补充声明的意思,f以小数形式输出。%f就是我(计算机)提前你说一下或者声明一下哈,我要用小数输出。
    • 键入问题,在刷题目的时候,有一道题目要求用户键入的正确格式,我选择了"a b c"(a空格b空格c,enter)结果是错误的,它告诉了我正确的答案应该如下输入才对。于是我试了一番,结果。。。。
#include <stdio.h>
int main(int argc, char **argv) {
    double a ,b ,c;
    scanf("%lf,%lf,%lf",&a,&b,&c);
	printf("%f,%f,%f",a,b,c);
}

在这里插入图片描述
后来我发现是我理解错误了,它这句话是在告诉我第一个是a的位置,所以用“a=”表示,第二个是b的位置,所以用“b=”表示,第三个是c的位置,所以用“c=”表示。而中间用“,”逗号隔开,表示第一个数据的结束,第二个数据的开始,用来表示这三个变量的分界点,就与定义的一一对应上了,然后enter键入,代表我确定是这个三个数字。
在这里插入图片描述

    • putchar( c),c可以是字符常量,整型常量,字符变量,或者整型变量。提到字符,这就得从计算机的源头说起,最常见到得就是英语文章,里面涉及英语单词,标点符号,除此之外还有其他方面用到得字符符号,把这些在各个领域常用到得符号集合在一起,存储在计算机里,方便使用计算机调用,这些符合的集合有个专门的名字,叫做ASCII码,它的范围用十进制表示是0~255(参考本书)。而putchar( c)这个字符输出函数就是专门用来显示这些字符的,而把一个整型赋值给c的时候,就是告诉计算机在ASCII码中找到这个整型数值,显示出与它对应的符号,当然,如果这个整型值不在ASCII码中,那就不知道什么了。这里值得注意的是如果我们要写一个字符,那怎么知道它是字符还是变量呢?为了区别,字符就该写成’a’,这个就是表示字母a,但是如果写成a,那就表示具有多大变量
      在这里插入图片描述
    • getchar( c)是字符输入函数。

第4章 选择结构程序设计c

逻辑运算

#include <stdio.h>
int main(int argc, char **argv) {
    int a=4,b=5,c=0,d;
    d = !a && !b||!c;
	printf("%d\n",d);
}
//为什么是1?不是0?这里会我们会错误地认为&&左边为0,&&后面的就不算了,所以会误以为d为0.
因为在表达式!a && !b||!c中,&& 的优先级高于 ||,所以实际上这个表达式相当于 (!a && !b) || !c。而因为 a 和 b 都是非零值,所以 !a && !b 的结果为 0,而 !c 结果也为1。最后,01 的结果总是1,因此 d 的值为1

在这里插入图片描述

第5章 循环程序设计

#include <stdio.h>
int main(int argc, char **argv) {
    int p[8]={11,12,13,14,15,16,17,18},i=0,j=0;
    while(i++ < 7)if(p[i]%2) j+= p[i];
	printf("%d",j);
}
j=//45,这个函数的作用是数组中的奇数之和,因为经过i++判断之后i++忽略掉了第0个元素11,直接从第2个元素13开始了。


问题:输入一个大于1的整数n,判定它是否为素数(prime,又称质数)?
质数:(1)不等于1;(2)公因数只要1和它本身的自然数;
采用算法:题目中已经大于1,就不考虑(1),满足(2)的思路:让n被i(i属于(2,n-1))的数字除一遍数,看是不是n还存在其他公因数,如果不是,那它就是素数。

#include <stdio.h>
int main(int argc, char **argv) {

    int n,i;
    printf("please enter a integer number,n = ?");//提示用户输入一个整数
    
    scanf("%d",&n);
    for(i = 2; i < n;i++) 
        if(n % i == 0)break;//核心代码:判断是不是n还存在其他公因数
        if(i < n) printf("%d is not a prime number.\n",n);
        else printf("%d is a prime number.\n",n);
    return 0;  
}

程序改进:缩小遍历范围。
待定

为了方便,可以定义个整型变量k(其值为√ ̄n的整数部分);如果n不能被2~k(即√ ̄n)的任意整数整除,则在完成最后一次循环后,i还要加1,因此i = k +1,然后才能终止循环。在循环之后判断i的值是否大于或等于k+1,若是则表明未曾被 2 ~ k任一整数除过,因此输入该数是素数。

这些话我还不能理解,并不能提供理论依据,属于知识盲区,所以用待定标识,以方便后面查找。这里大概意思是这个遍历范围可以缩小,将原来的遍历2~ n缩小到2 ~√n处理,以提高计算机的效率,方便理解怎么用?我找到了一下例子:

我将按照我的逻辑推理一个整数是否为素数的过程来演示一下。使用1718作为例子。
首先,我们需要计算每个整数的平方根。
对于17,它的平方根是大约4.12。我们只需要判断是否存在24之间的整数能整除17。
对于18,它的平方根是大约4.24。同样,我们只需要判断是否存在24之间的整数能整除18。
现在,我们来逐个判断这些整数是否能整除给定的数。

对于172不能整除173不能整除174不能整除17。
我们检查了从24的整数,没有找到能整除17的整数。所以17是素数。

对于182可以整除18。
我们不需要再继续判断了,因为我们已经找到了一个能整除18的整数。
所以,根据正确的逻辑推理,17是素数,而18不是素数。

上面注意 “除”“除以” 的区别?
除表示除数除被除数;除以表示被除数除以除数。“以”字的意思就是“用”“拿”。比如:8÷2=4,这道除法题用“除和除以”可以描述为2除8等于4或者8除以2等于4。“除”,是除数在前,被除数在后;例如2除6,就是用2来分6的意思。“除以”,是被除数在前,除数在后;例如6除以2,就是6被2分的意思。
在这里插入图片描述
来自于AEUC学术交流中心
优化该进后的代码:

#include <stdio.h>
#include <math.h>

int main(int argc, char **argv) {
    int n, i;
    printf("please enter an integer number, n = ?"); // 提示用户输入一个整数
    scanf("%d", &n);
    
    int limit = (int) sqrt(n); // 计算根号n
    
    for (i = 2; i <= limit; i++) {
        if (n % i == 0)
            break; // 核心代码:判断是不是n还存在其他公因数
    }
    
    if (i <= limit)
        printf("%d is not a prime number.\n", n);
    else
        printf("%d is a prime number.\n", n);
    
    return 0;  
}


第6章 利用数组处理批量数据

char c[3][3]={"a","bc","def"};矩阵 c 存储如下:
a b c
d e f
char c[3][3] = {"a", "bc", "def"},矩阵 c 存储如下:
a  \0 \0
b  c  \0
d  e  f

关于字符串的8种处理函数

序号函数名解释表达备注
1puts(参数1)输出字符串函数char str[]=“china”; put(str)- 存储时编译器会自动在字符串后面添加’\0‘作为结束标志,- 而puts(参数1) 可以把’\0‘转换为’\n‘输出字符串1后换行。
2gets(参数1)输入字符串函数输出完毕后按enter将缓冲中的数据传入到计算机中进行运算
3strcat(参数1,参数2)字符串连接函数strcat(arry1,arry2)- 把arry2拼接到arry1中 - arry1中的’\0’消除在进行拼接 -arry1要确保空间足够否则会造成内存越界
4strcpy(参数1,参数2)字符串复制函数strcpy(arry1,arry2)- 把arry2复制到arry1 -arry1要确保空间足够否则会造成内存越界
5strcmp(参数1,参数2)字符串比较函数strcmp(arry1,arry2)挨个比较arry1,arry2中的字符,同,返回值为0;前者大,则为一个正整数;前者小,则为负整数。
6strlen(参数1)测字符串长度函数strlen(arry1)-不管字符串中或者尾,凡遇’\0’ 则停计数,‘\0’不计入内 - sizeof()会计数’\0’
7strwr(参数1)转换为小写函数string lowercase
8strupr(参数1)转换为大写函数stringuppercase

共同点

  • 上面的都是把字符串存储到字符数组当中,所以这里的参数1和参数2均是字符数组的地址。
  • 使用字符串处理函数时,加上头文件:#inlcude<string.h>

第7章 用函数实现模块化程序设计

第8章 善于利用指针

第9章 用户建立数据类型

结构体

struct sk{
    int a;
    float b;
}data, *p = &data;1)(*p).data.a ?
(2(*p).a?
(3)p->data.a ?
(4)p.data.a ?

(*p).data.a:这个表达式尝试使用 (*p) 运算符先解引用指针 p,然后再使用成员访问运算符 . 访问 data 字段,最后使用 .a 访问结构体 data 中的成员 a。然而,data 结构体本身并没有名为 data 的字段,因此这个表达式是错误的。

(*p).a:这个表达式也使用 (*p) 运算符先解引用指针 p,然后使用成员访问运算符 . 访问 a 字段。这是正确的方法之一,用于访问结构体 data 中的成员 a。

p->data.a:这个表达式使用箭头运算符 -> 直接从指针 p 中访问结构体 data 中的成员 a。箭头运算符会自动进行指针解引用和成员访问操作。因此,这也是正确的方法之一,用于访问结构体 data 中的成员 a。

p.data.a:这个表达式是错误的。因为 p 是一个指针,使用点运算符 . 来访问结构体成员需要对指针进行解引用。这里应改为 (*p).a 或 p->a 来访问结构体 data 中的成员 a。

正确的两种方法是 (*p).ap->a。这两个表达式都能正确访问结构体 data 中的成员 a。



#include<stdio.h>
#include<string.h>


/*
定义了一个结构体 Persion,
其中包含姓名和支持人数两个字段。
然后定义一个 leader 数组,
用于存储两个候选人的信息(姓名和支持人数)。
*/ 

//定义结构体
struct Persion
{
	//姓名 
	char name[10];
	//支持人数计数 
	int count;
}leader[2]= {"风铃儿",0,"白玉袖",0};

int main()
{
	int i,j;
	char leader_name[20];
	
	//输入投票提示			
	printf("请投票给 风铃儿 白玉袖,共计10次:\n "); 
	
	/*
	功能:通过循环让用户输入投票人的姓名,存入leader_name[20]中,
	然后这些数据与结构体中存储风铃儿与白玉袖的数组leader[2]作对比, 
	如果输入的姓名与风铃儿相同,则风铃儿的支持人数加1, 
	如果输入的姓名与白玉袖相同,则白玉袖的支持人数加1。
	
	备注:此处加1是加在结构体数组中count成员中;	
	*/
	for(i = 0;i < 10;i++){
		
		//读入投票人姓名 
		scanf("%s",leader_name);
		for(j = 0;j < 2;j++){
			
			//将输入的姓名与结构体中的姓名进行比较 
			if(strcmp(leader_name,leader[j].name)==0){
				
				//如果输入的姓名与结构体中的姓名相同,则相应姓名对应的支持人数+1
				leader[j].count++;
			}
	    }
	}
	
	/*
	通过循环遍历 leader 数组leader[2]中的数据,
	输出风铃儿与白玉袖的姓名和支持人数
	*/		
	printf("\nResult:\n");
	for(i = 0;i < 2;i++){
		printf("%5s:%d\n",leader[i].name,leader[i].count); 
	}
	
	return 0; 
		 
} 

在这里插入图片描述

共同体类型

共用体类型

字节对齐

点击跳转详见博主棉花糖超人【C上分之路】第八篇:结构体声明定义、结构体数组以及字节对齐
1、按一个字节对齐;
2、按编译器默认进行对齐;
3、字节对齐规则:
(1)结构体内部任何K字节的基本对象相对于结构体首地址的偏移,必须是K的整数倍
(2)结构体变量的首地址能够被其最宽基本类型成员的大小所整除
(3)结构体的总大小为结构体最宽基本类型成员大小的整数倍

struct stu{
    union{
        char a[5];
        int b[2];
    }_class;
    
    char c[8];
    float d;
}lk;int2字节,char 1 float 4 ,Find  sizeof(lk)?

根据结构体内存对齐的规则,对于这个结构体的大小 sizeof(struct stu) 的计算如下:

char a[5] 占用 5 个字节。

int b[2] 占用 2 个 int类型大小的字节,因为 int 在题中定义为 2 个字节大小,所以 int b[2] 总共占用 4 个字节。

char c[8] 占用 8 个字节。

float d 占用 4 个字节。

所以,这个结构体的总大小为 5 + 4 + 8 + 4 = 21 个字节,即 sizeof(struct stu) 的值为 21



第10章 对文件的输入输出

结构体指针获取结构体成员

通过结构体指针获取结构体成员的方式
1(*ptr).structMember
2、ptr->structMember
.运算符高于*,所以(*ptr)括号不能少

1、问题:结构数组student中的元素都已有值,若要将这些元素写入到硬盘文件fp中,应该怎么写入,有多少种方式?

struct st{
    char name[8];
    int num;
    float s[4];   
}student[50];

1fwrite(student, sizeof(struct st), 50, fp);//√
2fwrite(student, 50 * sizeof(struct st), 1, fp);//√
3fwrite(student, 25 * sizeof(struct st), 25, fp);//x
//写入的数据超过实际的数组数据,可能造成文件大小不正确或写入了无效的数据,正确写法应为把第三个参数修改为2.
4for (i = 0; i < 50; i++) {
    fwrite(student + i, sizeof(struct st), 1, fp);
}//√


相关知识补充:
数据块写入:fwrite();
//参数1:要获取的数据的地址
//参数2:要写入内容的单字节数
//参数3:要写入size字节的数据项的个数
//参数4:目标文件指针
//返回值:返回实际写入的数据块的数目
//作用:向文件写入数据块,以二进制形式对文件进行操作,不局限于文本文件。
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

当将结构体数组写入硬盘文件时,有多种方法可以实现。以下是几种常见的方法,每种方法都附有代码和注释来解释其功能和用法。

方法1: 逐个写入结构体

FILE *fp = fopen("filename.txt", "wb"); // 打开文件

for (int i = 0; i < 50; i++) {
    fwrite(&student[i], sizeof(struct st), 1, fp); // 逐个写入结构体
}

fclose(fp); // 关闭文件

该方法通过循环逐个写入结构体数组 student 中的每个元素,每个元素的大小为 sizeof(struct st),写入的数据个数为 1。

方法2: 批量写入所有结构体

FILE *fp = fopen("filename.txt", "wb"); // 打开文件

fwrite(student, sizeof(struct st), 50, fp); // 批量写入所有结构体

fclose(fp); // 关闭文件

该方法通过一次性写入整个结构体数组 student 到文件中,每个元素的大小为 sizeof(struct st),写入的数据个数为 50。

方法3: 通过指针进行写入

FILE *fp = fopen("filename.txt", "wb"); // 打开文件

// 将指针指向结构体数组的第一个元素,然后写入整个结构体数组
fwrite(student, sizeof(struct st), 25, fp);

fclose(fp); // 关闭文件

该方法利用指针将结构体数组 student 的前 25 个元素作为整体进行写入,每个元素的大小为 sizeof(struct st),写入的数据个数为 25。
当涉及到rewind()函数时,有几个重要的知识点需要了解。下面我将以自然数字列举它们:

关于rewind()

  • rewind()函数用于将文件指针重新定位到文件的开头。
  • 它是一个C语言中的库函数,需要包含头文件<stdio.h>。
  • 函数原型:void rewind(FILE *stream);,接受一个FILE类型的指针参数。
  • 通过调用rewind(fp),可以将文件指针fp重新设置为文件的开头位置。
  • rewind()函数常用于重新读取文件的操作,可以通过重新定位文件指针来实现。
  • 它不返回任何值(即返回类型为void),只是执行将文件指针重新定位的操作。
  • 在使用rewind()函数之前,必须先通过fopen()函数打开相应的文件,并获得文件指针。
  • 在调用rewind()函数之后,可以通过fread()、fwrite()等函数重新读取或写入文件的内容。
  • EOF(End of File)是一个标识符,表示已达到文件的末尾。例如在循环中以while (ch != EOF)的方式来读取文件内容。

题目练习:

#include<stdio.h>
int main()
{
	FILE *fp;
	int i, a[6] = {1,2,3,4,5,6};
	fp = fopen("d2.dat","w+");
	for(i=0;i<6;i++) fprintf(fp,"%d\n",a[i]);
	rewind(fp);//使用rewind()函数将文件指针重新定位到文件的开头位置。 
	for(i=0;i<6;i++) fscanf(fp,"%d",&a[5-i]);//下标为5、4、3、2、1、0,将元素逆序读入a数组了。 
	fclose(fp);
	for(i=0;i<6;i++) printf("%d,",a[i]);
	return 0; 
} 

在这里插入图片描述
相关知识点补充
在这里插入图片描述

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第五版C语言程序设计第九章主要讲解了用户自己建立数据类型,包括结构体和联合体的定义、初始化、访问和使用等内容。具体包括以下几个方面: 1. 结构体的定义和使用 结构体是一种用户自定义的数据类型,可以将不同类型的数据组合在一起,形成一个新的数据类型。结构体的定义格式为: ```c struct 结构体名 { 数据类型1 成员名1; 数据类型2 成员名2; ... }; ``` 结构体的成员可以通过“.”运算符进行访问,例如: ```c struct Student { char name[20]; int age; float score; }; struct Student stu1 = {"Tom", 18, 90.5}; printf("Name: %s, Age: %d, Score: %.1f\n", stu1.name, stu1.age, stu1.score); ``` 2. 结构体指针的使用 结构体指针可以指向结构体变量,也可以指向动态分配的结构体内存。结构体指针的定义格式为: ```c struct 结构体名 *指针变量名; ``` 结构体指针的成员访问可以使用“->”运算符,例如: ```c struct Student stu2 = {"Jerry",20, 85.0}; struct Student *p = &stu2; printf("Name: %s, Age: %d, Score: %.1f\n", p->name, p->age, p->score); ``` 3. 联合体的定义和使用 联合体是一种特殊的结构体,所有成员共用同一块内存空间,不同成员的值会互相影响。联合体的定义格式为: ```c union 联合体名 { 数据类型1 成员名1; 数据类型2 成员名2; ... }; ``` 联合体的成员访问可以使用“.”运算符,例如: ```c union Data { int i; float f; char str[20]; }; union Data data; data.i = 10; printf("data.i: %d\n", data.i); data.f = 3.14; printf("data.f: %.2f\n", data.f); strcpy(data.str, "hello"); printf("data.str: %s\n", data.str); ``` 4. 枚举类型的定义和使用 枚举类型是一种用户自定义的数据类型,可以将一组相关的常量定义为一个枚举类型。枚举类型的定义格式为: ```c enum 枚举类型名 { 枚举常量1, 枚举常量2, ... }; ``` 枚举类型的变量可以直接赋值为枚举常量,例如: ```c enum Color {RED, GREEN, BLUE}; enum Color c = GREEN; printf("c: %d\n", c); ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值