c语言基础知识

ASCII码

字符A~Z的ASCII码值从65~90
• 字符a~z的ASCII码值从97~122
• 对应的⼤⼩写字符(a和A)的ASCII码值的差值32
• 数字字符0~9的ASCII码值从48~57
• 换⾏ \n 的ASCII值是:10
• 在这些字符中ASCII码值从0~31这32个字符是不可打印字符,⽆法打印在屏幕上观察

ASCII码表

字符串和\0(双引号括起来的⼀串字符)

对于字符串"abcdef",我们实际上看到了6个字符:a,b,c,d,e,f,但是实际上在末尾还隐藏⼀个 \0 的转义字符, \0 是字符串的结束标志。所以我们在使⽤库函数 printf() 打印字符串或者
strlen() 计算字符串⻓度的时候,遇到 \0 的时候就⾃动停⽌

#include <stdio.h>
int main()
{
char arr1[] = {'a', 'b', 'c', '\0'};
char arr2[] = "abc";
char arr3[] = {'a', 'b', 'c', '\0'};
printf("%s\n", arr1);//abc
printf("%s\n", arr2);//abc
printf("%s\n", arr3);//abc烫烫烫?^&%*
printf("%s\n", "abc\0def");
return 0;
}

 转义字符

\? :在书写连续多个问号时使⽤,防⽌他们被解析成三字⺟词,在新的编译器上没法验证
• \' :⽤于表⽰字符常量'
• \" :⽤于表⽰⼀个字符串内部的双引号
• \\ :⽤于表⽰⼀个反斜杠,防⽌它被解释为⼀个转义序列符。
• \a :警报,这会使得终端发出警报声或出现闪烁,或者两者同时发⽣。
• \b :退格键,光标回退⼀个字符,但不删除字符。
• \f :换⻚符,光标移到下⼀⻚。在现代系统上,这已经反映不出来了,⾏为改成类似于 \v 
• \n :换⾏符。
• \r :回⻋符,光标移到同⼀⾏的开头。
• \t :制表符,光标移到下⼀个⽔平制表位,通常是下⼀个8的倍数。
• \v :垂直分隔符,光标移到下⼀个垂直制表位,通常是下⼀⾏的同⼀列。
• \ddd :ddd表⽰1~3个⼋进制的数字。如:\130表⽰字符X
• \xdd :dd表⽰2个⼗六进制数字。如:\x30表⽰字符0
\0 :null字符,代表没有内容, \0 就是 \ddd 这类转义字符的⼀种,⽤于字符串的结束标志,其ASCII码值是0.

注释会被替换

编译时,注释会被替换成⼀个空格,所以 min/* 这⾥是注释*/Value 会变成 min Value ,⽽不
是 minValue

数据类型

 字符型                              

char                               
[signed] char
unsigned char

整型
//短整型
short [int]
[signed] short [int]
unsigned short [int]
//整型
int
[signed] int
unsigned int
//⻓整型
long [int]
[signed] long [int]
unsigned long [int]
//更⻓的整型
//C99中引⼊

long long [int]
[signed] long long [int]
unsigned long long [int]

浮点型
float(单精度浮点)
double(双精度浮点)
long double(精度更高的浮点型)

布尔类型
Bool
头⽂件 <stdbool.h>
布尔类型变量的取值是:true或者false

sizeof操作符

⽤来计算sizeof操作符数的类型⻓度的,单位是字节

sizeof( 类型 )
sizeof 表达式            

--------sizeof 后边的表达式是不真实参与运算的,根据表达式的类型来得出⼤⼩
--------size_t 类型(无符号整数,打印时用%zd)

sizeof 运算符的返回值,C语⾔只规定是⽆符号整数,并没有规定具体的类型,⽽是留给
系统⾃⼰去决定

sizeof中表达式不计算(sizeof 在代码进⾏编译的时候,就根据表达式的类型确定了,类型的常⽤,⽽表达式的执⾏却要在程序运⾏期间才能执⾏,在编译期间已经将sizeof处理掉了,所以在运⾏期间就不会执⾏表达式了)

printf("%zd\n", sizeof(3 + 3.5));//8

short s = 2;
int b = 10;
printf("%d\n", sizeof(s = b+1));//2    字节存储在short被截断

#include <stdio.h>
int main()
{printf("%zd\n", sizeof(char));//1printf("%zd\n", sizeof(_Bool));//1printf("%zd\n", sizeof(short));//2printf("%zd\n", sizeof(int));//4printf("%zd\n", sizeof(long));//4printf("%zd\n", sizeof(long long));//8printf("%zd\n", sizeof(float));//4printf("%zd\n", sizeof(double));//8printf("%zd\n", sizeof(long double));//8return 0;
}

signed和unsigned

C语⾔使⽤ signed 和 unsigned 关键字修饰字符型和整型类型的。
signed 关键字,表⽰⼀个类型带有正负号,包含负值;
unsigned 关键字,表⽰该类型不带有正负号,只能表⽰零和正整数。
对于 int 类型,默认是带有正负号的,也就是说 int 等同于 signed int 。
由于这是默认情况,关键字 signed ⼀般都省略不写,但是写了也不算错

int 类型也可以不带正负号,只表⽰⾮负整数。这时就必须使⽤关键字 unsigned 声明变量
整数变量声明为 unsigned 的好处是,同样⻓度的内存能够表⽰的最⼤整数值,增⼤了⼀倍
C语⾔规定 char 类型默认是否带有正负号,由当前系统决定。
这就是说, char 不等同于 signed char ,它有可能是 signed char ,也有可能是
unsigned char 。
这⼀点与 int 不同, int 就是等同于 signed int

全局变量:在⼤括号外部定义的变量就是全局变量
        全局变量的使⽤范围更⼴,整个⼯程中想使⽤,都是有办法使⽤的。
局部变量:在⼤括号内部定义的变量就是局部变量
        局部变量的使⽤范围是⽐较局限,只能在⾃⼰所在的局部范围内使⽤的

1.局部变量是放在内存的栈区
2. 全局变量是放在内存的静态区
3. 堆区是⽤来动态内存管理的

 % (计算时的正负号)

返回两个整数相除的余值。这个运算符只能⽤于整数,不能⽤于浮点数

负数求模的规则是,结果的正负号由第⼀个运算数的正负号决定

#include <stdio.h>
int main()
{
printf("%d\n", 11 % -5); // 1
printf("%d\n",-11 % -5); // -1
printf("%d\n",-11 % 5); // -1
return 0;
}

 占位符

• %a :⼗六进制浮点数,字⺟输出为⼩写。
• %A :⼗六进制浮点数,字⺟输出为⼤写。
• %c :字符。
• %d :⼗进制整数。
• %e :使⽤科学计数法的浮点数,指数部分的 e 为⼩写。
• %E :使⽤科学计数法的浮点数,指数部分的 E 为⼤写

• %i :整数,基本等同于 %d 。
• %f :⼩数(包含 float 类型和 double 类型)。
• %g :6个有效数字的浮点数。整数部分⼀旦超过6位,就会⾃动转为科学计数法,指数部分的 e
为⼩写。
• %G :等同于 %g ,唯⼀的区别是指数部分的 E 为⼤写。
• %hd :⼗进制shortint类型。
• %ho :⼋进制shortint类型。
• %hx :⼗六进制shortint类型。
• %hu :unsignedshortint类型。
• %ld :⼗进制longint类型。
• %lo :⼋进制longint类型。
• %lx :⼗六进制longint类型。
• %lu :unsignedlongint类型。
• %lld :⼗进制longlongint类型。
• %llo :⼋进制longlongint类型。
• %llx :⼗六进制longlongint类型。
• %llu :unsignedlonglongint类型。
• %Le :科学计数法表⽰的longdouble类型浮点数。
• %Lf :longdouble类型浮点数。
• %n :已输出的字符串数量。该占位符本⾝不输出,只将值存储在指定变量之中。
• %o :⼋进制整数。
• %p :指针。
• %s :字符串。
• %u :⽆符号整数(unsignedint)。
• %x :⼗六进制整数。
• %zd : size_t 类型。
• %% :输出⼀个百分号。

输出格式(限定宽度)

允许限定占位符的最⼩宽度

#include <stdio.h>
int main()
{
printf("%5d\n", 123); // 输出为 " 123"
return 0;
}//%5d 表⽰这个占位符的宽度⾄少为5位。如果不满5位,对应的值的前⾯会添加空格。输出的值默认是右对⻬,即输出内容前⾯会有空格;如果希望改成左对⻬,在输出内容后⾯添加空格,可以在占位符的 % 的后⾯插⼊⼀个-号printf("%-5d\n", 123); // 输出为 "123 "

 对于⼩数,这个限定符会限制所有数字的最⼩显⽰宽度 

// 输出 " 123.450000"
#include <stdio.h>
int main()
{
printf("%12f\n", 123.45);
return 0;
}//%12f 表⽰输出的浮点数最少要占据12位。由于⼩数的默认显⽰精度是⼩数点后6位,所以 123.45 输出结果的头部会添加2个空格

 显示正负号

printf() 不对正数显⽰ + 号,只对负数显⽰ - 号。如果想让正数也输出 + 号,可
以在占位符的 % 后⾯加⼀个 +

#include <stdio.h>
int main()
{
printf("%+d\n", 12); // 输出 +12
printf("%+d\n", -12); // 输出 -12
return 0;
}

 限定小数位数

⼩数点后⾯只保留两位,占位符可以写成 %.2f

最⼩宽度和⼩数位数这两个限定值,都可以⽤ * 代替,通过 printf() 的参数传⼊

#include <stdio.h>
int main()
{
printf("%*.*f\n", 6, 2, 0.5);
return 0;
}
// 等同于printf("%6.2f\n", 0.5);

输出部分字符串

%s 占位符⽤来输出字符串,默认是全部输出。如果只想输出开头的部分,可以⽤ %.[m]s 指定输出的⻓度,其中 [m] 代表⼀个数字,表⽰所要输出的⻓度(占位符 %.5s 表⽰只输出字符串“hello world”的前5个字符,即“hello”)

scanf

scanf() 处理数值占位符时,会⾃动过滤空⽩字符,包括空格、制表符、换⾏符等

⼀个或多个空格不影响 scanf() 解读数据
解读⽤⼾输⼊时,会从上⼀次解读遗留的第⼀个字符开始,直到读完缓存,或者遇到第⼀个不符合条件的字符为⽌
scanf() 的返回值是⼀个整数,表⽰成功读取的变量个数。
如果没有读取任何项,或者匹配失败,则返回 0 。如果在成功读取任何数据之前,发⽣了读取错误或者遇到读取到⽂件结尾,则返回常量EOF

占位符

• %c :字符
• %d :整数。
• %f : float 类型浮点数。
• %lf : double 类型浮点数。
• %Lf : long double 类型浮点数。
• %s :字符串。
• %[] :在⽅括号中指定⼀组匹配的字符(⽐如 %[0-9] ),遇到不在集合之中的字符,匹配将会停⽌。

上⾯所有占位符之中,除了 %c 以外,都会⾃动忽略起⾸的空⽩字符。 %c 不忽略空⽩字符,总是返回当前第⼀个字符,⽆论该字符是否为空格。
如果要强制跳过字符前的空⽩字符,可以写成 scanf(" %c", &ch) ,即 %c 前加上⼀个空格,表⽰跳过零个或多个空⽩字符。
下⾯要特别说⼀下占位符 %s ,它其实不能简单地等同于字符串。它的规则是,从当前第⼀个⾮空⽩字符开始读起,直到遇到空⽩字符(即空格、换⾏符、制表符等)为⽌。
因为 %s 不会包含空⽩字符,所以⽆法⽤来读取多个单词,除⾮多个 %s ⼀起使⽤。这也味着,scanf() 不适合读取可能包含空格的字符串,⽐如书名或歌曲名。另外, scanf() 遇到 %s 占位符,会在字符串变量末尾存储⼀个空字符 \0 。
scanf() 将字符串读⼊字符数组时,不会检测字符串是否超过了数组⻓度。所以,储存字符串时,很可能会超过数组的边界,导致预想不到的结果。为了防⽌这种情况,使⽤ %s 占位符时,应该指定读⼊字符串的最⻓⻓度,即写成 %[m]s ,其中的 [m] 是⼀个整数,表⽰读取符串的最⼤⻓度,后⾯的字符将被丢弃
 

赋值忽略符

#include <stdio.h>
int main()
{
int year = 0;
int month = 0;
int day = 0;
scanf("%d-%d-%d", &year, &month, &day);
printf("%d %d %d\n", year, month, day);
return 0;
}//如果⽤⼾输⼊ 2020-01-01 ,就会正确解读出年、⽉、⽇。问题是⽤⼾可能输⼊其他格式,⽐如 2020/01/01 ,这种情况下, scanf() 解析数据就会失败//只要把 * 加在任何占位符的百分号后⾯,该占位符就不会返回值,解析后将被丢弃
//scanf("%d%*c%d%*c%d", &year, &month, &day);

分支语句

if语句

如果表达式的结果为真,则语句执行。


在C语言中如何表示真假?
0表示假,非0表示真。

语法结构:
if(表达式) 语句;
if(表达式) 语句1;
else语句2;//多分支
if(表达式1)语句1;
else if(表达式2)语句2;
else语句3;//如果条件成立,要执行多条语句
#include <stdio.h>
int main()
{
if(表达式)
{
语句列表1;
}
else
{
语句列表2;
}
return 0;
}
#include <stdio.h>
int main()
{
int age = 0;
scanf("%d", &age);
if(age<18)
{
printf("少年\n");
}
else if(age>=18 && age<30)
{
printf("青年\n");
}
else if(age>=30 && age<50)
{
printf("中年\n");
}
else if(age>=50 && age<80)
{
printf("老年\n");
}
else
{
printf("老寿星\n");
}
}

悬空else(else是和它离的最近的if匹配的)

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{int a = 0;int b = 2;if (a == 1)//当满足该条件才会进入下一个if语句if (b == 2)printf("hehe\n");elseprintf("haha\n");return 0;
}//改进:加大括号
#include <stdio.h>
int main()
{int a = 0;int b = 2;
if(a == 1)
{if(b == 2){printf("hehe\n");}
}
else
{printf("haha\n");
}return 0;
}

练习
1. 判断一个数是否为奇数

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{size_t n = 0;scanf("%zd", &n);if (n % 2 == 0){printf("%zd是偶数", n);}else{printf("%zd是奇数", n);}
}

2. 输出1-100之间的奇数 

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{for (int i = 1; i <= 100; i++){if (i % 2 != 0){printf("%d ", i);}}
}

switch语句

switch(整型表达式)
{case 整形常量表达式:语句;
}break语句 的实际效果是把语句列表划分为不同的分支部分
在最后一个 case 语句的后面加上一条 break语句。
(之所以这么写是可以避免出现在以前的最后一个 case 语句后面忘了添加 break语句)不想忽略不匹配所有标签的表达式的值时该怎么办呢?
你可以在语句列表中增加一条default子句,每个switch语句中只能出现一条default子句
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{int day = 0;switch (day){case 1:printf("星期一\n");break;case 2:printf("星期二\n");break;case 3:printf("星期三\n");break;case 4:printf("星期四\n");break;case 5:printf("星期五\n");break;case 6:printf("星期六\n");break;case 7:printf("星期天\n");break;}return 0;
}//改进
//1. 输入1-5,输出的是“weekday”;
//2. 输入6-7,输出“weekend”#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{int day = 0;switch (day){case 1:case 2:case 3:case 4:case 5:printf("weekday\n");break;case 6:case 7:printf("weekend\n");break;}return 0;
}

循环语句


while

while(表达式)
循环语句;
#include <stdio.h>
int main()
{
int i = 1;
while(i<=10)
{
printf("%d ", i);//1 2 3 4 5 6 7 8 9 10
i = i+1;
}
return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{int n = 0;scanf("%d", &n);//521while (n){printf("%d ", n % 10);//1 2 5n = n / 10;}return 0;
}

while语句中的break和continue

break在while循环中的作用:
其实在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。
所以:while中的break是用于永久终止循环的continue在while循环中的作用就是:
continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行,
而是直接跳转到while语句的判断部分。进行下一次循环的入口判断

for循环

for(表达式1; 表达式2; 表达式3)
语句;//如果循环体想包含更多的语句,可以加上⼤括号//表达式1⽤于循环变量的初始化
//表达式2⽤于循环结束条件的判断
//表达式3⽤于循环变量的调整
#include <stdio.h>
int main()
{int i = 0;for(i=1; i<=10; i++){printf("%d ", i);}return 0;
}

do-while循环

do
语句;
while(表达式);
#include <stdio.h>
int main()
{int i = 1;do{printf("%d ", i);i = i + 1;}while(i<=10);return 0;
}

break和continue语句

break 的作⽤是⽤于永久的终⽌循环,只要 break 被执⾏,直接就会跳出循环,继续往后执

continue 的作⽤是跳过本次循环 continue 后边的代码,在 for 循环和 while 循环中有所
差异的

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{int i = 1;while (i <= 10){if (i == 5)break;//当i等于5后,就执⾏break,循环就终⽌了printf("%d ", i);i = i + 1;}return 0;
}

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{int i = 1;while (i <= 10){if (i == 5)continue;//当i等于5后,就执⾏continue,直接跳过continue的代码,去循环的判断的地⽅//因为这⾥跳过了i = i+1,所以i⼀直为5,程序陷⼊和死循环printf("%d ", i);i = i + 1;}return 0;
}

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{int i = 1;for (i = 1; i <= 10; i++){if (i == 5)break;printf("%d ", i);}return 0;
}

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{int i = 1;for (i = 1; i <= 10; i++){if (i == 5)continue;//这⾥continue跳过了后边的打印,来到了i++的调整部分printf("%d ", i);}return 0;
}

for循环嵌套

判断是否为素数

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{for (int i = 100; i <= 200; i++){int flag = 1;//标记是否为素数for (int j = 2; j < i; j++){if (i % j == 0) {flag = 0;break;//能被除1和自身之外的数整除,不是素数}}if (flag == 1) printf("%d ", i);}
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include <math.h>
//sqrt- 库函数- 开平方的
int main()
{//产生100~200之间的数int i = 0;int count = 0;for (i = 101; i <= 200; i += 2){int flag = 1;//假设i是素数//产生的i就是100到200之间的数字//每次循环进来产生一个i,这个时候判断i是否是素数//方法是:产生2~i-1之间的数字,去试除iint j = 0;for (j = 2; j <= sqrt(i); j++){if (i % j == 0){flag = 0;//表示i不是素数break;}}if (flag == 1){printf("%d ", i);count++;}}printf("\ncount = %d\n", count);return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{int i = 0;int j = 0;for (i=0; i < 5; i++){for (j=0; j < 5; j++){printf("hehe\n");}}return 0;
}

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{int i = 0;int j = 0;for (; i < 5; i++){for (; j < 5; j++)//i=0时,进入第二层循环,当j=5跳出循环,当i=1时,j仍然为5,进入不了第 //二层循环{printf("hehe\n");}}return 0;
}

goto语句

goto 语句可以实现在同⼀个函数内跳转到设置好的标号处

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{printf("hehe\n");goto next;printf("haha\n");
next:printf("跳过了haha的打印\n");return 0;
}

猜数字游戏

rand

头⽂件是:stdlib.h

C语⾔提供了⼀个函数叫rand,这函数是可以⽣成随机数的
int rand (void);

rand函数会返回⼀个伪随机数,这个随机数的范围是在0~RAND_MAX之间,这个RAND_MAX的⼤⼩是依赖编译器上实现的,但是⼤部分编译器上是32767

#include <stdio.h>
#include <stdlib.h>
int main()
{printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());return 0;
}

rand函数⽣成的随机数是伪随机的,伪随机数不是真正的随机数,是通过某种算法⽣成的随机数。真正的随机数的是⽆法预测下⼀个值是多少的。⽽rand函数是对⼀个叫“种⼦”的基准值进⾏运算⽣成的随机数,之所以前⾯每次运⾏程序产⽣的随机数序列是⼀样的,那是因为rand函数⽣成随机数的默认种⼦是1。如果要⽣成不同的随机数,就要让种⼦是变化的(故srand)

srand

⽤来初始化随机数的⽣成器的
void srand (unsigned int seed);
程序中在调⽤rand函数之前先调⽤srand函数,通过srand函数的参数seed来设置rand函数⽣成随机数的时候的种⼦,只要种⼦在变化,每次⽣成的随机数序列也就变化起来了
给srand的种⼦是如果是随机的,rand就能⽣成随机数;在⽣成随机数的时候⼜需要⼀个
机数
,这就⽭盾了(故引申出时间time)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{//使⽤time函数的返回值设置种⼦//因为srand的参数是unsigned int类型,我们将time函数的返回值强制类型转换srand((unsigned int)time(NULL));printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());printf("%d\n", rand());return 0;
}

 

time

头⽂件:time.h

程序中我们⼀般是使⽤程序运⾏的时间作为种⼦的,因为时间时刻在发⽣变化的
time_t time (time_t* timer);
time函数会返回当前的⽇历时间,其实返回的是1970年1⽉1⽇0时0分0秒到现在程序运⾏时间之间的差值,单位是秒。返回的类型是time_t类型的,time_t类型本质上其实就是32位或者64位的整型类型。
time函数的参数timer如果是⾮NULL的指针的话,函数也会将这个返回的差值放在timer指向的内存中带回去。
如果timer是NULL,就只返回这个时间的差值。time函数返回的这个时间差也被叫做:时间戳(time(NULL);//调⽤time函数返回时间戳,这⾥没有接收返回值)

设置随机数的范围

rand() %100;//余数的范围是0~99
rand()%100+1;//%100的余数是0~99,0~99的数字+1,范围是1~100
100 + rand()%(200-100+1)//余数的范围是0~100,加100后就是100~200

a + rand()%(b-a+1)//⽣成a~b的随机数

#include <stdlib.h>
#include <time.h>//函数
void menu()
{printf("************************\n");printf("******  1. play  *******\n");printf("******  0. exit  *******\n");printf("************************\n");
}//猜数字游戏的实现
void game()
{//1. 生成随机数(1~100)int ret = rand() % 100 + 1;//1~100//n%100 余数的取值的范围 0~99//2. 猜数字int guess = 0;int count = 5;while (count){printf("请猜数字:>");scanf("%d", &guess);if (guess < ret){printf("猜小了\n");}else if (guess > ret){printf("猜大了\n");}else{printf("恭喜你,猜对了\n");break;}count--;}if (count == 0){printf("猜失败了,正确的数字是:%d\n", ret);}
}int main()
{int input = 0;srand((unsigned int)time(NULL));//种子设置只需要一次do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:game();break;case 0:printf("退出游戏\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}

 数组

数组是⼀组相同类型元素的集合
数组中存放的是1个或者多个数据,但是数组元素个数不能为0
数组中存放的多个数据,类型是相同的

type arr_name[常量值];
存放在数组的值被称为数组的元素,数组在创建的时候可以指定数组的⼤⼩和数组的元素类型

初始化

//完全初始化
int arr[5] = {1,2,3,4,5};
//不完全初始化
int arr2[6] = {1};//第⼀个元素初始化为1,剩余的元素默认初始化为0
//错误的初始化 - 初始化项太多
int arr3[3] = {1, 2, 3, 4};

 ⼀维数组在内存中的存储

#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int i = 0;for (i = 0; i < 10; i++){printf("&arr[%d] = %p\n ", i, &arr[i]);}return 0;
}

数组随着下标的增⻓,地址是由⼩到⼤变化的,并且我们发现每两个相邻的元素之间相差4(因为⼀个整型是4个字节)。数组在内存中是连续存放的

sizeof计算数组元素个数(计算类型或者变量⼤⼩,计算数组的⼤⼩)

#include <stdio.h>
int main()
{int arr[10] = { 0 };printf("%d\n", sizeof(arr));return 0;
}
//输出的结果是40,计算的是数组所占内存空间的总⼤⼩,单位是字节

#include <stdio.h>
int main()
{int arr[10] = { 0 };printf("%d\n", sizeof(arr[0]));//计算⼀个元素的⼤⼩,单位是字节return 0;
}

#include <stdio.h>
int main()
{int arr[10] = { 0 };int sz = sizeof(arr) / sizeof(arr[0]);printf("%d\n", sz);return 0;
}

二维数组

type arr_name[常量值1][常量值2];

初始化

//不完全初始化

int arr1[3][5] = {1,2};
int arr2[3][5] = {0};
 

//完全初始化

int arr3[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
 

//按照⾏初始化

int arr4[3][5] = {{1,2},{3,4},{5,6}};
 

//初始化时省略⾏,但是不能省略列

int arr5[][5] = {1,2,3};
int arr6[][5] = {1,2,3,4,5,6,7};
int arr7[][5] = {{1,2}, {3,4}, {5,6}};
 

⼆维数组在内存中的存储

#include <stdio.h>
int main()
{int arr[3][5] = { 0 };int i = 0;int j = 0;for (i = 0; i < 3; i++){for (j = 0; j < 5; j++){printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);}}return 0;
}

每⼀⾏内部的每个元素都是相邻的,地址之间相差4个字节,跨⾏位置处的两个元素(如:arr[0][4]和arr[1][0])之间也是差4个字节,所以⼆维数组中的每个元素都是连续存放的

C99中的变⻓数组

在C99标准之前,C语⾔在创建数组的时候,数组⼤⼩的指定只能使⽤常量、常量表达式,或者如果我们初始化数据的话,可以省略数组⼤⼩。

int arr1[10];
int arr2[3+5];
int arr3[] = {1,2,3};

C99中给⼀个变⻓数组(variable-lengtharray,简称VLA)的新特性,允许我们可以使⽤变量指定数组⼤⼩

int n = a+b;
int arr[n];//数组 arr 就是变⻓数组,因为它的⻓度取决于变量 n 的值,编译器没法事先确定,只
有运⾏时才能知道 n 是多少
//变⻓数组的根本特征,就是数组⻓度只有运⾏时才能确定,所以变⻓数组不能初始化。它的好处是程
序员不必在开发时,随意为数组指定⼀个估计的⻓度,程序可以在运⾏时为数组分配精确的⻓度。有
⼀个⽐较迷惑的点,变⻓数组的意思是数组的⼤⼩是可以使⽤变量来指定的,在程序运⾏的时候,根
据变量的⼤⼩来指定数组的元素个数,⽽不是说数组的⼤⼩是可变的。数组的⼤⼩⼀旦确定就不能再
变化了
//VS2022上,虽然⽀持⼤部分C99的语法,没有⽀持C99中的变⻓数组,没法测试;
#include <stdio.h>
int main()
{int n = 0;scanf("%d", &n);//根据输⼊数值确定数组的⼤⼩int arr[n];int i = 0;for (i = 0; i < n; i++){scanf("%d", &arr[i]);}for (i = 0; i < n; i++){printf("%d ", arr[i]);}return 0;
}//gcc

数组练习

练习1:多个字符从两端移动,向中间汇聚
 

#include <stdio.h>
int main()
{char arr1[] = "welcome to bit...";char arr2[] = "#################";int left = 0;int right = strlen(arr1) - 1;printf("%s\n", arr2);while (left <= right){Sleep(1000);arr2[left] = arr1[left];arr2[right] = arr1[right];left++;right--;printf("%s\n", arr2);}return 0;
}

练习2:⼆分查找

#include <stdio.h>
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int left = 0;int right = sizeof(arr) / sizeof(arr[0]) - 1;int key = 7;//要找的数字int mid = 0;//记录中间元素的下标int find = 0;while (left <= right){mid = (left + right) / 2;//mid = left+(right-left)/2;if (arr[mid] > key){right = mid - 1;}else if (arr[mid] < key){left = mid + 1;}else{find = 1;break;}}if (1 == find)printf("找到了,下标是%d\n", mid);elseprintf("找不到\n");
}

函数

库函数

库函数⽂档的⼀般格式
1. 函数原型
2. 函数功能介绍
3. 参数和返回类型说明
4. 代码举例
5. 代码输出
6. 相关知识链接

⾃定义函数

ret_type fun_name(形式参数)
{}//ret_type 是函数返回类型
//fun_name 是函数名
//括号中放的是形式参数
//{}括起来的是函数体
#include <stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{int a = 0;int b = 0;//输⼊scanf("%d %d", &a, &b);//调⽤加法函数,完成a和b的相加//求和的结果放在r中int r = Add(a, b);//输出printf("%d\n", r);return 0;
}

形参和实参

x和y(形参)确实得到了a和b(实参)的值,但是x和y的地址和a和b的地址是不⼀样的,所以形参是实参的⼀份临时拷⻉

形式参数只有在函数被调⽤的过程中为了存放实参传递过来的值,才向内存申请空间,这个过程就是形式的实例化

对形参的修改不会影响实参

return语句

return后边可以是⼀个数值,也可以是⼀个表达式,如果是表达式则先执⾏表达式,再返回表达式的结果
return后边也可以什么都没有,直接写 return;适合函数返回类型是void的情况
return返回的值和函数返回类型不⼀致,系统会⾃动将返回的值隐式转换为函数的返回类型
return语句执⾏后,函数就彻底返回后边的代码不再执⾏
如果函数中存在if等分⽀的语句,则要保证每种情况下都有return返回,否则会出现编译错误

数组做函数参数

• 函数的形式参数要和函数的实参个数匹配
• 函数的实参是数组,形参也是可以写成数组形式
• 形参如果是⼀维数组,数组⼤⼩可以省略不写
• 形参如果是⼆维数组⾏可以省略,但是列不能省略
• 数组传参,形参是不会创建新的数组的
• 形参操作的数组和实参的数组是同⼀个数组​​​​​​​

#include <stdio.h>
void set_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){arr[i] = -1;}
}
void print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int sz = sizeof(arr) / sizeof(arr[0]);set_arr(arr, sz);//设置数组内容为-1print_arr(arr, sz);//打印数组内容return 0;
}

 嵌套调⽤和链式访问

#include <stdio.h>
int is_leap_year(int y)
{if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))return 1;elsereturn 0;
}
int get_days_of_month(int y, int m)
{int days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };int day = days[m];if (is_leap_year(y) && m == 2)day += 1;return day;
}
int main()
{int y = 0;int m = 0;scanf("%d %d", &y, &m);int d = get_days_of_month(y, m);printf("%d\n", d);return 0;
}

链式访问就是将⼀个函数的返回值作为另外⼀个函数的参数,像链条⼀样将函数串起来就是函数的链式访问

#include <stdio.h>
int main()
{printf("%d\n", strlen("abcdef"));//链式访问return 0;
}
#include <stdio.h>
int main()
{printf("%d", printf("%d", printf("%d", 43)));return 0;
}
//4321
//第⼀个printf打印的是第⼆个printf的返回值,第⼆个printf打印的是第三个printf的返回值。
//第三个printf打印43,在屏幕上打印2个字符,再返回2,第⼆个printf打印2,在屏幕上打印1个字符,//再放回1,第⼀个printf打印1

static和extern

static 静态
• 修饰局部变量
• 修饰全局变量
• 修饰函数

extern 是⽤来声明外部符号

作⽤域
限定这个名字的可⽤性的代码范围就是这个名字的作⽤域
1. 局部变量的作⽤域是变量所在的局部范围。
2. 全局变量的作⽤域是整个⼯程(项⽬)


⽣命周期指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的⼀个时间段。
1. 局部变量的⽣命周期是:进⼊作⽤域变量创建,⽣命周期开始,出作⽤域⽣命周期结束。
2. 全局变量的⽣命周期是:整个程序的⽣命周期

static修饰局部变量 

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void test()
{int i = 0;i++;printf("%d ", i);
}
int main()
{int i = 0;for (i = 0; i < 5; i++){test();}return 0;
}

#include <stdio.h>
void test()
{//static修饰局部变量static int i = 0;i++;printf("%d ", i);
}
int main()
{int i = 0;for (i = 0; i < 5; i++){test();}return 0;
}

static修饰全局变量

extern是⽤来声明外部符号的,如果⼀个全局的符号在A⽂件中定义的,在B⽂件中想使⽤,就可以使⽤ extern 进⾏声明,然后使⽤

//add.c
int g_val = 2018;
//test.c
#include <stdio.h>
extern int g_val;
int main()
{printf("%d\n", g_val);return 0;
}

⼀个全局变量被static修饰,使得这个全局变量只能在本源⽂件内使⽤不能在其他源⽂件内使⽤
本质原因是全局变量默认是具有外部链接属性的,在外部的⽂件中想使⽤,只要适当的声明就可以使⽤;但是全局变量被 static 修饰之后外部链接属性就变成了内部链接属性,只能在⾃⼰所在的源⽂件内部使⽤了,其他源⽂件,即使声明了,也是⽆法正常使⽤的。
使⽤建议:如果⼀个全局变量,只想在所在的源⽂件内部使⽤,不想被其他⽂件发现,就可以使⽤static修饰

static修饰函数

本质是因为函数默认是具有外部链接属性,具有外部链接属性,使得函数在整个⼯程中只要适当的声明就可以被使⽤。但是被 static 修饰后变成了内部链接属性,使得函数只能在⾃⼰所在源⽂件内部使⽤。
使⽤建议:⼀个函数只想在所在的源⽂件内部使⽤,不想被其他源⽂件使⽤,就可以使⽤ static 修饰

//add.c
int Add(int x, int y)
{return x+y;
}
//test.c
#include <stdio.h>
extern int Add(int x, int y);
int main()
{printf("%d\n", Add(2, 3));return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/394923.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sql实战cmseasy

环境搭建 这里我们用phpstady搭建 版本是cmseasy5.5 未授权访问 这里ip的方法获取客户端的ip 这里的意思是当你的server ip等于 客户端ip并且get传参 get(ishtml)1的情况下他会直接return 他就不会检查后面是不是admin&#xff0c;而他这个IP是从X_FORWARDED_FOR获取&…

Spring Boot 3.x Rest API统一异常处理最佳实践

上一篇&#xff1a;Spring Boot 3.x Rest API最佳实践之统一响应结构 在Spring MVC应用中&#xff0c;要对web表示层所抛出的异常进行捕获处理有多种方式&#xff0c;具体的可参考著名国外Spring技术实战网站baeldung上的相关话题。Spring Boot对Spring MVC应用中抛出的异常以…

RNN循环网络层

文章目录 1、简介2、RNN 网络原理3、PyTorch RNN 层的使用3.1、RNN送入单个数据3.2、RNN层送入批量数据 4、RNN三个维度4.1、解释4.2、输入数据的组织4.3、示例4.4、为什么需要这种格式&#xff1f;4.5、小结 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&a…

苹果手机数据被抹除还能恢复吗?这两个方法强烈推荐

苹果手机数据被抹除还能恢复吗&#xff1f;我们在使用苹果手机时&#xff0c;有时由于误操作、系统故障或升级失败等原因&#xff0c;导致手机照片、备忘录、视频、联系人等数据被意外抹除。 面对这类情况&#xff0c;我们应该怎么办&#xff1f;下面牛小编给大家的分享2个方法…

记录使用FlinkSql进行实时工作流开发

使用FlinkSql进行实时工作流开发 引言Flink SQL实战常用的Connector1. MySQL-CDC 连接器配置2. Kafka 连接器配置3. JDBC 连接器配置4. RabbitMQ 连接器配置5. REST Lookup 连接器配置6. HDFS 连接器配置 FlinkSql数据类型1. 基本数据类型2. 字符串数据类型3. 日期和时间数据类…

论文解读,神经网络全梯度表示《Full-Gradient Representation for Neural Network Visualization》

导语 这篇论文介绍了一种新的工具&#xff0c;称为全梯度&#xff0c;用于解释神经网络的响应。这个全梯度的概念将神经网络的响应分解为两个部分&#xff1a;输入灵敏度和每个神经元的灵敏度分量。 输入灵敏度&#xff1a;输入灵敏度指的是对于神经网络输出的影响程度。它反…

Python试讲

Python试讲 导语Python简介Python及其特点如何使用Python Python与计算计算变量 导语 本次试讲内容如下&#xff1a;Python简介与使用&#xff0c;Python与基本运算 辅助教材为 《趣学Python编程》和《Python编程从入门到实践》 Python简介 Python是目前入门最简单最好学的…

NSSCTF练习记录:[SWPUCTF 2021 新生赛]jicao

题目&#xff1a; 这段PHP代码的意思是&#xff1a; 对index.php文件进行语法高亮显示&#xff0c;插入flag.php文件&#xff0c;变量id的值为POST传递的值&#xff0c;变量json的值为GET传递的json类型的值。当id值为wllmNB且json中含有键为“x”&#xff0c;值为“wllm”的时…

数据结构:栈与队列OJ题

目录 前言 一、用栈实现队列 二、用队列实现栈 三、括号匹配问题 前言 前面讲了栈和队列的基础知识&#xff0c;今天来巩固一下加深理解&#xff0c;这里说明一下&#xff0c;因为现在都是在用C语言写&#xff0c;这些OJ题里都要用到前面实现栈和队列的代码&#xff0c;每道题…

告别数据丢失烦恼,转转数据恢复和另外三款工具助你一臂之力!

不知道大伙儿有没有和我一样&#xff0c;到哪都喜欢拍照片和视频&#xff0c;加上办公上也是七七八八的各种格式的文件实在是多&#xff0c;所以电脑和手机等等设备上经常内存爆满需要清理&#xff0c;难免会出现不小心误删或者格式化、清空等等的情况&#xff0c;用过几款和转…

Journyx项目管理软件 soap_cgi.pyc XXE漏洞复现

0x01 产品简介 Journyx-Journyx成立于1996年,提供自托管项目管理解决方案ProjectXecute。主要功能包括资源跟踪、待办事项列表、任务分配以及与MS Project的集成。要运行ProjectXecute,需要Windows 2003或更高版本、IIS Web服务器和Intel处理器。也可以在Linux、Solaris、AI…

#子传父父传子props和emits #封装的table #vue3

#子传父&父传子props和emits #封装的table #vue3 父组件&#xff1a;emits defineEmits props 子组件&#xff1a; 子组件 <template><el-table v-bind"$attrs" ref"innerTableRef" v-loading"loading" border :data"tabl…

力扣刷题-轮转数组

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 首先&#xff0c;我们现在这里提供的是一种特别简单的思路&#xff0c;我们先来看一下这段代码&#xff1a; void rotate(int* nums, int numsSize, int k) {k%numsSize;int n…

git clone private repo

Create personal access token Clone repo $ git clone https://<user_name>:<personal_access_tokens>github.com/<user_name>/<repo_name>.git

5个适用于Linux系统的PDF转Word工具

凭借其跨平台和设备的统一标准、兼容性和规模小巧等主要优点&#xff0c;可携带文档格式&#xff08;PDF&#xff09;可谓最主流的文件格式之一。 市面上有许多查看PDF文件的强大工具&#xff0c;因此所有Linux系统的用户都可以根据自身喜好找到合适的PDF查看工具。然而&#x…

Linux从0到1——基础IO(上)【文件描述符/重定向/缓冲区】

Linux从0到1——基础IO&#xff08;上&#xff09; 1. 预备知识2. 复习一下常见的C语言文件接口3. 系统调用接口3.1 函数传参小技巧——标志位3.2 使用系统调用接口3.2.1 open3.2.2 write3.2.3 read 4. 文件描述符fd4.1 fd的本质4.2 理解struct file结构体4.3 fd的分配规则 5. …

BES(恒玄)平台log分析

前言 恒玄软件调试和分析基本是通过日志形式分析的&#xff0c;今天就详细说下日志组成和常用分析方法 1.日志组成解析 bes日志组成一般说由以下组成&#xff1a;tick时钟 模块log打印所在线程编码log内容 [17:31:22.834] 21786/NONE / 2 | CPU USAGE: busy18 light8…

WebStorm格式化JSON,将一行很长的JSON展开

webstorm json格式化插件将一行很长的json展开 在WebStorm中&#xff0c;要展开很长的JSON行&#xff0c;可以使用内置的JSON格式化功能。 打开WebStorm&#xff0c;并打开包含JSON的文件。 选择JSON文件中的任意部分。 按下快捷键 CtrlAltL (Windows/Linux) 或 CmdAltL (Ma…

GPT-4.o mini

https://share.xuzhugpt.cloud/ GPT-4.o mini 目前免费使用 把上面[chatgpt4o-mini-xuzhu]复制到UserToken的文本框中 点击[个人账户] 测试一下哈&#xff0c;看看&#xff1a; GPT-4.o代码有时候还是有严重错误&#xff1a;好奇怎么来的 上面是我写得&#xff0c;下面是GPT写…

01背包问题 c++

题目描述 有一个背包能装的重量maxw(正整数&#xff0c;0≤maxw≤20000)&#xff0c;同时有n件物品(0≤n≤100)(每件物品只有一件&#xff0c;要么拿&#xff0c;要么不拿)&#xff0c;每件物品有一个重量wi(正整数)和一个价值vi(正整数)。要求从这n件物品中任取若干件装入背包…