C语言易错难懂知识

一些基础知识

1.进制

二进制:0b开头

八进制:0开头

十六进制:0X开头(09,AF)

2.float 4字节 有效数字6

double 8字节 有效数字15

不同类型数据运算时,除强制类型转换外,结果都为double型

在一个整数末尾加L/l表示长整型

eg. int b=5;

​ float a=2.0;

​ b=b/2*a; //b=5

3.素数中:不必被2(n-1)整除,只需被2✓n即可,j*j<i

4.a%b结果由a的正负性决定,只要a为负数,结果为负数

5.常量和常变量

常变量:在定义变量时,加一个const

​ eg.const int a=3;

变量:

(1)整型变量 1000

(2)实型变量 12.34e3

(3)字符常量(只能为一个字符,’ \42 ‘为一个字符)

​ 1)普通字符:以ASCII码存储

​ 常见的ASCII:’A’ =65 ‘a’ =97 0 =48

​ 2)转义字符:以’ \ ‘开头,将’ \ ‘后的字符转化为另外意思

​ eg.’ \x41 ‘表示16进制41的ASCII字符,即’ A ‘

​ 3)字符串常量:“boy”

​ 4)符号常量(不能对其赋新值):#define PI 3.1416

6.标识符:只由字母,数字,下划线组成;第一个字符必须是字母或下划线

7.unsigned int =>无符号整型 输出用%u

(充分利用变量的值的范围(数据范围只有正值))

8.逗号表达式结果取后面的值

eg.i=(a=6,a*5),a+6;//>i=30

9.要输出%应该连续使用2个%

10.字符变量

1
2
3
char c=' ? ';
printf("%d %c",c,c);
=>63 ?

11.i++和++i

i=3;

j=i++(i=4,j=3)//先赋值后自加

j=++i(i=4,j=4)//先自加在赋值

12.输出:

用%f得到6位小数

%-m.nf :

​ -号为左对齐

​ m为最小宽度:输出的总宽度(包括小数点、小数部分和整数部分)如果实际数据长度小于 m,将用空格填充;如果数据长度超过 m,则按实际长度输出,不截断

​ n为小数小数位数:如果实际小数位数超过 n,会进行四舍五入;如果不足n,会用0补足

%e指定以指数形式输出实数,自动给出数字部分小数位数为6位,指数部分占5列

eg.1.234560 e+002

13.运算符优先级

14.字符(一个)输入输出:

getchar putchar(c)

15.在判断一个量是否为真时,0为假,非0为真

a&&b//a和b都非0(只有a为真时,才需要判别b的值)

a||b//a和b至少一个非0(只有a为假时才需要判别b的值)

16.判断非闰年:

!((year%4==0&&year%100!=0)||year%400==0)

17.两个数乘积=两个数最大公约数 x 最小公倍数

选择结构

?:表达式(代替复杂的if)

max=(a>b)?a:b;//a>b为真的话,max=a,否则为b

switch

switch(表达式){

​ case 常量1:语句1;

​ ……

​ default:……;

}

循环结构

while语句

只要循环条件表达式为真时,就执行

eg.while(i–)//每次循环开始先检查i当前值是否为真,若为真,循环继续,每次循环结束时i值减少一

do while语句

先无条件执行,再判断循环条件是否成立

for语句

for(…;…[只要值为真,就执行循环];…)

break和continue

break:提前终止循环

continue:提前结束本次循环,接着执行下次循环

数组

a[3][4] =>3行4列

字符数组

字符串结束标志

一个字符串以字符'\0'作为结束标志(c语言在字符数组储存字符串常量市会自动加一个’\0’作结束符)

字符数组的输入输出

%s =>整个字符串

%c =>一个字符

输出时遇到’\0’就停止输出

scanf输入项如果是字符数组名,不加&

scanf读取字符串时,会在遇空白字符时停止读取

单行字符串
1
2
3
4
while((ch=getchar())!='\n'){
a[i]=ch;
i++;
}
1
2
3
4
char string[100];
char c;
gets(string);
for(int i=0;(c=string[i])!='\0';i++){···}\\遍历字符串中每个字符
n行字符串
1
2
3
4
5
6
7
scanf("%d",&n);
getchar();
char a[100];
for(int i=0;i<n;i++){
gets(a);
···
}
1
2
3
4
5
6
7
scanf("%d",&n);
getchar();
char string[n][100];
for(int i=0;i<n;i++){
gets(string[i]);
for(int j=0;string[i][j]!='\0';j++){···}
}

字符串处理函数

记得加#include<string.h>

puts

输出的字符串中可以包含转义字符

e.g

1
2
3
4
char str[]={"abc \n def"};
puts(str);
=>abc
def
gets

从终端输入一个字符串到字符数组,并且得到一个函数值,该函数值是字符数组的起始地址

注:gets和puts函数只能输入/输出一个字符串

strcat

把两个字符数组中字符串连接起来

strcat(str1,str2);

strcpy和strncpy

字符串复制函数

strcpy

将字符串2复制到字符数组1中

注:赋值语句只能将一个字符赋给一个字符型变量/字符数组元素

strncpy

将字符串2中前n个字符复制到字符数组1中

strncpy(str1,str2,2);//将str2前2个取代str1原有的2个

strcmp

字符串比较:2个字符串从左到右逐个比较(ASCII值),直到出现不同字符或遇到’\0’为止

字符串1=字符串2 函数值为0

字符串1>字符串2 函数值为正整数

字符串1<字符串2 函数值为负整数

strlen

测字符串长度

strlwr

转换为小写

strupr

转换为大写

<ctype.h>头文件

isalpha判断一个字符是否是字母

isdigit判断一个字符是否是数字

islower判断一个字符是否是小写字母

isupper判断一个字符是否是大写字母

isspace判断一个字符是否是空白字符

tolower将字符大写变小写

toupper将字符小写变大写

函数

函数的定义与声明

在定义函数时要指定函数的类型

函数类型决定返回值类型

用函数原型(函数首部)作函数声明

e.g int add(int a,int b);

函数的嵌套

1
2
3
4
5
6
7
int max4(int a,int b,int c,int d){
int max2(int a,int b);
return max2(max2(max2(a,b),c),d);
}
int max2(int a,int b){
return(a>=b?a:b);
}

局部与全局变量

注:全局变量开头一般用大写字母,来区别局部变量

static

静态局部/全局变量

函数中局部变量的值在函数调用结束后不消失而继续保留本次调用结束后的值

e.g static int f=1;

extern

外部变量声明

在一个文件内扩展外部变量的作用域

将外部变量的作用域扩展到其他文件

e.g extern A,B,C; \ \可省略int等类型

指针

指针变量

指针是一个地址,而指针变量是存放地址的变量

指针变量中只能存放地址(指针),不要将一个整数给指针变量

p=&a;

*p=1;\ \将1赋值给p所指向的变量即把1赋给a

定义:类型名 * 指针变量名

​ int* point;

​ point=&a;\ \等价于int* point=&a;

函数调用时不加*

e.g swap(point_1,point_2);

为使在函数中改变的变量能被main所用,应该用指针变量做函数参数

e.g

1
2
3
4
5
6
void swap(int *p1,int *p2){
int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}

通过指针引用数组

一维数组

1
2
int *p;
p=&a[0];\\不能写成*p=&a[0];
1
2
p=&a[0];
p=a;\\p的值是数组a的首元素

如果指针元素p指向数组中的一个元素,则p+1指向同一数组的下个元素,p-1指向同一数组的上个元素

如果p的初值为&a[0],则p+i/a+i即a[i]的地址,*(p+i)/ *(a+i)是p+i/a+i所指向的数组元素即a[i]

两个地址不能相加

若指针变量p1、p2都指向同一数组中的元素,p2-p1结果为(p2-p1)的值(地址之差)

1
2
3
4
5
int i,a[10];
int* p=a;\\p初值是a,p指向a[0]
for(i=0;i<10;i++)scanf("%d",p++);
p=a;\\重新使p指向a[0]
for(i=0;i<10;i++,p++)printf("%d",*p);

若a[0]=3,++(*p) =>a[0]=4

fun(int arr[],int n) = fun(int* arr,int n)

二维数组

*(a[i]+j)*(*(a+i)+j)a[i][j]的值

对于a是二维数组,则a[i]是一位数组名,他是一个地址,并不代表一个储存单元/其中的值

int (*p)[4]定义包含4个元素的一维数组的指针变量

e.g 有一个二维数组int arr[3][4],可用int (*p)[4]来声明一个指针p指向这个二维数组某一行

1
2
int arr[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
int (*p)[4]=&arr[0];\\p指向arr某一行

通过指针引用字符串

通过字符数组名/字符指针变量可以输出一个字符串

1
2
3
char* a,str[10];
a=str;
scanf("%s",a);

数组名虽代表地址,但它是常量,其值不可改变

字符指针变量指向的字符串常量中的内容是不可被取代

指向函数的指针

函数名是一个指针(地址)

1
2
3
int (*p)(int,int)
p=max;\\将函数入口地址赋给p
c=(*p)(a,b);\\用*p代替函数名

char* p[6]包含6个指针的数值

用户自己建立数据类型

结构体

struct 结构体名{

​ 成员表列(类型名 成员名);

};

引用结构体变量中成员的值:结构体变量名.成员名 e.gstudent1.num=10010;

不能通过输出结构体变量名来达到输出结构体变量所有成员的值

若p指向一个结构体变量stu

则 stu.num <=> (*p).num <=> p->num

结构体数组读入不用加&

共用体

union 共用体名{

​ 成员表列(类型名 成员名);

};

typedef声明新类型名

C语言经典例题

统计一个字符串中每个单词的字符数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<stdio.h>
int main(){
char c;
int count=0;\\用于统计当前单词的字符数
int first=1;\\用于标记是否是第一个输出的字符数,初始值为1,表示是第一个
while(scanf("%c",&c)&&c!='.'){
if(c==' '){\\如果读取到的字符是空格,说明当前单词结束
if(count>0){\\如果count大于0,说明前面有单词字符被统计
if(!first)printf(" ");\\如果first为0(即不是第一个输出),则先输出一个空格作为分隔
printf("%d",count);
first=0;
count=0;\\重置count为0,准备统计下一个单词
}
}
else count++;\\如果读取到的字符不是空格,则count加1,继续统计当前单词的字符数
}
if(count>0){\\循环结束后,如果count大于0,说明最后一个单词的字符数还没有输出
if(!first)printf(" ");\\如果不是第一个输出,先输出一个空格
printf("%d",count);\\输出最后一个单词的字符数
}
}

统计3个字符串,并找出其中最大值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
#include<string.h>
int main(){
char str [3] [20];
char string[20];
for(int i=0;i<3;i++){
gets(str[i]);
}
for(int i=0;i<2;i++){
if(strcmp(str[i],str[i+1])>0)strcpy(string,str[i]);
if(strcmp(str[i],str[i+1])<=0)strcpy(string,str[i+1]);
}
printf("%s",string);
}

字符串的复制(指针)

1
2
3
4
5
6
void copy_string(char* from,char* to){
while((*to=*from)!='\0'){
to++;
from++;
}
}
1
2
3
void copy_string(char* from,char* to){
while((*to++=*from++)!='\0');
}
1
2
3
4
5
6
void copy_string(char* from,char* to){
while(*from!='\0'){
*to++=*from++;
*to='\0';
}
}

选择排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void selectSort(int arr[],int n){
int i,j,minIndex,temp;
for(i=0;i<n-1;i++){ //外层循环控制排序趟数
minIndex=i; //假设当前位置元素就是最小值
for (j=i+1;j<n;j++){ //内层循环在剩余元素中找最小值
if(arr[j]<arr[minIndex])minIndex=j; //更新最小值索引
}
if(minIndex!=i){ //如果找到更小的元素,进行交换
temp=arr[i];
arr[i]=arr[minIndex];
arr[minIndex]=temp;
}
}
}

人数过半

输入N个数,求出现次数超过总数一半的数。数据保证存在这个数。

输入格式:

第一行输入一个正整数N(N<1000);

第二行输入N个数,每个数的绝对值不超过1e9。

输出格式:

输出一个数,即出现次数超过总数一半的数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>
int main(){
int n;
scanf("%d",&n);
int a[2000];
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
int can=a[0];
int cnt=1;
for(int i=0;i<n;i++){
if(cnt==0){
can=a[i];
cnt=1;
}
else if(can==a[i])cnt++;
else cnt--;
}
printf("%d",can);
}

n个数字中选出数字和最大的连续一段

1
2
3
4
5
6
int sum1=0,sum=-1000;
for(int i=0;i<n;i++){
sum1=(sum1+a[i])>a[i]?(sum1+a[i]):a[i];
if(sum1>sum)sum=sum1;
}
printf("%d",sum);

结尾不输出空行

1
2
3
4
5
int flag=1;
···
if(!flag)printf("\n");
flag=0;
···\\输出···

数组元素右移

n个数,第i个数向右移m个整数变为a[(i+n-m)%n]

单词翻转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<stdio.h>
#include<string.h>
char* r(char* s){
int len=strlen(s);
for(int i=0;i<len/2;i++){
char t=s[i];
s[i]=s[len-1-i];
s[len-1-i]=t;
}
return s;
}
int main(){
char s[25];
while(scanf("%s",s)!=EOF){ \\去除多余空格
printf("%s",r(s));
}
}

10进制数n转化成m进制

1
2
3
4
5
6
7
8
int cnt=1,sum=0;
while(n>0){
t=n%m;
t*=cnt;
sum+=t;
n/=m;
cnt*=10;
}

求n的素数因子和

1
2
3
4
5
6
7
8
for(int i=1;i<sqrt(n);i++){
if(n%i==0){
sum+=i;
if(n/i!=i){
sum+=n/i;
}
}
}

求a,b的最大公约数

1
2
3
4
5
6
7
m=a;
n=b;
while(n!=0){
t=m%n;
m=n;
n=t;
}\\m即为最大公约数

将一个n位数反转

1
2
3
4
5
6
7
int number,res=0,rem;
scanf("%d",&number);
while(number!=0){
rem=num%10;
res=res*10+rem;
number/=10;
}

杨辉三角

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
int main(){
int rows,conf=1;
scanf("%d",&rows);
for(int i=0;i<rows;i++){
for(int space=1;space<=rows-i-1;space++){
printf(" ");
}
for(int j=0;j<=i;j++){
if(j==0||i==0)conf=1;
else conf=conf*(i-j+1)/j;
printf("%4d ",conf);
}
printf("\n");
}
}