这个笔记是在我学习时记录的易忘点以及易错点等,不是主体知识,适合作为复习时注意的细枝末节,现在分享给大家,希望对大家有所帮助,如有知识错误,欢迎批评指正,感谢大家
附一张美图,祝大家新的一年天天开心,学业进步
一.运算符和表达式
1.八进制十六进制无浮点数
2.前加加比后加加快,自增运算符的内容
3.x按位异或同一个值两次,会变为原来的x值(用于加密)
也可以实现以下交换两数的值的算法:
交换两个数a,b的值:
{
int a,b;
a=a^b;//a^b赋值给a
b=a^b;//a由于赋值为了a^b,此时表达式则相当于a^b^b,结果为a,故实现b=a的赋值交换
a=a^b;//由于b此时值为a,a的值在此时为a^b,故表达式相当于a^b^a,结果为b,故实现a=b的赋值交换操作
}
4.按位或的运用
设计一个文件打开方式,只读的方式打开=1,只写的方式打开=2,1|2则表示可读可写。-----此方法实现了用两个方式表示出了三种方式
5.定义多个变量可以在一行内完成:
int a=1,b=2,c=3;//共用int类型
6.使用逗号赋值时,从后往前赋值。eg:int c = 0 ; c = 1, 2, 3, 4 ; printf("%d", c);
最后输出是1.7.在C语言中,逗号运算符(comma operator)用逗号分隔的表达式序列来进行运算。逗号运算符的作用是依次计算每个表达式,并返回最后一个表达式的结果。
逗号运算符的语法形式为:expression1, expression2, expression3, ..., expressionN
逗号运算符的计算规则如下:
从左到右依次计算每个表达式。
每个表达式的结果会被丢弃,只有最后一个表达式的结果会作为整个逗号表达式的结果。
如果逗号表达式出现在其他表达式中,它的优先级是最低的,因此通常需要使用圆括号来明确优先级。
以下是一些示例说明逗号运算符的用法:
int a = 1, b = 2, c = 3;
int result;
result = (a++, b++, c++); // 逗号表达式计算 a++, b++, c++,返回 c++ 的结果
printf("%d\n", result); // 输出 3
int x = 1, y = 2;
int z = (x += 1, y += 2, x + y); // 逗号表达式计算 x += 1, y += 2, x + y,返回 x + y 的结果
printf("%d\n", z); // 输出 6
8.左移和右移操作符(n位)分别相当于乘2的n次方和除2的n次方(左移右侧加n个0,右移减少右边的n个数字(相当于取整了eg:3>>1==1))
9.按位取反~,正数操作后得负数,因为符号位也取反了
10.取余必须作用于整数
11.赋值号左边必须是变量,不能是运算式eg:a+b=2
12.++i++必须不能这样写,错了,不能用在一起
13.double 和float类型输出默认六位小数点
14.c语言结合性:
在 C 语言中,运算符的结合性指的是当一个表达式中有多个相同优先级的运算符时,它们的计算顺序是从左到右还是从右到左。
C 语言中大部分运算符都是从左到右结合的,也就是说它们的计算顺序是从左边的操作数开始,依次向右计算。例如,加法运算符 `+` 和减法运算符 `-` 都是从左到右结合的。
然而,也有一些运算符是从右到左结合的,例如赋值运算符 `=`。这意味着赋值运算符的计算顺序是从右边的操作数开始,依次向左计算。例如,`a = b = c` 的计算顺序是先计算 `b = c`,然后再计算 `a = (b = c)`。
需要注意的是,运算符的结合性只会影响具有相同优先级的运算符之间的计算顺序。如果表达式中有不同优先级的运算符,那么优先级高的运算符会先进行计算。如果你想要改变运算符的计算顺序,可以使用括号来明确指定计算的顺序。
15.整数和整数做算术运算得出的也是整数
强制转换
有关以上知识点的例题:
#include
int main()
{
int a = 1, b = 6, c = 2,d = 5;
float e;
e = (a + b) / c + (float)(d/c);
printf("%f\n", e);
return 0;
}
A、6 B、5.5 C、5 D、5.000000
答案是D
首先由于最后e转为最大的类型,即float类型,故默认输出六位小数点数,秒了
细致点就是:d/c得整数2,再将其强转为float,即为2.000000;接着(a+b)/c得出3隐式转换为3.000000;最后相加得出5.000000
16.逻辑运算符的短路:
#include
int main()
{
int x = 0, y = 0, z = 0;
++x || ++y && z++;
printf("x=%d,y=%d,z=%d\n", x, y, z);
return 0;
}
输出x=1,y=0,z=0;因为按优先级,自增在逻辑运算符前完成,但先从左开始自增,故求得++x为1后,发生短路,后面的&&整体(因为&&比||优先级高,&&先形成整体(即先计算))不再计算,故只有x变为了1补充:用int类型输出浮点数,则会发生小数部分的截断,而格式占位符则会发生四舍五入:
eg:
#include
int main() {
printf("%9.3f", 5.555555);
return 0;
}
最终输出的是: 5.556(前面四个空格)因为3代表保留三位小数,9代表占九位,所以前面补四位
二.循环与分支
17.else语句和最近的if配对,如果不能配对就会报错。。。if(){}else{}格式必须为这样,中间不可以插入其他东西,eg:if(){}int a;else{}
18.break在循环和switch中使用,在循环中常与if使用
19.goto只能在同一作用域(即{})跳转
goto用法:
goto label;
...
label: statement;
goto语句和标签的上下位置不是确定的,可以互换
20.比较少用的循环(do-while):
do-while 循环是一种在循环体执行之后检查条件的循环结构。它的语法形式如下:
do {
// 循环体
} while (条件);do-while 循环的执行过程如下:
首先,执行循环体中的代码块。
然后,检查条件。如果条件为真,则继续执行循环体;如果条件为假,则跳出循环,继续执行循环后面的代码。
下面是一个示例,演示了 do-while 循环的使用:
#include
int main() {
int i = 1;
do {
printf("%d ", i);
i++;
} while (i <= 5);
return 0;
}
输出结果为:1 2 3 4 5。
三.数组
21.数组中使用圆括号代替{}赋值是错误的,其意义发生了改变,使用{}赋值一个{}表示二维数组中的一个一维数组,即一行的二维数组;而使用()表示一个元素
eg:int arr[]={
(1,2,3),
(4,5,6),
(7,8,9),
}
则输出arr[0][0]为3,与变量赋值中使用逗号表达式不同,见6.//其实是因为逗号表达式的优先级低于赋值运算符,而赋值运算符结合性是从右向左的,故会出现6中最后
赋的值为1的情况。
22.常使用循环嵌套一次访问二维数组
23.一维数组只可以存一个字符串,二维数组本质为多个一维数组,可以存多个字符串(待证)
24.字符串存储在字符数组中,赋值形式为:char a[]="字符串”或char a[]={"字符串"},其中存储的不只有字符串中内容,还有一个额外的\0
25.遍历输出储存在字符数组中的字符串:
int main()
{
char a[][3]={"ab","cd","ef","gh"};//3是为了存储长度为二的字符串,因为还有\0
for(int j=0;j<4;j++)//只需遍历一维数组即可,无需遍历一维数组中的元素,因为字符串是一个整体,并且一个一维数组只可以存储一个字符串
{
printf("%s\n",a[j]);
}
return 0;
}
26.sizeof测的是数据的字节数,而不是元素数
27.在c语言中转义字符使用易忘点:
在 C 语言中,`\x56` 是一个转义序列,用于表示一个字符的十六进制 ASCII 值。
具体来说,`\x` 后面跟着两个十六进制数字,表示一个字符的 ASCII 值。在这个例子中,`\x56` 表示 ASCII 值为十六进制 56(对应十进制的 86)的字符。
在 ASCII 编码中,每个字符都有一个对应的唯一的 ASCII 值。十六进制的数字可以用来表示这些 ASCII 值,其中十六进制的数字 0-9 对应十进制的 0-9,而十六进制的字母 A-F 对应十进制的 10-15。
因此,`\x56` 在 C 语言中代表 ASCII 值为十六进制 56 的字符。具体是哪个字符取决于系统的字符集和编码方式。
需要注意的是,`\x` 转义序列只能用于字符常量或字符串常量中,不能直接用于变量或其他上下文中。例如,可以在字符串中使用 `"Hello\x20World"` 来表示带有空格的字符串 "Hello World"。
28.给数组中用scanf输入各元素的值时需要用&,因为数组[位置]只能到开始位置,加上&则可以定位到每一个位置了;而再次由于此种特性,使用scanf给字符数组输入字符串时,只需要scanf(“%s”,数组名)即可,不需加取地址符&。
29.gets,puts,getchar,putchar函数用法:
`gets`、`puts`、`putchar` 和 `getchar` 是 C 语言中用于输入和输出的函数。它们的用法如下:
1. `gets` 函数://gets函数可以读取空格而不停止读取,读到回车才会停止
- 原型:`char* gets(char* str);`
- 功能:从标准输入(键盘)读取一行字符串,并将其存储到指定的字符数组中。
- 注意:`gets` 函数存在安全性问题,不建议在实际开发中使用。可以使用更安全的 `fgets` 函数替代。
- 示例:
```c
#include
int main() {
char str[100];
printf("Enter a string: ");
gets(str);
printf("You entered: %s\n", str);
return 0;
}
```
2. `puts` 函数:
- 原型:`int puts(const char* str);`
- 功能:将一个字符串输出到标准输出(屏幕)上,并自动在末尾添加换行符。
- 示例:
```c
#include
int main() {
char str[] = "Hello, World!";
puts(str);
return 0;
}
```
3. `putchar` 函数:
- 原型:`int putchar(int c);`
- 功能:将一个字符输出到标准输出(屏幕)上。
- 示例:
```c
#include
int main() {
char ch = 'A';
putchar(ch);
return 0;
}
```
4. `getchar` 函数:
- 原型:`int getchar(void);`
- 功能:从标准输入(键盘)读取一个字符。
- 示例:
```c
#include
int main() {
char ch;
printf("Enter a character: ");
ch = getchar();
printf("You entered: %c\n", ch);
return 0;
}
```
这些函数都是 C 语言标准库中提供的常用的输入输出函数,可以用于简单的输入和输出操作。但需要注意的是,`gets` 函数存在安全性问题,应该避免使用。在实际开发中,建议使用更安全的替代函数,如 `fgets`、`printf`、`scanf` 等。30.strcat函数在string.h头文件中,用法如下:
#include
#include
int main() {
char str1[20] = "Hello";
char str2[] = " World!";
strcat(str1, str2);
printf("%s", str1);
return 0;
}
输出Hello World!
原理:给个小例子来理解:
#include
#include
#include
int main()
{
char str1[6][10] = { "hello","world","how","are","you","!" };
char str2[50] = "\0";
for (int i = 0; i < 6; i++)
{
strcat(str2, str1[i]);
}
puts(str2);
return 0;
}
以上代码为何不会输出str2原来的\0?????
答案就在下面:
在使用 strcat 函数将字符串追加到目标字符串时,strcat 函数会从目标字符串的末尾开始追加,直到遇到目标字符串的终止符 \0(空字符)为止。然而,在你的代码中,目标字符串 str2 的初始值是 \0,因此 strcat 函数会从 str2 的开头开始追加,而不会考虑到 \0。
为了解决这个问题,你可以将目标字符串 str2 的初始值设置为一个非空字符串31.字符数组和字符串关系深究(另有strcpy函数使用方式):
在 C 语言中,如果将一个字符串复制到一个字符数组中,且字符数组的大小大于或等于目标字符串的长度,剩余的空间会被自动补充为 \0(空字符),即字符串的终止符。
例如,假设有以下代码:
#include
#include
int main() {
char str1[10] = "Hello";
char str2[20] = "Initial Value";
strcpy(str2, str1);
printf("str2: %s\n", str2);
return 0;
}
在这个示例中,str1 是一个长度为 6 的字符串(包括终止符 \0),str2 是一个长度为 20 的字符数组。通过使用 strcpy 函数将 str1 复制到 str2 中,剩余的空间将被自动补充为 \0。因此,输出将是 "str2: Hello"。
需要注意的是,如果目标字符数组的大小小于目标字符串的长度,将会导致溢出错误,这是需要避免的。在处理字符串时,始终确保目标字符数组具有足够的空间来存储目标字符串及其终止符。
32.汉字在字符数组中如何存储?
在 C 语言中,汉字可以使用多字节字符集(如 UTF-8)进行存储。UTF-8 是一种变长编码,每个字符可以由 1 到 4 个字节组成。
要存储汉字,你需要使用足够大的字符数组来容纳汉字的 UTF-8 编码。每个汉字在 UTF-8 编码中通常占用 2个字节。因此,为了存储一个汉字,你需要至少 2个字节的空间。
其他字符:如数字,字母,标点符号等则一个字节表示一个字符(英文状态下),中文状态下的字母,数字,标点符号则仍然两个字节//以上均为多字节字符集中的情况
eg:“你好”两个字的字符串长度为4.(用strlen可得出)
33.scanf读取内容读到空格及回车会停止读取
eg:scanf("%s",str);若输入i am a man,只会读到i存到str数组中
而gets可以读取空格,读到回车才会停止,gets与puts均包含在
34.不能给字符数组整体用=赋值(除了初始化时);而给其中元素可以用=赋值
35.strcpy为字符串拷贝函数,在《string.h》中,用法:strcpy(str1,str2);就把str1中的字符串改变为str2的字符串了,存储在str1中。
36.bool类型需要引用
使用eg(且包含strcmp比较字符串函数用法):
#include
#include
#include
int main()
{
char str1[]="aaa";
char str2[] ="bbb";
bool is=strcmp(str1,str2);
if(!is)//因为strcmp函数比较两个字符串是若两个字符串相同,则会返回0表示假;不相同返回其他值;
printf("两个字符串一样");
else
printf("两个字符串不一样");
return 0;
}
四.函数
37.函数定义时参数列表中为形参,调用时使用的实际值参数为实参。
1.函数参数详解:
函数的参数分为实参和形参。在调函数时,我们将调用其他函数的函数称为主调函数,将被调用的函数称为被调函数。在调用有参函数时,主调函数和被调函数之间有数据传递关系。主调函数中将值传递出去的参数称为“实际参数”(简称实参),被调函数中用于接收主调函数所传递过来值的参数称为“形式参数”或“虚拟参数”(简称形参)。
2.函数的传参(实参和形参之间的数据传递)详解:
在调用函数的过程中,系统会把实参的值传递给被调函数的形参,或者说形参从实参得到一个值。函数调用过程中需要注意以下几点:(1)实参可以是常量、变量或表达式。如:max(3,a+b);(2)实参与形参的数据类型应相同或者赋值兼容,并且实参在实参列表中的位置与形参在形参列表中的位置必须对应。(3)函数遇到return返回语句返回过后,不再继续执行return之后的语句了。(4)形参在其所在函数的调用期间有效,可以参加此函数中的运算,但是不能用于其他函数中,因为是局部定义的。(5)函数的形参和实参是两个不同的变量,所以,一般情况下形参值的改变不影响实参的值,除非在函数参数的传递类型为引用传递(地址传递)。在未调用函数时,形参并不占用存储单元,开始函数调用时,才给形参开辟存储空间,函数调用结束后,形参的存储单元就会被释放。
(5)eg1:
#include
void addition(int a)
{
a += 1;
}
int main()
{
int a;
scanf("%d", &a);
addition(a);
printf("%d", a);
return 0;
}
结果发现输入3时,最后输出还是3,因为传参过程相当于赋值,即int a(形参中的a)=a(输入值的a),最后将形参的a加一,然后函数结束将形参释放,而对于 实参的a未作出任何改变,所以输出实参a还是3.(即值复制后改变而非改变原值)
eg2:
#include
void addition(int a[])
{
for (int i = 0; i < sizeof(a) / sizeof(int); i++)
{
a[i] += 1;
}
}
int main()
{
int a[4] = { 1,2,3,4 };
addition(a);
for (int i = 0; i < sizeof(a) / sizeof(int); i++)
{
printf("%d ", a[i]);
}
return 0;
}
以上代码想要证明地址传递,但存在一个问题导致输出为2,3,3,4:在函数 addition 中,无法正确获取数组 a 的长度。
在 C 语言中,当数组作为函数参数传递时,它会被转换为指针,失去了原本的长度信息。因此,在函数 addition 中使用 sizeof(a) / sizeof(int) 来计算数组长度是不正确的,它会得到一个错误的结果。
为了解决这个问题,你可以通过传递数组长度作为额外的参数来解决。以下是修正后的代码:
#include
void addition(int a[], int length) {
for (int i = 0; i < length; i++) {
a[i] += 1;
}
}
int main() {
int a[4] = { 1, 2, 3, 4 };
int length = sizeof(a) / sizeof(int);
addition(a, length);
for (int i = 0; i < length; i++) {
printf("%d ", a[i]);
}
return 0;
}
最终输出2,3,4,5,也证明了参数为数组是地址传递
eg3:
#include
void mul(int *x){
*x=*x+10;
}
int main()
{
int a=10;
mul(&a);
printf("%d",a);
mul(&a);
printf("%d",a);
mul(&a);
printf("%d",a);
return 0;
}
结果输出20,30,40
也可以证明地址传递可以改变值。
3.函数的声明:
若函数为库函数,引用头文件即可,若为自定义函数,且写在主函数前面,则直接调用即可;若写在了主函数后面,则需要在主函数前声明一下,声明即为复制定义函数时除函数体的剩余部分,且加上‘;’即可在主函数调用了。
函数必须先定义或声明后使用。
4.函数的嵌套调用,即在一个函数中调用另外的函数,函数的定义是相互平行、独立的,在定义函数时,一个函数内不能再定义另一个函数,也就是说,函数不能嵌套定义。但是函数可以嵌套调用,也就是再调用一个函数的过程中,又调用另一个函数。
5.函数的递归调用:
eg://递归求1~n的和
int function(int n)
{if(0>=n)return n; //结束递归的条件
return n+function(n-1); //这里在函数中调用此函数本身,实现递归
}
6。局部变量和全局变量:
一般在函数外面就是全局变量,在大括号内定义的就是局部变量,函数的形参也是局部变量
7.静态变量与动态变量:
程序中所定义的局部变量默认是动态存储的。在定义变量前加一个static可定义一个静态变量。静态变量在程序开始后定义,结束前才会被释放,所以其生命周期比较长,在函数调用中只会被定义一次,不会被定义多次,当再次执行到定义语句时,其值不会被重置(初始化),会保留上次改变的值。如:
#include
void fun(){
static int n = 1; //静态变量:重复执行定义语句时,不会被重新定义和初始化
printf("%d\n",n++);}
int main(){
for(int i=0;i<6;i++)
fun();
return 0;
}
输出结果为1,2,3,4,5,6
静态变量用法和普通变量一样,只是不能重新定义
8.在头文件中是声明函数的,不能在头文件中定义变量,因为这样可能会出现有时报错有时不报错的问题
补充:
`system("pause")` 是一个在 C 和 C++ 中常见的用法,它用于在程序执行到该语句时暂停程序的运行,等待用户按下任意键继续执行。这个用法通常在命令行环境下使用,以防止程序在执行完毕后立即关闭窗口而导致无法看到程序输出的结果。
`system("pause")` 会调用操作系统的命令行解释器执行命令 `"pause"`,该命令会在命令行窗口中显示一条提示信息,等待用户按下任意键后窗口才会关闭。这样可以让你在程序执行完毕后,暂停程序的运行,以便查看输出结果。
需要注意的是,`system("pause")` 在不同的操作系统和编译器中的行为可能会有所不同。在某些操作系统或编译器中,可能没有可用的 `pause` 命令,或者 `pause` 命令的行为不同,因此在跨平台开发时需要注意这一点。
如果你想在程序中暂停执行,等待用户按下任意键后继续执行,但又希望保持跨平台的兼容性,可以考虑使用其他方式来实现,例如等待用户输入回车键。以下是一个示例:
```c
#include
int main() {
printf("Press Enter to continue...");
getchar(); // 等待用户按下回车键
return 0;
}
```
这样的代码可以在大多数操作系统和编译器中正常工作,并且具有更好的可移植性。