文章目录
前言
鉴于写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小数的极限?一般实数的整数部分全部输出,小数部分只输出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,那就表示具有多大变量
- 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。最后,0 或 1 的结果总是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处理,以提高计算机的效率,方便理解怎么用?我找到了一下例子:
我将按照我的逻辑推理一个整数是否为素数的过程来演示一下。使用17和18作为例子。
首先,我们需要计算每个整数的平方根。
对于17,它的平方根是大约4.12。我们只需要判断是否存在2到4之间的整数能整除17。
对于18,它的平方根是大约4.24。同样,我们只需要判断是否存在2到4之间的整数能整除18。
现在,我们来逐个判断这些整数是否能整除给定的数。
对于17:
2不能整除17。
3不能整除17。
4不能整除17。
我们检查了从2到4的整数,没有找到能整除17的整数。所以17是素数。
对于18:
2可以整除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种处理函数
序号 | 函数名 | 解释 | 表达 | 备注 |
---|---|---|---|---|
1 | puts(参数1) | 输出字符串函数 | char str[]=“china”; put(str) | - 存储时编译器会自动在字符串后面添加’\0‘作为结束标志,- 而puts(参数1) 可以把’\0‘转换为’\n‘输出字符串1后换行。 |
2 | gets(参数1) | 输入字符串函数 | 输出完毕后按enter将缓冲中的数据传入到计算机中进行运算 | |
3 | strcat(参数1,参数2) | 字符串连接函数 | strcat(arry1,arry2) | - 把arry2拼接到arry1中 - arry1中的’\0’消除在进行拼接 -arry1要确保空间足够否则会造成内存越界 |
4 | strcpy(参数1,参数2) | 字符串复制函数 | strcpy(arry1,arry2) | - 把arry2复制到arry1 -arry1要确保空间足够否则会造成内存越界 |
5 | strcmp(参数1,参数2) | 字符串比较函数 | strcmp(arry1,arry2) | 挨个比较arry1,arry2中的字符,同,返回值为0;前者大,则为一个正整数;前者小,则为负整数。 |
6 | strlen(参数1) | 测字符串长度函数 | strlen(arry1) | -不管字符串中或者尾,凡遇’\0’ 则停计数,‘\0’不计入内 - sizeof()会计数’\0’ |
7 | strwr(参数1) | 转换为小写函数 | string lowercase | |
8 | strupr(参数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).a 和 p->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;
若int为2字节,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];
1、fwrite(student, sizeof(struct st), 50, fp);//√
2、fwrite(student, 50 * sizeof(struct st), 1, fp);//√
3、fwrite(student, 25 * sizeof(struct st), 25, fp);//x
//写入的数据超过实际的数组数据,可能造成文件大小不正确或写入了无效的数据,正确写法应为把第三个参数修改为2.
4、for (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;
}
相关知识点补充